]> code.delx.au - gnu-emacs/blobdiff - src/xmenu.c
(lgrep, rgrep): Use add-to-history.
[gnu-emacs] / src / xmenu.c
index 768bd62da527240d5b71734a663f192caea044e3..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.
 
@@ -16,8 +16,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 /* X pop-up deck-of-cards menu facility for GNU Emacs.
  *
@@ -116,7 +116,8 @@ extern Lisp_Object Qmenu_bar_update_hook;
 extern void set_frame_menubar P_ ((FRAME_PTR, int, int));
 extern XtAppContext Xt_app_con;
 
-static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, char **));
+static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, Lisp_Object,
+                                    char **));
 static void popup_get_selection P_ ((XEvent *, struct x_display_info *,
                                      LWLIB_ID, int));
 
@@ -129,7 +130,8 @@ static void popup_get_selection P_ ((XEvent *, struct x_display_info *,
 #include "gtkutil.h"
 #define HAVE_BOXES 1
 extern void set_frame_menubar P_ ((FRAME_PTR, int, int));
-static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, char **));
+static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, Lisp_Object,
+                                    char **));
 #endif
 
 /* This is how to deal with multibyte text if HAVE_MULTILINGUAL_MENU
@@ -264,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;
@@ -308,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
@@ -318,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));
@@ -897,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);
@@ -999,7 +1040,7 @@ no quit occurs and `x-popup-menu' returns nil.  */)
 
 #ifdef HAVE_MENUS
 
-DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0,
+DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
        doc: /* Pop up a dialog box and return user's selection.
 POSITION specifies which frame to use.
 This is normally a mouse button event or a window or frame.
@@ -1007,7 +1048,7 @@ If POSITION is t, it means to use the frame the mouse is on.
 The dialog box appears in the middle of the specified frame.
 
 CONTENTS specifies the alternatives to display in the dialog box.
-It is a list of the form (TITLE ITEM1 ITEM2...).
+It is a list of the form (DIALOG ITEM1 ITEM2...).
 Each ITEM is a cons cell (STRING . VALUE).
 The return value is VALUE from the chosen item.
 
@@ -1016,11 +1057,14 @@ An ITEM may also be nil--that means to put all preceding items
 on the left of the dialog box and all following items on the right.
 \(By default, approximately half appear on each side.)
 
+If HEADER is non-nil, the frame title for the box is "Information",
+otherwise it is "Question".
+
 If the user gets rid of the dialog box without making a valid choice,
 for instance using the window manager, then this produces a quit and
 `x-popup-dialog' does not return.  */)
-     (position, contents)
-     Lisp_Object position, contents;
+     (position, contents, header)
+     Lisp_Object position, contents, header;
 {
   FRAME_PTR f = NULL;
   Lisp_Object window;
@@ -1115,7 +1159,7 @@ for instance using the window manager, then this produces a quit and
 
     /* Display them in a dialog box.  */
     BLOCK_INPUT;
-    selection = xdialog_show (f, 0, title, &error_name);
+    selection = xdialog_show (f, 0, title, header, &error_name);
     UNBLOCK_INPUT;
 
     unbind_to (specpdl_count, Qnil);
@@ -1567,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
@@ -1711,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 *));
@@ -1757,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];
 
@@ -1790,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;
        }
@@ -1801,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);
@@ -1886,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.  */
@@ -1899,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);
@@ -2025,8 +2088,7 @@ set_frame_menubar (f, first_time, deep_p)
         because it is not reentrant.  */
       specbind (Qdebug_on_next_call, Qnil);
 
-      record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil));
-      record_unwind_protect (unuse_menu_items, Qnil);
+      record_unwind_save_match_data ();
       if (NILP (Voverriding_local_map_menu_flag))
        {
          specbind (Qoverriding_terminal_local_map, Qnil);
@@ -2054,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 *));
@@ -2113,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;
@@ -2144,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
     {
@@ -3020,11 +3091,11 @@ static char * button_names [] = {
   "button6", "button7", "button8", "button9", "button10" };
 
 static Lisp_Object
-xdialog_show (f, keymaps, title, error)
+xdialog_show (f, keymaps, title, header, error_name)
      FRAME_PTR f;
      int keymaps;
-     Lisp_Object title;
-     char **error;
+     Lisp_Object title, header;
+     char **error_name;
 {
   int i, nb_buttons=0;
   char dialog_name[6];
@@ -3036,11 +3107,11 @@ xdialog_show (f, keymaps, title, error)
   /* 1 means we've seen the boundary between left-hand elts and right-hand.  */
   int boundary_seen = 0;
 
-  *error = NULL;
+  *error_name = NULL;
 
   if (menu_items_n_panes > 1)
     {
-      *error = "Multiple panes in dialog box";
+      *error_name = "Multiple panes in dialog box";
       return Qnil;
     }
 
@@ -3077,7 +3148,7 @@ xdialog_show (f, keymaps, title, error)
        if (NILP (item_name))
          {
            free_menubar_widget_value_tree (first_wv);
-           *error = "Submenu in dialog items";
+           *error_name = "Submenu in dialog items";
            return Qnil;
          }
        if (EQ (item_name, Qquote))
@@ -3091,7 +3162,7 @@ xdialog_show (f, keymaps, title, error)
        if (nb_buttons >= 9)
          {
            free_menubar_widget_value_tree (first_wv);
-           *error = "Too many dialog items";
+           *error_name = "Too many dialog items";
            return Qnil;
          }
 
@@ -3121,11 +3192,18 @@ xdialog_show (f, keymaps, title, error)
     wv = xmalloc_widget_value ();
     wv->name = dialog_name;
     wv->help = Qnil;
+
+    /*  Frame title: 'Q' = Question, 'I' = Information.
+        Can also have 'E' = Error if, one day, we want
+        a popup for errors. */
+    if (NILP(header))
+      dialog_name[0] = 'Q';
+    else
+      dialog_name[0] = 'I';
+
     /* Dialog boxes use a really stupid name encoding
        which specifies how many buttons to use
-       and how many buttons are on the right.
-       The Q means something also.  */
-    dialog_name[0] = 'Q';
+       and how many buttons are on the right. */
     dialog_name[1] = '0' + nb_buttons;
     dialog_name[2] = 'B';
     dialog_name[3] = 'R';
@@ -3319,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);