# HG changeset patch # User Richard M. Stallman # Date 763319001 0 # Node ID 38ec8e76594f11809208c89d806eaf2cffd33f18 # Parent 6ecf8ea4bd8d7f43879ff85170d7c115fe2f66e4 (Fx_popup_menu): If POSITION is nil, don't require an open X connection. (single_keymap_panes, keymap_panes, menu_item_enabled_p): New arg NOTREAL. (Fx_popup_menu): Pass new arg (1 if POSITION is nil). (menu_item_enabled_p): If NOTREAL, always return t. (single_keymap_panes) [!USE_X_TOOLKIT]: Append > to item_string if submenu. [USE_X_TOOLKIT]: Display submenus in Xt style. (menu_items): Record where submenus start and end. (menu_items_submenu_depth): New variable. (init_menu_items): Init it. (push_submenu_start, push_submenu_end): New functions. (grow_menu_items): New function. (push_menu_pane, push_menu_item): Use it. (push_menu_pane): Increment menu_items_n_panes only if depth is 0. (single_keymap_panes) [USE_X_TOOLKIT]: Record submenus in menu_items. (xmenu_show) [USE_X_TOOLKIT]: Give submenus to toolkit. diff -r 6ecf8ea4bd8d -r 38ec8e76594f src/xmenu.c --- a/src/xmenu.c Thu Mar 10 15:59:22 1994 +0000 +++ b/src/xmenu.c Thu Mar 10 17:03:21 1994 +0000 @@ -113,6 +113,11 @@ the item string, the enable flag, the item's value, and the equivalent keyboard key's description string. + In some cases, multiple levels of menus may be described. + A single vector slot containing nil indicates the start of a submenu. + A single vector slot containing lambda indicates the end of a submenu. + The submenu follows a menu item which is the way to reach the submenu. + Using a Lisp vector to hold this information while we decode it takes care of protecting all the data from GC. */ @@ -134,9 +139,13 @@ /* This is the index in menu_items of the first empty slot. */ static int menu_items_used; -/* The number of panes currently recorded in menu_items. */ +/* The number of panes currently recorded in menu_items, + excluding those within submenus. */ static int menu_items_n_panes; +/* Current depth within submenus. */ +static int menu_items_submenu_depth; + /* Initialize the menu_items structure if we haven't already done so. Also mark it as currently empty. */ @@ -151,6 +160,7 @@ menu_items_used = 0; menu_items_n_panes = 0; + menu_items_submenu_depth = 0; } /* Call at the end of generating the data in menu_items. @@ -176,6 +186,45 @@ } } +/* Make the menu_items vector twice as large. */ + +static void +grow_menu_items () +{ + Lisp_Object old; + int old_size = menu_items_allocated; + old = menu_items; + + menu_items_allocated *= 2; + menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil); + bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents, + old_size * sizeof (Lisp_Object)); +} + +/* Begin a submenu. */ + +static void +push_submenu_start () +{ + if (menu_items_used + 1 > menu_items_allocated) + grow_menu_items (); + + XVECTOR (menu_items)->contents[menu_items_used++] = Qnil; + menu_items_submenu_depth++; +} + +/* End a submenu. */ + +static void +push_submenu_end () +{ + if (menu_items_used + 1 > menu_items_allocated) + grow_menu_items (); + + XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda; + menu_items_submenu_depth--; +} + /* Start a new menu pane in menu_items.. NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */ @@ -184,18 +233,10 @@ Lisp_Object name, prefix_vec; { if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated) - { - Lisp_Object old; - int old_size = menu_items_allocated; - old = menu_items; + grow_menu_items (); - menu_items_allocated *= 2; - menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil); - bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents, - old_size * sizeof (Lisp_Object)); - } - - menu_items_n_panes++; + if (menu_items_submenu_depth == 0) + menu_items_n_panes++; XVECTOR (menu_items)->contents[menu_items_used++] = Qt; XVECTOR (menu_items)->contents[menu_items_used++] = name; XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec; @@ -212,16 +253,7 @@ Lisp_Object name, enable, key, equiv; { if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated) - { - Lisp_Object old; - int old_size = menu_items_allocated; - old = menu_items; - - menu_items_allocated *= 2; - menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil); - bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents, - old_size * sizeof (Lisp_Object)); - } + grow_menu_items (); XVECTOR (menu_items)->contents[menu_items_used++] = name; XVECTOR (menu_items)->contents[menu_items_used++] = enable; @@ -319,15 +351,18 @@ } /* Return non-nil if the command DEF is enabled when used as a menu item. - This is based on looking for a menu-enable property. */ + This is based on looking for a menu-enable property. + If NOTREAL is set, don't bother really computing this. */ static Lisp_Object -menu_item_enabled_p (def) +menu_item_enabled_p (def, notreal) Lisp_Object def; { Lisp_Object enabled, tem; enabled = Qt; + if (notreal) + return enabled; if (XTYPE (def) == Lisp_Symbol) { /* No property, or nil, means enable. @@ -343,12 +378,15 @@ } /* Look through KEYMAPS, a vector of keymaps that is NMAPS long, - and generate menu panes for them in menu_items. */ + and generate menu panes for them in menu_items. + If NOTREAL is nonzero, + don't bother really computing whether an item is enabled. */ static void -keymap_panes (keymaps, nmaps) +keymap_panes (keymaps, nmaps, notreal) Lisp_Object *keymaps; int nmaps; + int notreal; { int mapno; @@ -358,7 +396,7 @@ But don't make a pane that is empty--ignore that map instead. P is the number of panes we have made so far. */ for (mapno = 0; mapno < nmaps; mapno++) - single_keymap_panes (keymaps[mapno], Qnil, Qnil); + single_keymap_panes (keymaps[mapno], Qnil, Qnil, notreal); finish_menu_items (); } @@ -366,13 +404,16 @@ /* This is a recursive subroutine of keymap_panes. It handles one keymap, KEYMAP. The other arguments are passed along - or point to local variables of the previous function. */ + or point to local variables of the previous function. + If NOTREAL is nonzero, + don't bother really computing whether an item is enabled. */ static void -single_keymap_panes (keymap, pane_name, prefix) +single_keymap_panes (keymap, pane_name, prefix, notreal) Lisp_Object keymap; Lisp_Object pane_name; Lisp_Object prefix; + int notreal; { Lisp_Object pending_maps; Lisp_Object tail, item, item1, item_string, table; @@ -410,7 +451,8 @@ a string. Since there's no GCPRO5, we refetch item_string instead of protecting it. */ GCPRO4 (keymap, pending_maps, def, descrip); - enabled = menu_item_enabled_p (def); + enabled = menu_item_enabled_p (def, notreal); + UNGCPRO; item_string = XCONS (item1)->car; @@ -420,8 +462,28 @@ pending_maps = Fcons (Fcons (def, Fcons (item_string, XCONS (item)->car)), pending_maps); else - push_menu_item (item_string, enabled, XCONS (item)->car, - descrip); + { + Lisp_Object submap; + submap = get_keymap_1 (def, 0, 1); +#ifndef USE_X_TOOLKIT + /* Indicate visually that this is a submenu. */ + if (!NILP (submap)) + item_string = concat2 (item_string, + build_string (" >")); +#endif + push_menu_item (item_string, enabled, XCONS (item)->car, + descrip); +#ifdef USE_X_TOOLKIT + /* Display a submenu using the toolkit. */ + if (! NILP (submap)) + { + push_submenu_start (); + single_keymap_panes (submap, Qnil, + XCONS (item)->car, notreal); + push_submenu_end (); + } +#endif + } } } } @@ -455,7 +517,7 @@ a string. Since there's no GCPRO5, we refetch item_string instead of protecting it. */ GCPRO4 (keymap, pending_maps, def, descrip); - enabled = menu_item_enabled_p (def); + enabled = menu_item_enabled_p (def, notreal); UNGCPRO; item_string = XCONS (item1)->car; @@ -465,8 +527,26 @@ pending_maps = Fcons (Fcons (def, Fcons (item_string, character)), pending_maps); else - push_menu_item (item_string, enabled, - character, descrip); + { + Lisp_Object submap; + submap = get_keymap_1 (def, 0, 1); +#ifndef USE_X_TOOLKIT + if (!NILP (submap)) + item_string = concat2 (item_string, + build_string (" >")); +#endif + push_menu_item (item_string, enabled, character, + descrip); +#ifdef USE_X_TOOLKIT + if (! NILP (submap)) + { + push_submenu_start (); + single_keymap_panes (submap, Qnil, + character, notreal); + push_submenu_end (); + } +#endif + } } } } @@ -476,12 +556,14 @@ /* Process now any submenus which want to be panes at this level. */ while (!NILP (pending_maps)) { - Lisp_Object elt, eltcdr; + Lisp_Object elt, eltcdr, string; elt = Fcar (pending_maps); eltcdr = XCONS (elt)->cdr; - single_keymap_panes (Fcar (elt), - /* Fails to discard the @. */ - XCONS (eltcdr)->car, XCONS (eltcdr)->cdr); + string = XCONS (eltcdr)->car; + /* We no longer discard the @ from the beginning of the string here. + Instead, we do this in xmenu_show. */ + single_keymap_panes (Fcar (elt), string, + XCONS (eltcdr)->cdr, notreal); pending_maps = Fcdr (pending_maps); } } @@ -581,10 +663,10 @@ int menubarp = 0; struct gcpro gcpro1; - check_x (); - if (! NILP (position)) { + check_x (); + /* Decode the first argument: find the window and the coordinates. */ if (EQ (position, Qt)) { @@ -668,7 +750,7 @@ keymap = get_keymap (menu); /* Extract the detailed info to make one pane. */ - keymap_panes (&menu, 1); + keymap_panes (&menu, 1, NILP (position)); /* Search for a string appearing directly as an element of the keymap. That string is the title of the menu. */ @@ -704,7 +786,7 @@ } /* Extract the detailed info to make one pane. */ - keymap_panes (maps, nmaps); + keymap_panes (maps, nmaps, NILP (position)); /* Make the title be the pane title of the first pane. */ if (!NILP (title) && menu_items_n_panes >= 0) @@ -1123,6 +1205,11 @@ widget_value *menubar_item = 0; widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0; + widget_value **submenu_stack + = (widget_value **) alloca (menu_items_used * sizeof (widget_value *)); + Lisp_Object *subprefix_stack + = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); + int submenu_depth = 0; /* Define a queue to save up for later unreading all X events that don't pertain to the menu. */ @@ -1188,7 +1275,23 @@ i = 0; while (i < menu_items_used) { - if (EQ (XVECTOR (menu_items)->contents[i], Qt)) + if (EQ (XVECTOR (menu_items)->contents[i], Qnil)) + { + submenu_stack[submenu_depth++] = save_wv; + save_wv = prev_wv; + prev_wv = 0; + i++; + } + else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda)) + { + prev_wv = save_wv; + save_wv = submenu_stack[--submenu_depth]; + i++; + } + else if (EQ (XVECTOR (menu_items)->contents[i], Qt) + && submenu_depth != 0) + i += MENU_ITEMS_PANE_LENGTH; + else if (EQ (XVECTOR (menu_items)->contents[i], Qt)) { /* Create a new pane. */ Lisp_Object pane_name, prefix; @@ -1197,7 +1300,7 @@ prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; pane_string = (NILP (pane_name) ? "" : (char *) XSTRING (pane_name)->data); - /* If there is just one pane, put all its items directly + /* If there is just one top-level pane, put all its items directly under the top-level menu. */ if (menu_items_n_panes == 1) pane_string = ""; @@ -1402,7 +1505,18 @@ { Lisp_Object entry; - if (EQ (XVECTOR (menu_items)->contents[i], Qt)) + if (EQ (XVECTOR (menu_items)->contents[i], Qnil)) + { + subprefix_stack[submenu_depth++] = prefix; + prefix = entry; + i++; + } + else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda)) + { + prefix = subprefix_stack[--submenu_depth]; + i++; + } + else if (EQ (XVECTOR (menu_items)->contents[i], Qt)) { prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX]; @@ -1416,9 +1530,13 @@ { if (keymaps != 0) { + int j; + entry = Fcons (entry, Qnil); if (!NILP (prefix)) entry = Fcons (prefix, entry); + for (j = submenu_depth - 1; j >= 0; j--) + entry = Fcons (subprefix_stack[j], entry); } return entry; }