]> code.delx.au - gnu-emacs/blobdiff - src/xmenu.c
(set_case_table): If standard is nonzero, setup
[gnu-emacs] / src / xmenu.c
index c8c40a472562ef2c67128d2bb5249c67dd934d0a..c17e1e801b7ff8fc1a98b2137de8c75ad3422beb 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
-   Free Software Foundation, Inc.
+   Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2003, 2004,
+   2005  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -116,7 +116,7 @@ extern XtAppContext Xt_app_con;
 
 static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, char **));
 static void popup_get_selection P_ ((XEvent *, struct x_display_info *,
-                                     LWLIB_ID, int, int));
+                                     LWLIB_ID, int));
 
 /* Define HAVE_BOXES if menus can handle radio and toggle buttons.  */
 
@@ -137,13 +137,10 @@ static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, char **));
 #ifdef USE_GTK
 /* gtk just uses utf-8.  */
 # define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
-#else
-/* I'm not convinced ENCODE_SYSTEM is defined correctly, or maybe
-   something else should be used here.  Except under MS-Windows it
-   just converts to unibyte, but encoding with `locale-coding-system'
-   seems better -- X may actually display the result correctly, and
-   it's not necessarily equivalent to the unibyte text.  -- fx  */
+#elif defined HAVE_X_I18N
 # define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str)
+#else
+# define ENCODE_MENU_STRING(str) string_make_unibyte (str)
 #endif
 
 static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
@@ -645,10 +642,10 @@ list_of_panes (menu)
 
   init_menu_items ();
 
