-/* Initialize the menu_items structure if we haven't already done so.
- Also mark it as currently empty. */
-
-static void
-init_menu_items ()
-{
- if (!NILP (menu_items_inuse))
- error ("Trying to use a menu from within a menu-entry");
-
- if (NILP (menu_items))
- {
- menu_items_allocated = 60;
- menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
- }
-
- menu_items_inuse = Qt;
- 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. */
-
-static void
-finish_menu_items ()
-{
-}
-
-static Lisp_Object
-unuse_menu_items (dummy)
- Lisp_Object dummy;
-{
- return menu_items_inuse = Qnil;
-}
-
-/* Call when finished using the data for the current menu
- in menu_items. */
-
-static void
-discard_menu_items ()
-{
- /* Free the structure if it is especially large.
- Otherwise, hold on to it, to save time. */
- if (menu_items_allocated > 200)
- {
- menu_items = Qnil;
- menu_items_allocated = 0;
- }
- xassert (NILP (menu_items_inuse));
-}
-
-/* This undoes save_menu_items, and it is called by the specpdl unwind
- mechanism. */
-
-static Lisp_Object
-restore_menu_items (saved)
- Lisp_Object saved;
-{
- menu_items = XCAR (saved);
- menu_items_inuse = (! NILP (menu_items) ? Qt : Qnil);
- menu_items_allocated = (VECTORP (menu_items) ? ASIZE (menu_items) : 0);
- saved = XCDR (saved);
- menu_items_used = XINT (XCAR (saved));
- saved = XCDR (saved);
- menu_items_n_panes = XINT (XCAR (saved));
- saved = XCDR (saved);
- menu_items_submenu_depth = XINT (XCAR (saved));
- return Qnil;
-}
-
-/* Push the whole state of menu_items processing onto the specpdl.
- It will be restored when the specpdl is unwound. */
-
-static void
-save_menu_items ()
-{
- Lisp_Object saved = list4 (!NILP (menu_items_inuse) ? menu_items : Qnil,
- make_number (menu_items_used),
- make_number (menu_items_n_panes),
- make_number (menu_items_submenu_depth));
- record_unwind_protect (restore_menu_items, saved);
- menu_items_inuse = Qnil;
- menu_items = Qnil;
-}
-\f
-/* Make the menu_items vector twice as large. */
-
-static void
-grow_menu_items ()
-{
- menu_items_allocated *= 2;
- menu_items = larger_vector (menu_items, menu_items_allocated, Qnil);
-}
-
-/* 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--;
-}
-
-/* Indicate boundary between left and right. */
-
-static void
-push_left_right_boundary ()
-{
- if (menu_items_used + 1 > menu_items_allocated)
- grow_menu_items ();
-
- XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
-}
-
-/* Start a new menu pane in menu_items.
- NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
-
-static void
-push_menu_pane (name, prefix_vec)
- Lisp_Object name, prefix_vec;
-{
- if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
- grow_menu_items ();
-
- 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;
-}
-
-/* Push one menu item into the current pane. NAME is the string to
- display. ENABLE if non-nil means this item can be selected. KEY
- is the key generated by choosing this item, or nil if this item
- doesn't really have a definition. DEF is the definition of this
- item. EQUIV is the textual description of the keyboard equivalent
- for this item (or nil if none). TYPE is the type of this menu
- item, one of nil, `toggle' or `radio'. */
-
-static void
-push_menu_item (name, enable, key, def, equiv, type, selected, help)
- Lisp_Object name, enable, key, def, equiv, type, selected, help;
-{
- if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
- grow_menu_items ();
-
- XVECTOR (menu_items)->contents[menu_items_used++] = name;
- XVECTOR (menu_items)->contents[menu_items_used++] = enable;
- XVECTOR (menu_items)->contents[menu_items_used++] = key;
- XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
- XVECTOR (menu_items)->contents[menu_items_used++] = def;
- XVECTOR (menu_items)->contents[menu_items_used++] = type;
- XVECTOR (menu_items)->contents[menu_items_used++] = selected;
- XVECTOR (menu_items)->contents[menu_items_used++] = help;
-}
-\f
-/* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
- 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, notreal)
- Lisp_Object *keymaps;
- int nmaps;
- int notreal;
-{
- int mapno;
-
- init_menu_items ();
-
- /* Loop over the given keymaps, making a pane for each map.
- 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],
- Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10);
-
- finish_menu_items ();
-}
-
-/* Args passed between single_keymap_panes and single_menu_item. */
-struct skp
- {
- Lisp_Object pending_maps;
- int maxdepth, notreal;
- int notbuttons;
- };
-
-static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
- void *));
-
-/* 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.
- If NOTREAL is nonzero, only check for equivalent key bindings, don't
- evaluate expressions in menu items and don't make any menu.
-
- If we encounter submenus deeper than MAXDEPTH levels, ignore them. */
-
-static void
-single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
- Lisp_Object keymap;
- Lisp_Object pane_name;
- Lisp_Object prefix;
- int notreal;
- int maxdepth;
-{
- struct skp skp;
- struct gcpro gcpro1;
-
- skp.pending_maps = Qnil;
- skp.maxdepth = maxdepth;
- skp.notreal = notreal;
- skp.notbuttons = 0;
-
- if (maxdepth <= 0)
- return;
-
- push_menu_pane (pane_name, prefix);
-
-#ifndef HAVE_BOXES
- /* Remember index for first item in this pane so we can go back and
- add a prefix when (if) we see the first button. After that, notbuttons
- is set to 0, to mark that we have seen a button and all non button
- items need a prefix. */
- skp.notbuttons = menu_items_used;
-#endif
-
- GCPRO1 (skp.pending_maps);
- map_keymap_canonical (keymap, single_menu_item, Qnil, &skp);
- UNGCPRO;
-
- /* Process now any submenus which want to be panes at this level. */
- while (CONSP (skp.pending_maps))
- {
- Lisp_Object elt, eltcdr, string;
- elt = XCAR (skp.pending_maps);
- eltcdr = XCDR (elt);
- string = XCAR (eltcdr);
- /* 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,
- XCDR (eltcdr), notreal, maxdepth - 1);
- skp.pending_maps = XCDR (skp.pending_maps);
- }
-}
-\f
-/* This is a subroutine of single_keymap_panes that handles one
- keymap entry.
- KEY is a key in a keymap and ITEM is its binding.
- SKP->PENDING_MAPS_PTR is a list of keymaps waiting to be made into
- separate panes.
- If SKP->NOTREAL is nonzero, only check for equivalent key bindings, don't
- evaluate expressions in menu items and don't make any menu.
- If we encounter submenus deeper than SKP->MAXDEPTH levels, ignore them.
- SKP->NOTBUTTONS is only used when simulating toggle boxes and radio
- buttons. It keeps track of if we have seen a button in this menu or
- not. */
-
-static void
-single_menu_item (key, item, dummy, skp_v)
- Lisp_Object key, item, dummy;
- void *skp_v;
-{
- Lisp_Object map, item_string, enabled;
- struct gcpro gcpro1, gcpro2;
- int res;
- struct skp *skp = skp_v;
-
- /* Parse the menu item and leave the result in item_properties. */
- GCPRO2 (key, item);
- res = parse_menu_item (item, skp->notreal, 0);
- UNGCPRO;
- if (!res)
- return; /* Not a menu item. */
-
- map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
-
- if (skp->notreal)
- {
- /* We don't want to make a menu, just traverse the keymaps to
- precompute equivalent key bindings. */
- if (!NILP (map))
- single_keymap_panes (map, Qnil, key, 1, skp->maxdepth - 1);
- return;
- }
-
- enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE];
- item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME];
-
- if (!NILP (map) && SREF (item_string, 0) == '@')
- {
- if (!NILP (enabled))
- /* An enabled separate pane. Remember this to handle it later. */
- skp->pending_maps = Fcons (Fcons (map, Fcons (item_string, key)),
- skp->pending_maps);
- return;
- }
-
-#ifndef HAVE_BOXES
- /* Simulate radio buttons and toggle boxes by putting a prefix in
- front of them. */
- {
- Lisp_Object prefix = Qnil;
- Lisp_Object type = XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE];
- if (!NILP (type))
- {
- Lisp_Object selected
- = XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED];
-
- if (skp->notbuttons)
- /* The first button. Line up previous items in this menu. */
- {
- int index = skp->notbuttons; /* Index for first item this menu. */
- int submenu = 0;
- Lisp_Object tem;
- while (index < menu_items_used)
- {
- tem
- = XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME];
- if (NILP (tem))
- {
- index++;
- submenu++; /* Skip sub menu. */
- }
- else if (EQ (tem, Qlambda))
- {
- index++;
- submenu--; /* End sub menu. */
- }
- else if (EQ (tem, Qt))
- index += 3; /* Skip new pane marker. */
- else if (EQ (tem, Qquote))
- index++; /* Skip a left, right divider. */
- else
- {
- if (!submenu && SREF (tem, 0) != '\0'
- && SREF (tem, 0) != '-')
- XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME]
- = concat2 (build_string (" "), tem);
- index += MENU_ITEMS_ITEM_LENGTH;
- }
- }
- skp->notbuttons = 0;
- }
-
- /* Calculate prefix, if any, for this item. */
- if (EQ (type, QCtoggle))
- prefix = build_string (NILP (selected) ? "[ ] " : "[X] ");
- else if (EQ (type, QCradio))
- prefix = build_string (NILP (selected) ? "( ) " : "(*) ");
- }
- /* Not a button. If we have earlier buttons, then we need a prefix. */
- else if (!skp->notbuttons && SREF (item_string, 0) != '\0'
- && SREF (item_string, 0) != '-')
- prefix = build_string (" ");
-
- if (!NILP (prefix))
- item_string = concat2 (prefix, item_string);
- }
-#endif /* not HAVE_BOXES */
-
-#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
- if (!NILP (map))
- /* Indicate visually that this is a submenu. */
- item_string = concat2 (item_string, build_string (" >"));
-#endif
-
- push_menu_item (item_string, enabled, key,
- XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
- XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
- XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
- XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
- XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
-
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
- /* Display a submenu using the toolkit. */
- if (! (NILP (map) || NILP (enabled)))
- {
- push_submenu_start ();
- single_keymap_panes (map, Qnil, key, 0, skp->maxdepth - 1);
- push_submenu_end ();
- }
-#endif
-}
-\f
-/* Push all the panes and items of a menu described by the
- alist-of-alists MENU.
- This handles old-fashioned calls to x-popup-menu. */
-
-static void
-list_of_panes (menu)
- Lisp_Object menu;
-{
- Lisp_Object tail;
-
- init_menu_items ();
-
- for (tail = menu; CONSP (tail); tail = XCDR (tail))
- {
- Lisp_Object elt, pane_name, pane_data;
- elt = XCAR (tail);
- pane_name = Fcar (elt);
- CHECK_STRING (pane_name);
- push_menu_pane (ENCODE_MENU_STRING (pane_name), Qnil);
- pane_data = Fcdr (elt);
- CHECK_CONS (pane_data);
- list_of_items (pane_data);
- }
-
- finish_menu_items ();
-}
-
-/* Push the items in a single pane defined by the alist PANE. */
-
-static void
-list_of_items (pane)
- Lisp_Object pane;
-{
- Lisp_Object tail, item, item1;
-
- for (tail = pane; CONSP (tail); tail = XCDR (tail))
- {
- item = XCAR (tail);
- if (STRINGP (item))
- push_menu_item (ENCODE_MENU_STRING (item), Qnil, Qnil, Qt,
- Qnil, Qnil, Qnil, Qnil);
- else if (CONSP (item))
- {
- item1 = XCAR (item);
- CHECK_STRING (item1);
- push_menu_item (ENCODE_MENU_STRING (item1), Qt, XCDR (item),
- Qt, Qnil, Qnil, Qnil, Qnil);
- }
- else
- push_left_right_boundary ();
-
- }
-}
-\f