]> code.delx.au - gnu-emacs/blobdiff - src/xmenu.c
(restore_menu_items): Return a value.
[gnu-emacs] / src / xmenu.c
index bcc0e57bcdbcfee69d245c7f618c8f2f626e74ff..c740062a82fc77c4e1c11c4fe20b725a683b8815 100644 (file)
@@ -1,6 +1,6 @@
 /* X Communication module for terminals which understand the X protocol.
-   Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2003, 2004,
-   2005  Free Software Foundation, Inc.
+   Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2002, 2003,
+                 2004, 2005, 2006 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -266,14 +266,15 @@ menubar_id_to_frame (id)
 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);
     }
 
-  if (!NILP (menu_items_inuse))
-    error ("Trying to use a menu from within a menu-entry");
   menu_items_inuse = Qt;
   menu_items_used = 0;
   menu_items_n_panes = 0;
@@ -310,6 +311,40 @@ discard_menu_items ()
   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
@@ -320,6 +355,7 @@ grow_menu_items ()
   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));
@@ -899,8 +935,11 @@ no quit occurs and `x-popup-menu' returns nil.  */)
 
       xpos += XINT (x);
       ypos += XINT (y);
+
+      XSETFRAME (Vmenu_updating_frame, f);
     }
-  Vmenu_updating_frame = Qnil;
+  else
+    Vmenu_updating_frame = Qnil;
 #endif /* HAVE_MENUS */
 
   record_unwind_protect (unuse_menu_items, Qnil);
@@ -1572,6 +1611,15 @@ menubar_selection_callback (widget, client_data)
   if (! cb_data || ! cb_data->cl_data || ! cb_data->cl_data->f)
     return;
 
+  /* For a group of radio buttons, GTK calls the selection callback first
+     for the item that was active before the selection and then for the one that
+     is active after the selection.  For C-h k this means we get the help on
+     the deselected item and then the selected item is executed.  Prevent that
+     by ignoring the non-active item.  */
+  if (GTK_IS_RADIO_MENU_ITEM (widget)
+      && ! gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)))
+    return;
+
   /* When a menu is popped down, X generates a focus event (i.e. focus
      goes back to the frame below the menu).  Since GTK buffers events,
      we force it out here before the menu selection event.  Otherwise
@@ -1716,6 +1764,7 @@ digest_single_submenu (start, end, top_level_items)
   int i;
   int submenu_depth = 0;
   widget_value **submenu_stack;
+  int panes_seen = 0;
 
   submenu_stack
     = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
@@ -1762,6 +1811,8 @@ digest_single_submenu (start, end, top_level_items)
          Lisp_Object pane_name, prefix;
          char *pane_string;
 
+         panes_seen++;
+
          pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
          prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
 
@@ -1795,8 +1846,11 @@ digest_single_submenu (start, end, top_level_items)
              wv->enabled = 1;
              wv->button_type = BUTTON_TYPE_NONE;
              wv->help = Qnil;
+             save_wv = wv;
            }
-         save_wv = wv;
+         else
+           save_wv = first_wv;
+
          prev_wv = 0;
          i += MENU_ITEMS_PANE_LENGTH;
        }
@@ -1806,6 +1860,10 @@ digest_single_submenu (start, end, top_level_items)
          Lisp_Object item_name, enable, descrip, def, type, selected;
          Lisp_Object help;
 
+         /* All items should be contained in panes.  */
+         if (panes_seen == 0)
+           abort ();
+
          item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
          enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
          descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
@@ -1891,7 +1949,7 @@ update_submenu_strings (first_wv)
     {
       if (STRINGP (wv->lname))
         {
-          wv->name = SDATA (wv->lname);
+          wv->name = (char *) SDATA (wv->lname);
 
           /* Ignore the @ that means "separate pane".
              This is a kludge, but this isn't worth more time.  */
@@ -1904,7 +1962,7 @@ update_submenu_strings (first_wv)
         }
 
       if (STRINGP (wv->lkey))
-        wv->key = SDATA (wv->lkey);
+        wv->key = (char *) SDATA (wv->lkey);
 
       if (wv->contents)
         update_submenu_strings (wv->contents);
@@ -2031,7 +2089,6 @@ set_frame_menubar (f, first_time, deep_p)
       specbind (Qdebug_on_next_call, Qnil);
 
       record_unwind_save_match_data ();
-      record_unwind_protect (unuse_menu_items, Qnil);
       if (NILP (Voverriding_local_map_menu_flag))
        {
          specbind (Qoverriding_terminal_local_map, Qnil);
@@ -2059,6 +2116,8 @@ set_frame_menubar (f, first_time, deep_p)
 
       /* Fill in menu_items with the current menu bar contents.
         This can evaluate Lisp code.  */
+      save_menu_items ();
+
       menu_items = f->menu_bar_vector;
       menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
       submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
@@ -2118,23 +2177,33 @@ set_frame_menubar (f, first_time, deep_p)
        }
 
       set_buffer_internal_1 (prev);
-      unbind_to (specpdl_count, Qnil);
 
       /* If there has been no change in the Lisp-level contents
         of the menu bar, skip redisplaying it.  Just exit.  */
 
+      /* Compare the new menu items with the ones computed last time.  */
       for (i = 0; i < previous_menu_items_used; i++)
        if (menu_items_used == i
            || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
          break;
       if (i == menu_items_used && i == previous_menu_items_used && i != 0)
        {
+         /* The menu items have not changed.  Don't bother updating
+            the menus in any form, since it would be a no-op.  */
          free_menubar_widget_value_tree (first_wv);
          discard_menu_items ();
-
+         unbind_to (specpdl_count, Qnil);
          return;
        }
 
+      /* The menu items are different, so store them in the frame.  */
+      f->menu_bar_vector = menu_items;
+      f->menu_bar_items_used = menu_items_used;
+
+      /* This calls restore_menu_items to restore menu_items, etc.,
+        as they were outside.  */
+      unbind_to (specpdl_count, Qnil);
+
       /* Now GC cannot happen during the lifetime of the widget_value,
         so it's safe to store data from a Lisp_String.  */
       wv = first_wv->contents;
@@ -2149,9 +2218,6 @@ set_frame_menubar (f, first_time, deep_p)
           wv = wv->next;
        }
 
-      f->menu_bar_vector = menu_items;
-      f->menu_bar_items_used = menu_items_used;
-      discard_menu_items ();
     }
   else
     {
@@ -3331,6 +3397,11 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
       return Qnil;
     }
 
+  /* Don't GC while we prepare and show the menu,
+     because we give the oldxmenu library pointers to the
+     contents of strings.  */
+  inhibit_garbage_collection ();
+
 #ifdef HAVE_X_WINDOWS
   /* Adjust coordinates to relative to the outer (window manager) window.  */
   x += FRAME_OUTER_TO_INNER_DIFF_X (f);