-  for (tail = menu; !NILP (tail); tail = Fcdr (tail))
+  for (tail = menu; CONSP (tail); tail = XCDR (tail))
     {
       Lisp_Object elt, pane_name, pane_data;
-      elt = Fcar (tail);
+      elt = XCAR (tail);
       pane_name = Fcar (elt);
       CHECK_STRING (pane_name);
       push_menu_pane (ENCODE_MENU_STRING (pane_name), Qnil);
@@ -668,22 +665,22 @@ list_of_items (pane)
 {
   Lisp_Object tail, item, item1;
 
-  for (tail = pane; !NILP (tail); tail = Fcdr (tail))
+  for (tail = pane; CONSP (tail); tail = XCDR (tail))
     {
-      item = Fcar (tail);
+      item = XCAR (tail);
       if (STRINGP (item))
        push_menu_item (ENCODE_MENU_STRING (item), Qnil, Qnil, Qt,
                        Qnil, Qnil, Qnil, Qnil);
-      else if (NILP (item))
-       push_left_right_boundary ();
-      else
+      else if (CONSP (item))
        {
-         CHECK_CONS (item);
-         item1 = Fcar (item);
+         item1 = XCAR (item);
          CHECK_STRING (item1);
-         push_menu_item (ENCODE_MENU_STRING (item1), Qt, Fcdr (item),
+         push_menu_item (ENCODE_MENU_STRING (item1), Qt, XCDR (item),
                          Qt, Qnil, Qnil, Qnil, Qnil);
        }
+      else
+       push_left_right_boundary ();
+
     }
 }
 \f
@@ -741,8 +738,7 @@ POSITION is a position specification.  This is either a mouse button event
 or a list ((XOFFSET YOFFSET) WINDOW)
 where XOFFSET and YOFFSET are positions in pixels from the top left
 corner of WINDOW's frame.  (WINDOW may be a frame object instead of a window.)
-This controls the position of the center of the first line
-in the first pane of the menu, not the top left of the menu as a whole.
+This controls the position of the top left of the menu as a whole.
 If POSITION is t, it means to use the current mouse position.
 
 MENU is a specifier for a menu.  For the simplest case, MENU is a keymap.
@@ -803,8 +799,8 @@ cached information about equivalent key sequences.  */)
          if (CONSP (tem))
            {
              window = Fcar (Fcdr (position));
-             x = Fcar (tem);
-             y = Fcar (Fcdr (tem));
+             x = XCAR (tem);
+             y = Fcar (XCDR (tem));
            }
          else
            {
@@ -932,11 +928,11 @@ cached information about equivalent key sequences.  */)
 
       /* The first keymap that has a prompt string
         supplies the menu title.  */
-      for (tem = menu, i = 0; CONSP (tem); tem = Fcdr (tem))
+      for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
        {
          Lisp_Object prompt;
 
-         maps[i++] = keymap = get_keymap (Fcar (tem), 1, 0);
+         maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
 
          prompt = Fkeymap_prompt (keymap);
          if (NILP (title) && !NILP (prompt))
@@ -1118,9 +1114,19 @@ on the left of the dialog box and all following items on the right.
 
 #ifndef MSDOS
 
+/* Set menu_items_inuse so no other popup menu or dialog is created.  */
+
+void
+x_menu_set_in_use (in_use)
+     int in_use;
+{
+  menu_items_inuse = in_use ? Qt : Qnil;
+  popup_activated_flag = in_use;
+}
+
 /* Wait for an X event to arrive or for a timer to expire.  */
 
-static void
+void
 x_menu_wait_for_event (void *data)
 {
   extern EMACS_TIME timer_check P_ ((int));
@@ -1177,24 +1183,21 @@ x_menu_wait_for_event (void *data)
    popped down (deactivated).  This is used for x-popup-menu
    and x-popup-dialog; it is not used for the menu bar.
 
-   If DOWN_ON_KEYPRESS is nonzero, pop down if a key is pressed.
-
    NOTE: All calls to popup_get_selection should be protected
    with BLOCK_INPUT, UNBLOCK_INPUT wrappers.  */
 
 static void
-popup_get_selection (initial_event, dpyinfo, id, do_timers, down_on_keypress)
+popup_get_selection (initial_event, dpyinfo, id, do_timers)
      XEvent *initial_event;
      struct x_display_info *dpyinfo;
      LWLIB_ID id;
      int do_timers;
-     int down_on_keypress;
 {
   XEvent event;
 
   while (popup_activated_flag)
     {
-       if (initial_event)
+      if (initial_event)
         {
           event = *initial_event;
           initial_event = 0;
@@ -1223,20 +1226,15 @@ popup_get_selection (initial_event, dpyinfo, id, do_timers, down_on_keypress)
           event.xbutton.state = 0;
 #endif
         }
-      /* If the user presses a key that doesn't go to the menu,
-         deactivate the menu.
-         The user is likely to do that if we get wedged.
-         All toolkits now pop down menus on ESC.
-         For dialogs however, the focus may not be on the dialog, so
-         in that case, we pop down. */
+      /* Pop down on C-g and Escape.  */
       else if (event.type == KeyPress
-               && down_on_keypress
                && dpyinfo->display == event.xbutton.display)
         {
           KeySym keysym = XLookupKeysym (&event.xkey, 0);
-          if (!IsModifierKey (keysym)
-              && x_any_window_to_frame (dpyinfo, event.xany.window) != NULL)
-           popup_activated_flag = 0;
+
+          if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
+              || keysym == XK_Escape) /* Any escape, ignore modifiers.  */
+            popup_activated_flag = 0;
         }
 
       x_dispatch_event (&event, event.xany.display);
@@ -1444,9 +1442,9 @@ menu_highlight_callback (widget, id, call_data)
 /* Find the menu selection and store it in the keyboard buffer.
    F is the frame the menu is on.
    MENU_BAR_ITEMS_USED is the length of VECTOR.
-   VECTOR is an array of menu events for the whole menu.
- */
-void
+   VECTOR is an array of menu events for the whole menu.  */
+
+static void
 find_and_call_menu_selection (f, menu_bar_items_used, vector, client_data)
      FRAME_PTR f;
      int menu_bar_items_used;
@@ -1553,6 +1551,17 @@ menubar_selection_callback (widget, client_data)
   if (! cb_data || ! cb_data->cl_data || ! cb_data->cl_data->f)
     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
+     sit-for will exit at once if the focus event follows the menu selection
+     event.  */
+
+  BLOCK_INPUT;
+  while (gtk_events_pending ())
+    gtk_main_iteration ();
+  UNBLOCK_INPUT;
+
   find_and_call_menu_selection (cb_data->cl_data->f,
                                 cb_data->cl_data->menu_bar_items_used,
                                 cb_data->cl_data->menu_bar_vector,
@@ -1738,7 +1747,7 @@ digest_single_submenu (start, end, top_level_items)
 #ifndef HAVE_MULTILINGUAL_MENU
          if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
            {
-             pane_name = ENCODE_SYSTEM (pane_name);
+             pane_name = ENCODE_MENU_STRING (pane_name);
              AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
            }
 #endif
@@ -2217,6 +2226,9 @@ set_frame_menubar (f, first_time, deep_p)
     }
   else
     {
+      char menuOverride[] = "Ctrl<KeyPress>g: MenuGadgetEscape()";
+      XtTranslations  override = XtParseTranslationTable (menuOverride);
+
       menubar_widget = lw_create_widget ("menubar", "menubar", id, first_wv,
                                         f->output_data.x->column_widget,
                                         0,
@@ -2225,6 +2237,9 @@ set_frame_menubar (f, first_time, deep_p)
                                         popup_deactivate_callback,
                                         menu_highlight_callback);
       f->output_data.x->menubar_widget = menubar_widget;
+
+      /* Make menu pop down on C-g.  */
+      XtOverrideTranslations (menubar_widget, override);
     }
 
   {
@@ -2410,20 +2425,16 @@ popup_selection_callback (widget, client_data)
   if (cb_data) menu_item_selection = (Lisp_Object *) cb_data->call_data;
 }
 
-static GtkWidget *current_menu;
-
 static Lisp_Object
-pop_down_menu (dummy)
-     Lisp_Object dummy;
+pop_down_menu (arg)
+     Lisp_Object arg;
 {
+  struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
+
   popup_activated_flag = 0;
-  if (current_menu)
-    {
-      BLOCK_INPUT;
-      gtk_widget_destroy (current_menu);
-      UNBLOCK_INPUT;
-      current_menu = 0;
-    }
+  BLOCK_INPUT;
+  gtk_widget_destroy (GTK_WIDGET (p->pointer));
+  UNBLOCK_INPUT;
   return Qnil;
 }
 
@@ -2451,10 +2462,6 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
                            G_CALLBACK (menu_highlight_callback));
   xg_crazy_callback_abort = 0;
 
-  for (i = 0; i < 5; i++)
-    if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
-      break;
-
   if (! for_click)
     {
       /* Not invoked by a click.  pop up at x/y.  */
@@ -2467,20 +2474,30 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
       popup_x_y.x = x;
       popup_x_y.y = y;
       popup_x_y.f = f;
-    }
 
+      i = 0;  /* gtk_menu_popup needs this to be 0 for a non-button popup.  */
+    }
+  else
+    {
+      for (i = 0; i < 5; i++)
+        if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
+          break;
+    }
+  
   /* Display the menu.  */
   gtk_widget_show_all (menu);
   gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i, 0);
 
-  current_menu = menu;
-  record_unwind_protect (pop_down_menu, Qnil);
+  record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
 
-  /* Set this to one.  popup_widget_loop increases it by one, so it becomes
-     two.  show_help_echo uses this to detect popup menus.  */
-  popup_activated_flag = 1;
-  /* Process events that apply to the menu.  */
-  popup_widget_loop (1, menu);
+  if (GTK_WIDGET_MAPPED (menu))
+    {
+      /* Set this to one.  popup_widget_loop increases it by one, so it becomes
+         two.  show_help_echo uses this to detect popup menus.  */
+      popup_activated_flag = 1;
+      /* Process events that apply to the menu.  */
+      popup_widget_loop (1, menu);
+    }
 
   unbind_to (specpdl_count, Qnil);
 
@@ -2593,7 +2610,7 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
                                   make_number (menu_id & ~(-1 << (fact)))));
 
     /* Process events that apply to the menu.  */
