Merged in changes from CVS trunk.
[bpt/emacs.git] / src / xmenu.c
index 68aad9c..2ca6e24 100644 (file)
@@ -1,5 +1,5 @@
 /* X Communication module for terminals which understand the X protocol.
-   Copyright (C) 1986, 88, 93, 94, 96, 99, 2000, 2001
+   Copyright (C) 1986, 88, 93, 94, 96, 99, 2000, 2001, 2003
    Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -39,10 +39,10 @@ Boston, MA 02111-1307, USA.  */
 #include <stdio.h>
 
 #include "lisp.h"
-#include "termhooks.h"
 #include "keyboard.h"
 #include "keymap.h"
 #include "frame.h"
+#include "termhooks.h"
 #include "window.h"
 #include "blockinput.h"
 #include "buffer.h"
@@ -68,6 +68,8 @@ Boston, MA 02111-1307, USA.  */
 #include "dispextern.h"
 
 #ifdef HAVE_X_WINDOWS
+/*  Defining HAVE_MULTILINGUAL_MENU would mean that the toolkit menu
+    code accepts the Emacs internal encoding.  */
 #undef HAVE_MULTILINGUAL_MENU
 #ifdef USE_X_TOOLKIT
 #include "widget.h"
@@ -97,7 +99,6 @@ Lisp_Object Vmenu_updating_frame;
 Lisp_Object Qdebug_on_next_call;
 
 extern Lisp_Object Qmenu_bar;
-extern Lisp_Object Qmouse_click, Qevent_kind;
 
 extern Lisp_Object QCtoggle, QCradio;
 
@@ -127,6 +128,22 @@ extern void set_frame_menubar ();
 static Lisp_Object xdialog_show ();
 #endif
 
+/* This is how to deal with multibyte text if HAVE_MULTILINGUAL_MENU
+   isn't defined.  The use of HAVE_MULTILINGUAL_MENU could probably be
+   confined to an extended version of this with sections of code below
+   using it unconditionally.  */
+#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  */
+# define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str)
+#endif
+
 static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
                                Lisp_Object, Lisp_Object, Lisp_Object,
                                Lisp_Object, Lisp_Object));
@@ -417,7 +434,7 @@ struct skp
   };
 
 static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
-                                 struct skp*));
+                                 void *));
 
 /* This is a recursive subroutine of keymap_panes.
    It handles one keymap, KEYMAP.
@@ -489,13 +506,14 @@ single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
    not.  */
 
 static void
-single_menu_item (key, item, dummy, skp)
+single_menu_item (key, item, dummy, skp_v)
      Lisp_Object key, item, dummy;
-     struct skp *skp;
+     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);
@@ -591,7 +609,7 @@ single_menu_item (key, item, dummy, skp)
 #endif /* not HAVE_BOXES */
 
 #if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
-  if (!NILP(map))
+  if (!NILP (map))
     /* Indicate visually that this is a submenu.  */
     item_string = concat2 (item_string, build_string (" >"));
 #endif
@@ -632,7 +650,7 @@ list_of_panes (menu)
       elt = Fcar (tail);
       pane_name = Fcar (elt);
       CHECK_STRING (pane_name);
-      push_menu_pane (pane_name, Qnil);
+      push_menu_pane (ENCODE_MENU_STRING (pane_name), Qnil);
       pane_data = Fcdr (elt);
       CHECK_CONS (pane_data);
       list_of_items (pane_data);
@@ -653,7 +671,8 @@ list_of_items (pane)
     {
       item = Fcar (tail);
       if (STRINGP (item))
-       push_menu_item (item, Qnil, Qnil, Qt, Qnil, Qnil, Qnil, Qnil);
+       push_menu_item (ENCODE_MENU_STRING (item), Qnil, Qnil, Qt,
+                       Qnil, Qnil, Qnil, Qnil);
       else if (NILP (item))
        push_left_right_boundary ();
       else
@@ -661,7 +680,8 @@ list_of_items (pane)
          CHECK_CONS (item);
          item1 = Fcar (item);
          CHECK_STRING (item1);
-         push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil, Qnil, Qnil, Qnil);
+         push_menu_item (ENCODE_MENU_STRING (item1), Qt, Fcdr (item),
+                         Qt, Qnil, Qnil, Qnil, Qnil);
        }
     }
 }
@@ -675,7 +695,7 @@ list_of_items (pane)
    the scroll bar or the edit window.  Fx_popup_menu needs to be
    sure it is the edit window.  */
 static void
-mouse_position_for_popup(f, x, y)
+mouse_position_for_popup (f, x, y)
      FRAME_PTR f;
      int *x;
      int *y;
@@ -708,10 +728,8 @@ mouse_position_for_popup(f, x, y)
 
   /* xmenu_show expects window coordinates, not root window
      coordinates.  Translate.  */
