]> code.delx.au - gnu-emacs/blobdiff - src/xmenu.c
Merge from emacs--devo--0
[gnu-emacs] / src / xmenu.c
index 6de1dcb2bd15c40877994c6eafccd834ad0c4df3..bf9a9101ef043d0a0dd85bc2cfc21f43ed25fc5c 100644 (file)
@@ -1,12 +1,12 @@
 /* X Communication module for terminals which understand the X protocol.
    Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2002, 2003,
-                 2004, 2005, 2006 Free Software Foundation, Inc.
+                 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
@@ -41,10 +41,10 @@ Boston, MA 02110-1301, 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"
@@ -82,7 +82,11 @@ Boston, MA 02110-1301, USA.  */
 #include <X11/StringDefs.h>
 #include <X11/Shell.h>
 #ifdef USE_LUCID
+#ifdef HAVE_XAW3D
+#include <X11/Xaw3d/Paned.h>
+#else /* !HAVE_XAW3D */
 #include <X11/Xaw/Paned.h>
+#endif /* HAVE_XAW3D */
 #endif /* USE_LUCID */
 #include "../lwlib/lwlib.h"
 #else /* not USE_X_TOOLKIT */
@@ -97,10 +101,10 @@ Boston, MA 02110-1301, USA.  */
 #define FALSE 0
 #endif /* no TRUE */
 
-Lisp_Object Vmenu_updating_frame;
-
 Lisp_Object Qdebug_on_next_call;
 
+extern Lisp_Object Vmenu_updating_frame;
+
 extern Lisp_Object Qmenu_bar;
 
 extern Lisp_Object QCtoggle, QCradio;
@@ -325,8 +329,9 @@ restore_menu_items (saved)
   menu_items_used = XINT (XCAR (saved));
   saved = XCDR (saved);
   menu_items_n_panes = XINT (XCAR (saved));
-  saved = XCDR (saved);  
+  saved = XCDR (saved);
   menu_items_submenu_depth = XINT (XCAR (saved));
+  return Qnil;
 }
 
 /* Push the whole state of menu_items processing onto the specpdl.
@@ -740,6 +745,9 @@ mouse_position_for_popup (f, x, y)
   Window root, dummy_window;
   int dummy;
 
+  if (! FRAME_X_P (f))
+    abort ();
+
   BLOCK_INPUT;
 
   XQueryPointer (FRAME_X_DISPLAY (f),
@@ -821,7 +829,7 @@ no quit occurs and `x-popup-menu' returns nil.  */)
   int xpos = 0, ypos = 0;
   Lisp_Object title;
   char *error_name = NULL;
-  Lisp_Object selection;
+  Lisp_Object selection = Qnil;
   FRAME_PTR f = NULL;
   Lisp_Object x, y, window;
   int keymaps = 0;