-    popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 1, 0);
+    popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 1);
 
     unbind_to (specpdl_count, Qnil);
   }
@@ -2678,7 +2695,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 #ifndef HAVE_MULTILINGUAL_MENU
          if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
            {
-             pane_name = ENCODE_SYSTEM (pane_name);
+             pane_name = ENCODE_MENU_STRING (pane_name);
              AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
            }
 #endif
@@ -2908,8 +2925,7 @@ create_and_show_dialog (f, first_wv)
   if (menu)
     {
       int specpdl_count = SPECPDL_INDEX ();
-      current_menu = menu;
-      record_unwind_protect (pop_down_menu, Qnil);
+      record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
 
       /* Display the menu.  */
       gtk_widget_show_all (menu);
@@ -2972,7 +2988,7 @@ create_and_show_dialog (f, first_wv)
                                   make_number (dialog_id & ~(-1 << (fact)))));
 
     popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f),
-                         dialog_id, 1, 1);
+                         dialog_id, 1);
 
     unbind_to (count, Qnil);
   }
@@ -3152,6 +3168,9 @@ xdialog_show (f, keymaps, title, error)
            }
        }
     }
+  else
+    /* Make "Cancel" equivalent to C-g.  */
+    Fsignal (Qquit, Qnil);
 
   return Qnil;
 }
@@ -3200,24 +3219,22 @@ menu_help_callback (help_string, pane, item)
                  Qnil, menu_object, make_number (item), 1);
 }
 
-static XMenu *current_menu;
-
 static Lisp_Object