-  *x -= f->output_data.x->left_pos
-    + FRAME_OUTER_TO_INNER_DIFF_X (f);
-  *y -= f->output_data.x->top_pos
-    + FRAME_OUTER_TO_INNER_DIFF_Y (f);
+  *x -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
+  *y -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
 }
 
 #endif /* HAVE_X_WINDOWS */
@@ -860,10 +878,8 @@ cached information about equivalent key sequences.  */)
          CHECK_LIVE_WINDOW (window);
          f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
 
-         xpos = (FONT_WIDTH (FRAME_FONT (f))
-                 * XFASTINT (XWINDOW (window)->left));
-         ypos = (FRAME_LINE_HEIGHT (f)
-                 * XFASTINT (XWINDOW (window)->top));
+         xpos = WINDOW_LEFT_EDGE_X (XWINDOW (window));
+         ypos = WINDOW_TOP_EDGE_Y (XWINDOW (window));
        }
       else
        /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
@@ -1076,6 +1092,12 @@ on the left of the dialog box and all following items on the right.
     CHECK_STRING (title);
     record_unwind_protect (unuse_menu_items, Qnil);
 
+    if (NILP (Fcar (Fcdr (contents))))
+      /* No buttons specified, add an "Ok" button so users can pop down
+         the dialog.  Also, the lesstif/motif version crashes if there are
+         no buttons.  */
+      contents = Fcons (title, Fcons (Fcons (build_string ("Ok"), Qt), Qnil));
+    
     list_of_panes (Fcons (contents, Qnil));
 
     /* Display them in a dialog box.  */
@@ -1099,17 +1121,19 @@ on the left of the dialog box and all following items on the right.
    and x-popup-dialog; it is not used for the menu bar.
 
    If DO_TIMERS is nonzero, run timers.
+   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.  */
 
 #ifdef USE_X_TOOLKIT
 static void
-popup_get_selection (initial_event, dpyinfo, id, do_timers)
+popup_get_selection (initial_event, dpyinfo, id, do_timers, down_on_keypress)
      XEvent *initial_event;
      struct x_display_info *dpyinfo;
      LWLIB_ID id;
      int do_timers;
+     int down_on_keypress;
 {
   XEvent event;
 
@@ -1145,15 +1169,20 @@ popup_get_selection (initial_event, dpyinfo, id, do_timers)
           event.xbutton.state = 0;
 #endif
         }
-      /* If the user presses a key, deactivate the menu.
+      /* 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.
-         This is mostly for Lucid, Motif pops down the menu on ESC.  */
+         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. */
       else if (event.type == KeyPress
+               && down_on_keypress
                && dpyinfo->display == event.xbutton.display)
         {
           KeySym keysym = XLookupKeysym (&event.xkey, 0);
-          if (!IsModifierKey (keysym))
-            popup_activated_flag = 0;
+          if (!IsModifierKey (keysym)
+              && x_any_window_to_frame (dpyinfo, event.xany.window) != NULL)
+           popup_activated_flag = 0;
         }
 
       x_dispatch_event (&event, event.xany.display);
@@ -1200,7 +1229,8 @@ x_activate_menubar (f)
     return;
 
 #ifdef USE_GTK
-  if (! xg_win_to_widget (f->output_data.x->saved_menu_event->xany.window))
+  if (! xg_win_to_widget (FRAME_X_DISPLAY (f),
+                          f->output_data.x->saved_menu_event->xany.window))
     return;
 #endif
 
@@ -1286,6 +1316,7 @@ show_help_event (f, widget, help)
     }
   else
     {
+#if 0  /* This code doesn't do anything useful.  ++kfs */
       /* WIDGET is the popup menu.  It's parent is the frame's
         widget.  See which frame that is.  */
       xt_or_gtk_widget frame_widget = XtParent (widget);
@@ -1299,7 +1330,7 @@ show_help_event (f, widget, help)
                  FRAME_X_P (f) && f->output_data.x->widget == frame_widget))
            break;
        }
-
+#endif
       show_help_echo (help, Qnil, Qnil, Qnil, 1);
     }
 }
@@ -1402,6 +1433,7 @@ find_and_call_menu_selection (f, menu_bar_items_used, vector, client_data)
              int j;
              struct input_event buf;
              Lisp_Object frame;
+             EVENT_INIT (buf);
 
              XSETFRAME (frame, f);
              buf.kind = MENU_BAR_EVENT;
@@ -1700,13 +1732,13 @@ digest_single_submenu (start, end, top_level_items)
 #ifndef HAVE_MULTILINGUAL_MENU
           if (STRING_MULTIBYTE (item_name))
            {
-             item_name = ENCODE_SYSTEM (item_name);
+             item_name = ENCODE_MENU_STRING (item_name);
              AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
            }
 
           if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
            {
-             descrip = ENCODE_SYSTEM (descrip);
+             descrip = ENCODE_MENU_STRING (descrip);
              AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
            }
 #endif /* not HAVE_MULTILINGUAL_MENU */