@@ -935,6 +943,9 @@ no quit occurs and `x-popup-menu' returns nil.  */)
       xpos += XINT (x);
       ypos += XINT (y);
 
+      if (! FRAME_X_P (f))
+        error ("Can not put X menu on non-X terminal");
+
       XSETFRAME (Vmenu_updating_frame, f);
     }
   else
@@ -1123,6 +1134,9 @@ for instance using the window manager, then this produces a quit and
        but I don't want to make one now.  */
     CHECK_WINDOW (window);
 
+  if (! FRAME_X_P (f))
+    error ("Can not put X dialog on non-X terminal");
+
 #if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
   /* Display a menu with these alternatives
      in the middle of frame F.  */
@@ -1181,6 +1195,10 @@ x_menu_set_in_use (in_use)
 {
   menu_items_inuse = in_use ? Qt : Qnil;
   popup_activated_flag = in_use;
+#ifdef USE_X_TOOLKIT
+  if (popup_activated_flag)
+    x_activate_timeout_atimer ();
+#endif
 }
 
 /* Wait for an X event to arrive or for a timer to expire.  */
@@ -1300,9 +1318,124 @@ popup_get_selection (initial_event, dpyinfo, id, do_timers)
     }
 }
 
+DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
+       doc: /* Start key navigation of the menu bar in FRAME.
+This initially opens the first menu bar item and you can then navigate with the
+arrow keys, select a menu entry with the return key or cancel with the
+escape key.  If FRAME has no menu bar this function does nothing.
+
+If FRAME is nil or not given, use the selected frame.  */)
+     (frame)
+     Lisp_Object frame;
+{
+  XEvent ev;
+  FRAME_PTR f = check_x_frame (frame);
+  Widget menubar;
+  BLOCK_INPUT;
+
+  if (FRAME_EXTERNAL_MENU_BAR (f))
+    set_frame_menubar (f, 0, 1);
+
+  menubar = FRAME_X_OUTPUT (f)->menubar_widget;
+  if (menubar)
+    {
+      Window child;
+      int error_p = 0;
+
+      x_catch_errors (FRAME_X_DISPLAY (f));
+      memset (&ev, 0, sizeof ev);
+      ev.xbutton.display = FRAME_X_DISPLAY (f);
+      ev.xbutton.window = XtWindow (menubar);
+      ev.xbutton.root = FRAME_X_DISPLAY_INFO (f)->root_window;
+      ev.xbutton.time = XtLastTimestampProcessed (FRAME_X_DISPLAY (f));
+      ev.xbutton.button = Button1;
+      ev.xbutton.x = ev.xbutton.y = FRAME_MENUBAR_HEIGHT (f) / 2;
+      ev.xbutton.same_screen = True;
+
+#ifdef USE_MOTIF
+      {
+        Arg al[2];
+        WidgetList list;
+        Cardinal nr;
+        XtSetArg (al[0], XtNchildren, &list);
+        XtSetArg (al[1], XtNnumChildren, &nr);
+        XtGetValues (menubar, al, 2);
+        ev.xbutton.window = XtWindow (list[0]);
+      }
+#endif
+
+      XTranslateCoordinates (FRAME_X_DISPLAY (f),
+                             /* From-window, to-window.  */
+                             ev.xbutton.window, ev.xbutton.root,
+
+                             /* From-position, to-position.  */
+                             ev.xbutton.x, ev.xbutton.y,
+                             &ev.xbutton.x_root, &ev.xbutton.y_root,
+
+                             /* Child of win.  */
+                             &child);
+      error_p = x_had_errors_p (FRAME_X_DISPLAY (f));
+      x_uncatch_errors ();
+
+      if (! error_p)
+        {
+          ev.type = ButtonPress;
+          ev.xbutton.state = 0;
+
+          XtDispatchEvent (&ev);
+          ev.xbutton.type = ButtonRelease;
+          ev.xbutton.state = Button1Mask;
+          XtDispatchEvent (&ev);
+        }
+    }
+
+  UNBLOCK_INPUT;
+
+  return Qnil;
+}
 #endif /* USE_X_TOOLKIT */
 
+
 #ifdef USE_GTK
+DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
+       doc: /* Start key navigation of the menu bar in FRAME.
+This initially opens the first menu bar item and you can then navigate with the
+arrow keys, select a menu entry with the return key or cancel with the
+escape key.  If FRAME has no menu bar this function does nothing.
+
+If FRAME is nil or not given, use the selected frame.  */)
+     (frame)
+     Lisp_Object frame;
+{
+  GtkWidget *menubar;
+  FRAME_PTR f;
+
+  /* gcc 2.95 doesn't accept the FRAME_PTR declaration after
+     BLOCK_INPUT.  */
+
+  BLOCK_INPUT;
+  f = check_x_frame (frame);
+
+  if (FRAME_EXTERNAL_MENU_BAR (f))
+    set_frame_menubar (f, 0, 1);
+
+  menubar = FRAME_X_OUTPUT (f)->menubar_widget;
+  if (menubar)
+    {
+      /* Activate the first menu.  */
+      GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));
+
+      gtk_menu_shell_select_item (GTK_MENU_SHELL (menubar),
+                                  GTK_WIDGET (children->data));
+
+      popup_activated_flag = 1;
+      g_list_free (children);
+    }
+  UNBLOCK_INPUT;
+
+  return Qnil;
+}
+
 /* Loop util popup_activated_flag is set to zero in a callback.
    Used for popup menus and dialogs. */
 
@@ -1340,6 +1473,9 @@ void
 x_activate_menubar (f)
      FRAME_PTR f;
 {
+  if (! FRAME_X_P (f))
+    abort ();
+
   if (!f->output_data.x->saved_menu_event->type)
     return;
 
@@ -1368,14 +1504,6 @@ x_activate_menubar (f)
   f->output_data.x->saved_menu_event->type = 0;
 }
 
-/* Detect if a dialog or menu has been posted.  */
-
-int
-popup_activated ()
-{
-  return popup_activated_flag;
-}
-
 /* This callback is invoked when the user selects a menubar cascade
    pushbutton, but before the pulldown menu is posted.  */
 
@@ -1387,6 +1515,9 @@ popup_activate_callback (widget, id, client_data)
      XtPointer client_data;
 {
   popup_activated_flag = 1;
+#ifdef USE_X_TOOLKIT
+  x_activate_timeout_atimer ();
+#endif
 }
 #endif
 
@@ -1979,9 +2110,14 @@ update_frame_menubar (f)
 #ifdef USE_GTK
   return xg_update_frame_menubar (f);
 #else
-  struct x_output *x = f->output_data.x;
+  struct x_output *x;
   int columns, rows;
 
+  if (! FRAME_X_P (f))
+    abort ();
+
+  x = f->output_data.x;
+
   if (!x->menubar_widget || XtIsManaged (x->menubar_widget))
     return 0;
 
@@ -2027,7 +2163,7 @@ set_frame_menubar (f, first_time, deep_p)
      int first_time;
      int deep_p;
 {
-  xt_or_gtk_widget menubar_widget = f->output_data.x->menubar_widget;
+  xt_or_gtk_widget menubar_widget;
 #ifdef USE_X_TOOLKIT
   LWLIB_ID id;
 #endif
@@ -2037,6 +2173,10 @@ set_frame_menubar (f, first_time, deep_p)
   int *submenu_start, *submenu_end;
   int *submenu_top_level_items, *submenu_n_panes;
 
+  if (! FRAME_X_P (f))
+    abort ();
+
+  menubar_widget = f->output_data.x->menubar_widget;
 
   XSETFRAME (Vmenu_updating_frame, f);
 
@@ -2180,21 +2320,27 @@ set_frame_menubar (f, first_time, deep_p)
       /* 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,
@@ -2383,6 +2529,9 @@ free_frame_menubar (f)
 {
   Widget menubar_widget;
 
+  if (! FRAME_X_P (f))
+    abort ();
+
   menubar_widget = f->output_data.x->menubar_widget;
 
   f->output_data.x->menubar_height = 0;
@@ -2535,6 +2684,9 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
   struct next_popup_x_y popup_x_y;
   int specpdl_count = SPECPDL_INDEX ();
 
+  if (! FRAME_X_P (f))
+    abort ();
+
   xg_crazy_callback_abort = 1;
   menu = xg_create_widget ("popup", first_wv->name, f, first_wv,
                            G_CALLBACK (popup_selection_callback),
@@ -2643,6 +2795,9 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
   LWLIB_ID menu_id;
   Widget menu;
 
+  if (! FRAME_X_P (f))
+    abort ();
+
   menu_id = widget_id_tick++;
   menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
                            f->output_data.x->widget, 1, 0,
@@ -2681,6 +2836,7 @@ create_and_show_popup_menu (f, first_wv, x, y, for_click)
   /* Display the menu.  */
   lw_popup_menu (menu, (XEvent *) &dummy);
   popup_activated_flag = 1;
+  x_activate_timeout_atimer ();
 
   {
     int fact = 4 * sizeof (LWLIB_ID);
@@ -2718,6 +2874,9 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 
   int first_pane;
 
+  if (! FRAME_X_P (f))
+    abort ();
+
   *error = NULL;
 
   if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
@@ -3000,6 +3159,9 @@ create_and_show_dialog (f, first_wv)
 {
   GtkWidget *menu;
 
+  if (! FRAME_X_P (f))
+    abort ();
+
   menu = xg_create_widget ("dialog", first_wv->name, f, first_wv,
                            G_CALLBACK (dialog_selection_callback),
                            G_CALLBACK (popup_deactivate_callback),
@@ -3049,6 +3211,9 @@ create_and_show_dialog (f, first_wv)
 {
   LWLIB_ID dialog_id;
 
+  if (!FRAME_X_P (f))
+    abort();
+
   dialog_id = widget_id_tick++;
   lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
                     f->output_data.x->widget, 1, 0,
@@ -3058,6 +3223,7 @@ create_and_show_dialog (f, first_wv)
   /* Display the dialog box.  */
   lw_pop_up_all_widgets (dialog_id);
   popup_activated_flag = 1;
+  x_activate_timeout_atimer ();
 
   /* Process events that apply to the dialog box.
      Also handle timers.  */
@@ -3100,6 +3266,9 @@ xdialog_show (f, keymaps, title, header, error_name)
   /* 1 means we've seen the boundary between left-hand elts and right-hand.  */
   int boundary_seen = 0;
 
+  if (! FRAME_X_P (f))
+    abort ();
+
   *error_name = NULL;
 
   if (menu_items_n_panes > 1)
@@ -3210,6 +3379,14 @@ xdialog_show (f, keymaps, title, header, error_name)
   /* No selection has been chosen yet.  */
   menu_item_selection = 0;
 
+  /* Force a redisplay before showing the dialog.  If a frame is created
+     just before showing the dialog, its contents may not have been fully
+     drawn, as this depends on timing of events from the X server.  Redisplay
+     is not done when a dialog is shown.  If redisplay could be done in the
+     X event loop (i.e. the X event loop does not run in a signal handler)
+     this would not be needed.  */
+  Fredisplay (Qt);
+
   /* Actually create and show the dialog.  */
   create_and_show_dialog (f, first_wv);
 
@@ -3297,7 +3474,7 @@ menu_help_callback (help_string, pane, item)
     pane_name = first_item[MENU_ITEMS_PANE_NAME];
   else if (EQ (first_item[0], Qquote))
     /* This shouldn't happen, see xmenu_show.  */
-    pane_name = empty_string;
+    pane_name = empty_unibyte_string;
   else
     pane_name = first_item[MENU_ITEMS_ITEM_NAME];
 
@@ -3367,6 +3544,9 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
   unsigned int dummy_uint;
   int specpdl_count = SPECPDL_INDEX ();
 
+  if (! FRAME_X_P (f))
+    abort ();
+
   *error = 0;
   if (menu_items_n_panes == 0)
     return Qnil;
@@ -3630,6 +3810,27 @@ xmenu_show (f, x, y, for_click, keymaps, title, error)
 #endif /* not USE_X_TOOLKIT */
 
 #endif /* HAVE_MENUS */
+
+/* Detect if a dialog or menu has been posted.  */
+
+int
+popup_activated ()
+{
+  return popup_activated_flag;
+}
+
+/* The following is used by delayed window autoselection.  */
+
+DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
+       doc: /* Return t if a menu or popup dialog is active.  */)
+     ()
+{
+#ifdef HAVE_MENUS
+  return (popup_activated ()) ? Qt : Qnil;
+#else
+  return Qnil;
+#endif /* HAVE_MENUS */
+}
 \f
 void
 syms_of_xmenu ()
@@ -3641,17 +3842,20 @@ syms_of_xmenu ()
   Qdebug_on_next_call = intern ("debug-on-next-call");
   staticpro (&Qdebug_on_next_call);
 
-  DEFVAR_LISP ("menu-updating-frame", &Vmenu_updating_frame,
-              doc: /* Frame for which we are updating a menu.
-The enable predicate for a menu command should check this variable.  */);
-  Vmenu_updating_frame = Qnil;
-
 #ifdef USE_X_TOOLKIT
   widget_id_tick = (1<<16);
   next_menubar_widget_id = 1;
 #endif
 
   defsubr (&Sx_popup_menu);
+  defsubr (&Smenu_or_popup_active_p);
+
+#if defined (USE_GTK) || defined (USE_X_TOOLKIT)
+  defsubr (&Sx_menu_bar_open_internal);
+  Ffset (intern ("accelerate-menu"),
+        intern (Sx_menu_bar_open_internal.symbol_name));
+#endif
+
 #ifdef HAVE_MENUS
   defsubr (&Sx_popup_dialog);
 #endif