-pop_down_menu (frame)
-     Lisp_Object frame;
+pop_down_menu (arg)
+     Lisp_Object arg;
 {
-  struct frame *f = XFRAME (frame);
+  struct Lisp_Save_Value *p1 = XSAVE_VALUE (Fcar (arg));
+  struct Lisp_Save_Value *p2 = XSAVE_VALUE (Fcdr (arg));
+  
+  FRAME_PTR f = p1->pointer;
+  XMenu *menu = p2->pointer;
 
   BLOCK_INPUT;
-  if (current_menu)
-    {
 #ifndef MSDOS
-      XUngrabPointer (FRAME_X_DISPLAY (f), CurrentTime);
-      XUngrabKeyboard (FRAME_X_DISPLAY (f), CurrentTime);
+  XUngrabPointer (FRAME_X_DISPLAY (f), CurrentTime);
+  XUngrabKeyboard (FRAME_X_DISPLAY (f), CurrentTime);
 #endif
-      XMenuDestroy (FRAME_X_DISPLAY (f), current_menu);
-      current_menu = 0;
-    }
+  XMenuDestroy (FRAME_X_DISPLAY (f), menu);
 
 #ifdef HAVE_X_WINDOWS
   /* Assume the mouse has moved out of the X window.
@@ -3250,11 +3267,11 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   Window root;
   XMenu *menu;
   int pane, selidx, lpane, status;
-  Lisp_Object entry, pane_prefix, frame;
+  Lisp_Object entry, pane_prefix;
   char *datap;
   int ulx, uly, width, height;
   int dispwidth, dispheight;
-  int i, j;
+  int i, j, lines, maxlines;
   int maxwidth;
   int dummy_int;
   unsigned int dummy_uint;
@@ -3285,31 +3302,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 
 #ifdef HAVE_X_WINDOWS
   /* Adjust coordinates to relative to the outer (window manager) window.  */
-  {
-    Window child;
-    int win_x = 0, win_y = 0;
-
-    /* Find the position of the outside upper-left corner of
-       the inner window, with respect to the outer window.  */
-    if (f->output_data.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
-      {
-       BLOCK_INPUT;
-       XTranslateCoordinates (FRAME_X_DISPLAY (f),
-
-                              /* From-window, to-window.  */
-                              f->output_data.x->window_desc,
-                              f->output_data.x->parent_desc,
-
-                              /* From-position, to-position.  */
-                              0, 0, &win_x, &win_y,
-
-                              /* Child of window.  */
-                              &child);
-       UNBLOCK_INPUT;
-       x += win_x;
-       y += win_y;
-      }
-  }
+  x += FRAME_OUTER_TO_INNER_DIFF_X (f);
+  y += FRAME_OUTER_TO_INNER_DIFF_Y (f);
 #endif /* HAVE_X_WINDOWS */
 
   /* Adjust coordinates to be root-window-relative.  */
@@ -3317,7 +3311,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   y += f->top_pos;
 
   /* Create all the necessary panes and their items.  */
-  i = 0;
+  maxlines = lines = i = 0;
   while (i < menu_items_used)
     {
       if (EQ (XVECTOR (menu_items)->contents[i], Qt))
@@ -3326,6 +3320,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
          Lisp_Object pane_name, prefix;
          char *pane_string;
 
+          maxlines = max (maxlines, lines);
+          lines = 0;
          pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
          prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
          pane_string = (NILP (pane_name)
@@ -3418,9 +3414,12 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
              return Qnil;
            }
          i += MENU_ITEMS_ITEM_LENGTH;
+          lines++;
        }
     }
 
+  maxlines = max (maxlines, lines);
+
   /* All set and ready to fly.  */
   XMenuRecompute (FRAME_X_DISPLAY (f), menu);
   dispwidth = DisplayWidth (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
@@ -3444,6 +3443,15 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   if (ulx < 0) x -= ulx;
   if (uly < 0) y -= uly;
 
+  if (! for_click)
+    {
+      /* If position was not given by a mouse click, adjust so upper left
+         corner of the menu as a whole ends up at given coordinates.  This
+         is what x-popup-menu says in its documentation.  */
+      x += width/2;
+      y += 1.5*height/(maxlines+2);
+    }
+
   XMenuSetAEQ (menu, TRUE);
   XMenuSetFreeze (menu, TRUE);
   pane = selidx = 0;
@@ -3452,13 +3460,13 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   XMenuActivateSetWaitFunction (x_menu_wait_for_event, FRAME_X_DISPLAY (f));
 #endif
   
-  XSETFRAME (frame, f);
-  record_unwind_protect (pop_down_menu, frame);
+  record_unwind_protect (pop_down_menu,
+                         Fcons (make_save_value (f, 0),
+                                make_save_value (menu, 0)));
 
   /* Help display under X won't work because XMenuActivate contains
      a loop that doesn't give Emacs a chance to process it.  */
   menu_help_frame = f;
-  current_menu = menu;
   status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
                           x, y, ButtonReleaseMask, &datap,
                           menu_help_callback);
@@ -3508,7 +3516,13 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
     case XM_FAILURE:
       *error = "Can't activate menu";
     case XM_IA_SELECT:
+      entry = Qnil;
+      break;
     case XM_NO_SELECT:
+      /* Make "Cancel" equivalent to C-g unless this menu was popped up by
+         a mouse press.  */
+      if (! for_click)
+        Fsignal (Qquit, Qnil);
       entry = Qnil;
       break;
     }