@@ -1778,8 +1810,8 @@ update_frame_menubar (f)
   BLOCK_INPUT;
   /* Save the size of the frame because the pane widget doesn't accept
      to resize itself. So force it.  */
-  columns = f->width;
-  rows = f->height;
+  columns = FRAME_COLS (f);
+  rows = FRAME_LINES (f);
 
   /* Do the voodoo which means "I'm changing lots of things, don't try
      to refigure sizes until I'm done."  */
@@ -1848,6 +1880,12 @@ set_frame_menubar (f, first_time, deep_p)
       f->output_data.x->saved_menu_event->type = 0;
     }
 
+#ifdef USE_GTK
+  /* If we have detached menus, we must update deep so detached menus
+     also gets updated.  */
+  deep_p = deep_p || xg_have_tear_offs ();
+#endif
+
   if (deep_p)
     {
       /* Make a widget-value tree representing the entire menu trees.  */
@@ -2048,7 +2086,7 @@ set_frame_menubar (f, first_time, deep_p)
   xg_crazy_callback_abort = 1;
   if (menubar_widget)
     {
-      /* The third arg is DEEP_P, which says to consider the entire
+      /* The fourth arg is DEEP_P, which says to consider the entire
         menu trees we supply, rather than just the menu bar item names.  */
       xg_modify_menubar_widgets (menubar_widget,
                                  f,
@@ -2313,8 +2351,8 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
       pos_func = menu_position_func;
 
       /* Adjust coordinates to be root-window-relative.  */
-      x += f->output_data.x->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
-      y += f->output_data.x->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
+      x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
+      y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
 
       popup_x_y.x = x;
       popup_x_y.y = y;
@@ -2325,17 +2363,13 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
   gtk_widget_show_all (menu);
   gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i, 0);
 
-  xg_did_tearoff = 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 ();
 
-  if (xg_did_tearoff)
-    xg_keep_popup (menu, xg_did_tearoff);
-  else
-    gtk_widget_destroy (menu);
+  gtk_widget_destroy (menu);
 
   /* Must reset this manually because the button release event is not passed
      to Emacs event loop. */
@@ -2380,7 +2414,6 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
   XButtonPressedEvent dummy;
   LWLIB_ID menu_id;
   Widget menu;
-  Window child;
 
   menu_id = widget_id_tick++;
   menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
@@ -2401,8 +2434,8 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
   dummy.y = y;
 
   /* Adjust coordinates to be root-window-relative.  */
-  x += f->output_data.x->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
-  y += f->output_data.x->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
+  x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
+  y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
 
   dummy.x_root = x;
   dummy.y_root = y;
@@ -2422,7 +2455,7 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
   popup_activated_flag = 1;
 
   /* Process events that apply to the menu.  */
-  popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 0);
+  popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 0, 0);
 
   /* fp turned off the following statement and wrote a comment
      that it is unnecessary--that the menu has already disappeared.
@@ -2564,13 +2597,13 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 #ifndef HAVE_MULTILINGUAL_MENU
           if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
            {
-             item_name = ENCODE_SYSTEM (item_name);
+             item_name = ENCODE_MENU_STRING (item_name);
              AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
            }
 
           if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
            {
-             descrip = ENCODE_SYSTEM (descrip);
+             descrip = ENCODE_MENU_STRING (descrip);
              AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
            }
 #endif /* not HAVE_MULTILINGUAL_MENU */
@@ -2630,7 +2663,7 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 
 #ifndef HAVE_MULTILINGUAL_MENU
       if (STRING_MULTIBYTE (title))
-       title = ENCODE_SYSTEM (title);
+       title = ENCODE_MENU_STRING (title);
 #endif
 
       wv_title->name = (char *) SDATA (title);
@@ -2816,7 +2849,8 @@ create_and_show_dialog (f, first_wv)
                            Fcons (make_number (dialog_id >> (fact)),
                                   make_number (dialog_id & ~(-1 << (fact)))));
 
-    popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id, 1);
+    popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f),
+                         dialog_id, 1, 1);
 
     unbind_to (count, Qnil);
   }
@@ -3119,8 +3153,8 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 #endif /* HAVE_X_WINDOWS */
 
   /* Adjust coordinates to be root-window-relative.  */
-  x += f->output_data.x->left_pos;
-  y += f->output_data.x->top_pos;
+  x += f->left_pos;
+  y += f->top_pos;
 
   /* Create all the necessary panes and their items.  */
   i = 0;
@@ -3359,3 +3393,6 @@ The enable predicate for a menu command should check this variable.  */);
   defsubr (&Sx_popup_dialog);
 #endif
 }
+
+/* arch-tag: 92ea573c-398e-496e-ac73-2436f7d63242
+   (do not change this comment) */