This commit was manufactured by cvs2svn to create branch 'chise-r21-4-18'.
[chise/xemacs-chise.git-] / src / menubar-x.c
diff --git a/src/menubar-x.c b/src/menubar-x.c
new file mode 100644 (file)
index 0000000..207a36f
--- /dev/null
@@ -0,0 +1,1388 @@
+/* Implements an elisp-programmable menubar -- X interface.
+   Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
+   Copyright (C) 2000 Ben Wing.
+
+This file is part of XEmacs.
+
+XEmacs 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) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* Synched up with: Not in FSF. */
+
+/* This file Mule-ized by Ben Wing, 7-8-00. */
+
+/* Authorship:
+
+   Created 16-dec-91 by Jamie Zawinski.
+   Menu filters and many other keywords added by Stig for 19.12.
+   Original device-abstraction work and GC cleanup work by Ben Wing for 19.13.
+   Menu accelerators c. 1997? by ??.  Moved here from event-stream.c.
+   Other work post-1996 by ??.
+*/
+
+#include <config.h>
+#include "lisp.h"
+
+#include "console-x.h"
+#include "EmacsFrame.h"
+#include "gui-x.h"
+#include "../lwlib/lwlib.h"
+
+#include "buffer.h"
+#include "commands.h"           /* zmacs_regions */
+#include "events.h"
+#include "frame.h"
+#include "gui.h"
+#include "keymap.h"
+#include "menubar.h"
+#include "opaque.h"
+#include "window.h"
+
+static int set_frame_menubar (struct frame *f,
+                             int deep_p,
+                             int first_time_p);
+
+#define FRAME_MENUBAR_DATA(frame) ((frame)->menubar_data)
+#define XFRAME_MENUBAR_DATA(frame) XPOPUP_DATA ((frame)->menubar_data)
+
+#define MENUBAR_TYPE   0
+#define SUBMENU_TYPE   1
+#define POPUP_TYPE     2
+
+\f
+/* Converting Lisp menu tree descriptions to lwlib's `widget_value' form.
+
+   menu_item_descriptor_to_widget_value() converts a lisp description of a
+   menubar into a tree of widget_value structures.  It allocates widget_values
+   with malloc_widget_value() and allocates other storage only for the `key'
+   slot.  All other slots are filled with pointers to Lisp_String data.  We
+   allocate a widget_value description of the menu or menubar, and hand it to
+   lwlib, which then makes a copy of it, which it manages internally.  We then
+   immediately free our widget_value tree; it will not be referenced again.
+
+   Incremental menu construction callbacks operate just a bit differently.
+   They allocate widget_values and call replace_widget_value_tree() to tell
+   lwlib to destructively modify the incremental stub (subtree) of its
+   separate widget_value tree.
+
+   This function is highly recursive (it follows the menu trees) and may call
+   eval.  The reason we keep pointers to lisp string data instead of copying
+   it and freeing it later is to avoid the speed penalty that would entail
+   (since this needs to be fast, in the simple cases at least).  (The reason
+   we malloc/free the keys slot is because there's not a lisp string around
+   for us to use in that case.)
+
+   Since we keep pointers to lisp strings, and we call eval, we could lose if
+   GC relocates (or frees) those strings.  It's not easy to gc protect the
+   strings because of the recursive nature of this function, and the fact that
+   it returns a data structure that gets freed later.  So...  we do the
+   sleaziest thing possible and inhibit GC for the duration.  This is probably
+   not a big deal...
+
+   We do not have to worry about the pointers to Lisp_String data after
+   this function successfully finishes.  lwlib copies all such data with
+   strdup().  */
+
+static widget_value *
+menu_item_descriptor_to_widget_value_1 (Lisp_Object desc,
+                                       int menu_type, int deep_p,
+                                       int filter_p,
+                                       int depth)
+{
+  /* This function cannot GC.
+     It is only called from menu_item_descriptor_to_widget_value, which
+     prohibits GC. */
+  int menubar_root_p = (menu_type == MENUBAR_TYPE && depth == 0);
+  int count = specpdl_depth ();
+  int partition_seen = 0;
+  widget_value *wv = xmalloc_widget_value ();
+  Lisp_Object wv_closure = make_opaque_ptr (wv);
+
+  record_unwind_protect (widget_value_unwind, wv_closure);
+
+  if (STRINGP (desc))
+    {
+      Bufbyte *string_chars = XSTRING_DATA (desc);
+      wv->type = (separator_string_p (string_chars) ? SEPARATOR_TYPE :
+                 TEXT_TYPE);
+      if (wv->type == SEPARATOR_TYPE)
+       {
+         wv->value = menu_separator_style_and_to_external (string_chars);
+       }
+      else
+       {
+         LISP_STRING_TO_EXTERNAL_MALLOC (desc, wv->name, Qlwlib_encoding);
+         wv->enabled = 1;
+         /* dverna Dec. 98: command_builder_operate_menu_accelerator will
+            manipulate the accel as a Lisp_Object if the widget has a name.
+            Since simple labels have a name, but no accel, we *must* set it
+            to nil */
+         wv->accel = LISP_TO_VOID (Qnil);
+       }
+    }
+  else if (VECTORP (desc))
+    {
+      Lisp_Object gui_item = gui_parse_item_keywords (desc);
+      if (!button_item_to_widget_value (Qmenubar,
+                                       gui_item, wv, 1,
+                                       (menu_type == MENUBAR_TYPE
+                                        && depth <= 1), 1, 1))
+       {
+         /* :included form was nil */
+         wv = NULL;
+         goto menu_item_done;
+       }
+    }
+  else if (CONSP (desc))
+    {
+      Lisp_Object incremental_data = desc;
+      widget_value *prev = 0;
+
+      if (STRINGP (XCAR (desc)))
+       {
+         Lisp_Object key, val;
+         Lisp_Object include_p = Qnil, hook_fn = Qnil, config_tag = Qnil;
+         Lisp_Object active_p = Qt;
+         Lisp_Object accel;
+         int included_spec = 0;
+         int active_spec = 0;
+         wv->type = CASCADE_TYPE;
+         wv->enabled = 1;
+         wv->name = add_accel_and_to_external (XCAR (desc));
+
+         accel = gui_name_accelerator (XCAR (desc));
+         wv->accel = LISP_TO_VOID (accel);
+
+         desc = Fcdr (desc);
+
+         while (key = Fcar (desc), KEYWORDP (key))
+           {
+             Lisp_Object cascade = desc;
+             desc = Fcdr (desc);
+             if (NILP (desc))
+               syntax_error ("Keyword in menu lacks a value", cascade);
+             val = Fcar (desc);
+             desc = Fcdr (desc);
+             if (EQ (key, Q_included))
+               include_p = val, included_spec = 1;
+             else if (EQ (key, Q_config))
+               config_tag = val;
+             else if (EQ (key, Q_filter))
+               hook_fn = val;
+             else if (EQ (key, Q_active))
+               active_p = val, active_spec = 1;
+             else if (EQ (key, Q_accelerator))
+               {
+                 if ( SYMBOLP (val)
+                      || CHARP (val))
+                   wv->accel = LISP_TO_VOID (val);
+                 else
+                   syntax_error ("bad keyboard accelerator", val);
+               }
+             else if (EQ (key, Q_label))
+               {
+                 /* implement in 21.2 */
+               }
+             else
+               syntax_error ("Unknown menu cascade keyword", cascade);
+           }
+
+         if ((!NILP (config_tag)
+              && NILP (Fmemq (config_tag, Vmenubar_configuration)))
+             || (included_spec && NILP (Feval (include_p))))
+           {
+             wv = NULL;
+             goto menu_item_done;
+           }
+
+         if (active_spec)
+           active_p = Feval (active_p);
+
+         if (!NILP (hook_fn) && !NILP (active_p))
+           {
+#if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
+             if (filter_p || depth == 0)
+               {
+#endif
+                 desc = call1_trapping_errors ("Error in menubar filter",
+                                               hook_fn, desc);
+                 if (UNBOUNDP (desc))
+                   desc = Qnil;
+#if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
+               }
+             else
+               {
+                 widget_value *incr_wv = xmalloc_widget_value ();
+                 wv->contents = incr_wv;
+                 incr_wv->type = INCREMENTAL_TYPE;
+                 incr_wv->enabled = 1;
+                 incr_wv->name = wv->name;
+                 incr_wv->name = xstrdup (wv->name);
+                 /* This is automatically GC protected through
+                    the call to lw_map_widget_values(); no need
+                    to worry. */
+                 incr_wv->call_data = LISP_TO_VOID (incremental_data);
+                 goto menu_item_done;
+               }
+#endif /* LWLIB_MENUBARS_LUCID || LWLIB_MENUBARS_MOTIF */
+           }
+         if (menu_type == POPUP_TYPE && popup_menu_titles && depth == 0)
+           {
+             /* Simply prepend three more widget values to the contents of
+                the menu: a label, and two separators (to get a double
+                line). */
+             widget_value *title_wv = xmalloc_widget_value ();
+             widget_value *sep_wv = xmalloc_widget_value ();
+             title_wv->type = TEXT_TYPE;
+             title_wv->name = xstrdup (wv->name);
+             title_wv->enabled = 1;
+             title_wv->next = sep_wv;
+             sep_wv->type = SEPARATOR_TYPE;
+             sep_wv->value = menu_separator_style_and_to_external ((Bufbyte *) "==");
+             sep_wv->next = 0;
+
+             wv->contents = title_wv;
+             prev = sep_wv;
+           }
+         wv->enabled = ! NILP (active_p);
+         if (deep_p && !wv->enabled  && !NILP (desc))
+           {
+             widget_value *dummy;
+             /* Add a fake entry so the menus show up */
+             wv->contents = dummy = xmalloc_widget_value ();
+             dummy->name = xstrdup ("(inactive)");
+             dummy->accel = LISP_TO_VOID (Qnil);
+             dummy->enabled = 0;
+             dummy->selected = 0;
+             dummy->value = NULL;
+             dummy->type = BUTTON_TYPE;
+             dummy->call_data = NULL;
+             dummy->next = NULL;
+
+             goto menu_item_done;
+           }
+
+       }
+      else if (menubar_root_p)
+       {
+         wv->name = xstrdup ("menubar");
+         wv->type = CASCADE_TYPE; /* Well, nothing else seems to fit and
+                                     this is ignored anyway...  */
+       }
+      else
+       {
+         syntax_error ("Menu name (first element) must be a string", desc);
+       }
+
+      if (deep_p || menubar_root_p)
+       {
+         widget_value *next;
+         for (; !NILP (desc); desc = Fcdr (desc))
+           {
+             Lisp_Object child = Fcar (desc);
+             if (menubar_root_p && NILP (child))       /* the partition */
+               {
+                 if (partition_seen)
+                   syntax_error
+                     ("More than one partition (nil) in menubar description",
+                      desc);
+                 partition_seen = 1;
+                 next = xmalloc_widget_value ();
+                 next->type = PUSHRIGHT_TYPE;
+               }
+             else
+               {
+                 next = menu_item_descriptor_to_widget_value_1
+                   (child, menu_type, deep_p, filter_p, depth + 1);
+               }
+             if (! next)
+               continue;
+             else if (prev)
+               prev->next = next;
+             else
+               wv->contents = next;
+             prev = next;
+           }
+       }
+      if (deep_p && !wv->contents)
+       wv = NULL;
+    }
+  else if (NILP (desc))
+    syntax_error ("nil may not appear in menu descriptions", desc);
+  else
+    syntax_error ("Unrecognized menu descriptor", desc);
+
+ menu_item_done:
+
+  if (wv)
+    {
+      /* Completed normally.  Clear out the object that widget_value_unwind()
+        will be called with to tell it not to free the wv (as we are
+        returning it.) */
+      set_opaque_ptr (wv_closure, 0);
+    }
+
+  unbind_to (count, Qnil);
+  return wv;
+}
+
+static widget_value *
+menu_item_descriptor_to_widget_value (Lisp_Object desc,
+                                     int menu_type, /* if this is a menubar,
+                                                       popup or sub menu */
+                                     int deep_p,    /*  */
+                                     int filter_p)  /* if :filter forms
+                                                       should run now */
+{
+  widget_value *wv;
+  int count = specpdl_depth ();
+  record_unwind_protect (restore_gc_inhibit,
+                        make_int (gc_currently_forbidden));
+  gc_currently_forbidden = 1;
+  /* Can't GC! */
+  wv = menu_item_descriptor_to_widget_value_1 (desc, menu_type, deep_p,
+                                              filter_p, 0);
+  unbind_to (count, Qnil);
+  return wv;
+}
+
+
+#if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
+int in_menu_callback;
+
+static Lisp_Object
+restore_in_menu_callback (Lisp_Object val)
+{
+  in_menu_callback = XINT (val);
+  return Qnil;
+}
+#endif /* LWLIB_MENUBARS_LUCID || LWLIB_MENUBARS_MOTIF */
+
+#if 0
+/* #### Sort of a hack needed to process Vactivate_menubar_hook
+   correctly wrt buffer-local values.  A correct solution would
+   involve adding a callback mechanism to run_hook().  This function
+   is currently unused.  */
+static int
+my_run_hook (Lisp_Object hooksym, int allow_global_p)
+{
+  /* This function can GC */
+  Lisp_Object tail;
+  Lisp_Object value = Fsymbol_value (hooksym);
+  int changes = 0;
+
+  if (!NILP (value) && (!CONSP (value) || EQ (XCAR (value), Qlambda)))
+    return !EQ (call0 (value), Qt);
+
+  EXTERNAL_LIST_LOOP (tail, value)
+    {
+      if (allow_global_p && EQ (XCAR (tail), Qt))
+       changes |= my_run_hook (Fdefault_value (hooksym), 0);
+      if (!EQ (call0 (XCAR (tail)), Qt))
+       changes = 1;
+    }
+  return changes;
+}
+#endif
+
+
+/* The order in which callbacks are run is funny to say the least.
+   It's sometimes tricky to avoid running a callback twice, and to
+   avoid returning prematurely.  So, this function returns true
+   if the menu's callbacks are no longer gc protected.  So long
+   as we unprotect them before allowing other callbacks to run,
+   everything should be ok.
+
+   The pre_activate_callback() *IS* intentionally called multiple times.
+   If client_data == NULL, then it's being called before the menu is posted.
+   If client_data != NULL, then client_data is a (widget_value *) and
+   client_data->data is a Lisp_Object pointing to a lisp submenu description
+   that must be converted into widget_values.  *client_data is destructively
+   modified.
+
+   #### Stig thinks that there may be a GC problem here due to the
+   fact that pre_activate_callback() is called multiple times, but I
+   think he's wrong.
+
+   */
+
+static void
+pre_activate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
+{
+  /* This function can GC */
+  struct device *d = get_device_from_display (XtDisplay (widget));
+  struct frame *f = x_any_window_to_frame (d, XtWindow (widget));
+  Lisp_Object frame;
+  int count;
+
+  /* set in lwlib to the time stamp associated with the most recent menu
+     operation */
+  extern Time x_focus_timestamp_really_sucks_fix_me_better;
+
+  if (!f)
+    f = x_any_window_to_frame (d, XtWindow (XtParent (widget)));
+  if (!f)
+    return;
+
+  /* make sure f is the selected frame */
+  XSETFRAME (frame, f);
+  Fselect_frame (frame);
+
+  if (client_data)
+    {
+      /* this is an incremental menu construction callback */
+      widget_value *hack_wv = (widget_value *) client_data;
+      Lisp_Object submenu_desc;
+      widget_value *wv;
+
+      assert (hack_wv->type == INCREMENTAL_TYPE);
+      VOID_TO_LISP (submenu_desc, hack_wv->call_data);
+
+      /*
+       * #### Fix the menu code so this isn't necessary.
+       *
+       * Protect against reentering the menu code otherwise we will
+       * crash later when the code gets confused at the state
+       * changes.
+       */
+      count = specpdl_depth ();
+      record_unwind_protect (restore_in_menu_callback,
+                            make_int (in_menu_callback));
+      in_menu_callback = 1;
+      wv = menu_item_descriptor_to_widget_value (submenu_desc, SUBMENU_TYPE,
+                                                1, 0);
+      unbind_to (count, Qnil);
+
+      if (!wv)
+       {
+         wv = xmalloc_widget_value ();
+         wv->type = CASCADE_TYPE;
+         wv->next = NULL;
+         wv->accel = LISP_TO_VOID (Qnil);
+         wv->contents = xmalloc_widget_value ();
+         wv->contents->type = TEXT_TYPE;
+         wv->contents->name = xstrdup ("No menu");
+         wv->contents->next = NULL;
+         wv->contents->accel = LISP_TO_VOID (Qnil);
+       }
+      assert (wv && wv->type == CASCADE_TYPE && wv->contents);
+      replace_widget_value_tree (hack_wv, wv->contents);
+      free_popup_widget_value_tree (wv);
+    }
+  else if (!POPUP_DATAP (FRAME_MENUBAR_DATA (f)))
+    return;
+  else
+    {
+#if 0 /* Unused, see comment below. */
+      int any_changes;
+
+      /* #### - this menubar update mechanism is expensively anti-social and
+        the activate-menubar-hook is now mostly obsolete. */
+      any_changes = my_run_hook (Qactivate_menubar_hook, 1);
+
+      /* #### - It is necessary to *ALWAYS* call set_frame_menubar() now that
+        incremental menus are implemented.  If a subtree of a menu has been
+        updated incrementally (a destructive operation), then that subtree
+        must somehow be wiped.
+
+        It is difficult to undo the destructive operation in lwlib because
+        a pointer back to lisp data needs to be hidden away somewhere.  So
+        that an INCREMENTAL_TYPE widget_value can be recreated...  Hmmmmm. */
+      if (any_changes ||
+         !XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date)
+       set_frame_menubar (f, 1, 0);
+#else
+      run_hook (Qactivate_menubar_hook);
+      set_frame_menubar (f, 1, 0);
+#endif
+      DEVICE_X_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) =
+       DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) =
+       x_focus_timestamp_really_sucks_fix_me_better;
+    }
+}
+
+static widget_value *
+compute_menubar_data (struct frame *f, Lisp_Object menubar, int deep_p)
+{
+  if (NILP (menubar))
+    return 0;
+  else
+    {
+      widget_value *data;
+      int count = specpdl_depth ();
+
+      record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
+      Fset_buffer (XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer);
+      data = menu_item_descriptor_to_widget_value (menubar, MENUBAR_TYPE,
+                                                  deep_p, 0);
+      unbind_to (count, Qnil);
+
+      return data;
+    }
+}
+
+static int
+set_frame_menubar (struct frame *f, int deep_p, int first_time_p)
+{
+  widget_value *data;
+  Lisp_Object menubar;
+  int menubar_visible;
+  long id;
+  /* As with the toolbar, the minibuffer does not have its own menubar. */
+  struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f));
+
+  if (! FRAME_X_P (f))
+    return 0;
+
+  /***** first compute the contents of the menubar *****/
+
+  if (! first_time_p)
+    {
+      /* evaluate `current-menubar' in the buffer of the selected window
+        of the frame in question. */
+      menubar = symbol_value_in_buffer (Qcurrent_menubar, w->buffer);
+    }
+  else
+    {
+      /* That's a little tricky the first time since the frame isn't
+        fully initialized yet. */
+      menubar = Fsymbol_value (Qcurrent_menubar);
+    }
+
+  if (NILP (menubar))
+    {
+      menubar = Vblank_menubar;
+      menubar_visible = 0;
+    }
+  else
+    menubar_visible = !NILP (w->menubar_visible_p);
+
+  data = compute_menubar_data (f, menubar, deep_p);
+  if (!data || (!data->next && !data->contents))
+    ABORT ();
+
+  if (NILP (FRAME_MENUBAR_DATA (f)))
+    {
+      struct popup_data *mdata =
+       alloc_lcrecord_type (struct popup_data, &lrecord_popup_data);
+
+      mdata->id = new_lwlib_id ();
+      mdata->last_menubar_buffer = Qnil;
+      mdata->menubar_contents_up_to_date = 0;
+      XSETPOPUP_DATA (FRAME_MENUBAR_DATA (f), mdata);
+    }
+
+  /***** now store into the menubar widget, creating it if necessary *****/
+
+  id = XFRAME_MENUBAR_DATA (f)->id;
+  if (!FRAME_X_MENUBAR_WIDGET (f))
+    {
+      Widget parent = FRAME_X_CONTAINER_WIDGET (f);
+
+      assert (first_time_p);
+
+      /* It's the first time we've mapped the menubar so compute its
+        contents completely once.  This makes sure that the menubar
+        components are created with the right type. */
+      if (!deep_p)
+       {
+         free_popup_widget_value_tree (data);
+         data = compute_menubar_data (f, menubar, 1);
+       }
+
+
+      FRAME_X_MENUBAR_WIDGET (f) =
+       lw_create_widget ("menubar", "menubar", id, data, parent,
+                         0, pre_activate_callback,
+                         popup_selection_callback, 0);
+
+    }
+  else
+    {
+      lw_modify_all_widgets (id, data, deep_p ? True : False);
+    }
+  free_popup_widget_value_tree (data);
+
+  XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date = deep_p;
+  XFRAME_MENUBAR_DATA (f)->last_menubar_buffer =
+    XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer;
+  return menubar_visible;
+}
+
+
+/* Called from x_create_widgets() to create the initial menubar of a frame
+   before it is mapped, so that the window is mapped with the menubar already
+   there instead of us tacking it on later and thrashing the window after it
+   is visible. */
+int
+x_initialize_frame_menubar (struct frame *f)
+{
+  return set_frame_menubar (f, 1, 1);
+}
+
+
+static LWLIB_ID last_popup_menu_selection_callback_id;
+
+static void
+popup_menu_selection_callback (Widget widget, LWLIB_ID id,
+                              XtPointer client_data)
+{
+  last_popup_menu_selection_callback_id = id;
+  popup_selection_callback (widget, id, client_data);
+  /* lw_destroy_all_widgets() will be called from popup_down_callback() */
+}
+
+static void
+popup_menu_down_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
+{
+  if (popup_handled_p (id))
+    return;
+  assert (popup_up_p != 0);
+  ungcpro_popup_callbacks (id);
+  popup_up_p--;
+  /* if this isn't called immediately after the selection callback, then
+     there wasn't a menu selection. */
+  if (id != last_popup_menu_selection_callback_id)
+    popup_selection_callback (widget, id, (XtPointer) -1);
+  lw_destroy_all_widgets (id);
+}
+
+\f
+static void
+make_dummy_xbutton_event (XEvent *dummy, Widget daddy, Lisp_Event *eev)
+     /* NULL for eev means query pointer */
+{
+  XButtonPressedEvent *btn = (XButtonPressedEvent *) dummy;
+
+  btn->type = ButtonPress;
+  btn->serial = 0;
+  btn->send_event = 0;
+  btn->display = XtDisplay (daddy);
+  btn->window = XtWindow (daddy);
+  if (eev)
+    {
+      Position shellx, shelly, framex, framey;
+      Arg al [2];
+      btn->time = eev->timestamp;
+      btn->button = eev->event.button.button;
+      btn->root = RootWindowOfScreen (XtScreen (daddy));
+      btn->subwindow = (Window) NULL;
+      btn->x = eev->event.button.x;
+      btn->y = eev->event.button.y;
+      shellx = shelly = 0;
+#ifndef HAVE_WMCOMMAND
+      {
+       Widget shell = XtParent (daddy);
+
+       XtSetArg (al [0], XtNx, &shellx);
+       XtSetArg (al [1], XtNy, &shelly);
+       XtGetValues (shell, al, 2);
+      }
+#endif
+      XtSetArg (al [0], XtNx, &framex);
+      XtSetArg (al [1], XtNy, &framey);
+      XtGetValues (daddy, al, 2);
+      btn->x_root = shellx + framex + btn->x;
+      btn->y_root = shelly + framey + btn->y;
+      btn->state = ButtonPressMask; /* all buttons pressed */
+    }
+  else
+    {
+      /* CurrentTime is just ZERO, so it's worthless for
+        determining relative click times. */
+      struct device *d = get_device_from_display (XtDisplay (daddy));
+      btn->time = DEVICE_X_MOUSE_TIMESTAMP (d); /* event-Xt maintains this */
+      btn->button = 0;
+      XQueryPointer (btn->display, btn->window, &btn->root,
+                    &btn->subwindow, &btn->x_root, &btn->y_root,
+                    &btn->x, &btn->y, &btn->state);
+    }
+}
+
+\f
+
+static void
+x_update_frame_menubar_internal (struct frame *f)
+{
+  /* We assume the menubar contents has changed if the global flag is set,
+     or if the current buffer has changed, or if the menubar has never
+     been updated before.
+   */
+  int menubar_contents_changed =
+    (f->menubar_changed
+     || NILP (FRAME_MENUBAR_DATA (f))
+     || (!EQ (XFRAME_MENUBAR_DATA (f)->last_menubar_buffer,
+             XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer)));
+
+  Boolean menubar_was_visible = XtIsManaged (FRAME_X_MENUBAR_WIDGET (f));
+  Boolean menubar_will_be_visible = menubar_was_visible;
+  Boolean menubar_visibility_changed;
+
+  if (menubar_contents_changed)
+    menubar_will_be_visible = set_frame_menubar (f, 0, 0);
+
+  menubar_visibility_changed = menubar_was_visible != menubar_will_be_visible;
+
+  if (!menubar_visibility_changed)
+    return;
+
+  /* Set menubar visibility */
+  (menubar_will_be_visible ? XtManageChild : XtUnmanageChild)
+    (FRAME_X_MENUBAR_WIDGET (f));
+
+  MARK_FRAME_SIZE_SLIPPED (f);
+}
+
+static void
+x_update_frame_menubars (struct frame *f)
+{
+  assert (FRAME_X_P (f));
+
+  x_update_frame_menubar_internal (f);
+
+  /* #### This isn't going to work right now that this function works on
+     a per-frame, not per-device basis.  Guess what?  I don't care. */
+}
+
+static void
+x_free_frame_menubars (struct frame *f)
+{
+  Widget menubar_widget;
+
+  assert (FRAME_X_P (f));
+
+  menubar_widget = FRAME_X_MENUBAR_WIDGET (f);
+  if (menubar_widget)
+    {
+      LWLIB_ID id = XFRAME_MENUBAR_DATA (f)->id;
+      lw_destroy_all_widgets (id);
+      XFRAME_MENUBAR_DATA (f)->id = 0;
+    }
+}
+
+static void
+x_popup_menu (Lisp_Object menu_desc, Lisp_Object event)
+{
+  int menu_id;
+  struct frame *f = selected_frame ();
+  widget_value *data;
+  Widget parent;
+  Widget menu;
+  Lisp_Event *eev = NULL;
+  XEvent xev;
+  Lisp_Object frame;
+
+  XSETFRAME (frame, f);
+  CHECK_X_FRAME (frame);
+  parent = FRAME_X_SHELL_WIDGET (f);
+
+  if (!NILP (event))
+    {
+      CHECK_LIVE_EVENT (event);
+      eev= XEVENT (event);
+      if (eev->event_type != button_press_event
+         && eev->event_type != button_release_event)
+       wrong_type_argument (Qmouse_event_p, event);
+    }
+  else if (!NILP (Vthis_command_keys))
+    {
+      /* if an event wasn't passed, use the last event of the event sequence
+        currently being executed, if that event is a mouse event */
+      eev = XEVENT (Vthis_command_keys); /* last event first */
+      if (eev->event_type != button_press_event
+         && eev->event_type != button_release_event)
+       eev = NULL;
+    }
+  make_dummy_xbutton_event (&xev, parent, eev);
+
+  if (SYMBOLP (menu_desc))
+    menu_desc = Fsymbol_value (menu_desc);
+  CHECK_CONS (menu_desc);
+  CHECK_STRING (XCAR (menu_desc));
+  data = menu_item_descriptor_to_widget_value (menu_desc, POPUP_TYPE, 1, 1);
+
+  if (! data) error ("no menu");
+
+  menu_id = new_lwlib_id ();
+  menu = lw_create_widget ("popup", "popup" /* data->name */, menu_id, data,
+                          parent, 1, 0,
+                          popup_menu_selection_callback,
+                          popup_menu_down_callback);
+  free_popup_widget_value_tree (data);
+
+  gcpro_popup_callbacks (menu_id);
+
+  /* Setting zmacs-region-stays is necessary here because executing a command
+     from a menu is really a two-command process: the first command (bound to
+     the button-click) simply pops up the menu, and returns.  This causes a
+     sequence of magic-events (destined for the popup-menu widget) to begin.
+     Eventually, a menu item is selected, and a menu-event blip is pushed onto
+     the end of the input stream, which is then executed by the event loop.
+
+     So there are two command-events, with a bunch of magic-events between
+     them.  We don't want the *first* command event to alter the state of the
+     region, so that the region can be available as an argument for the second
+     command.
+  */
+  if (zmacs_regions)
+    zmacs_region_stays = 1;
+
+  popup_up_p++;
+  lw_popup_menu (menu, &xev);
+  /* this speeds up display of pop-up menus */
+  XFlush (XtDisplay (parent));
+}
+
+\f
+
+#if defined(LWLIB_MENUBARS_LUCID)
+static void
+menu_move_up (void)
+{
+  widget_value *current = lw_get_entries (False);
+  widget_value *entries = lw_get_entries (True);
+  widget_value *prev    = NULL;
+
+  while (entries != current)
+    {
+      if (entries->name /*&& entries->enabled*/) prev = entries;
+      entries = entries->next;
+      assert (entries);
+    }
+
+  if (!prev)
+    /* move to last item */
+    {
+      while (entries->next)
+       {
+         if (entries->name /*&& entries->enabled*/) prev = entries;
+         entries = entries->next;
+       }
+      if (prev)
+       {
+         if (entries->name /*&& entries->enabled*/)
+           prev = entries;
+       }
+      else
+       {
+         /* no selectable items in this menu, pop up to previous level */
+         lw_pop_menu ();
+         return;
+       }
+    }
+  lw_set_item (prev);
+}
+
+static void
+menu_move_down (void)
+{
+  widget_value *current = lw_get_entries (False);
+  widget_value *new = current;
+
+  while (new->next)
+    {
+      new = new->next;
+      if (new->name /*&& new->enabled*/) break;
+    }
+
+  if (new==current||!(new->name/*||new->enabled*/))
+    {
+      new = lw_get_entries (True);
+      while (new!=current)
+       {
+         if (new->name /*&& new->enabled*/) break;
+         new = new->next;
+       }
+      if (new==current&&!(new->name /*|| new->enabled*/))
+       {
+         lw_pop_menu ();
+         return;
+       }
+    }
+
+  lw_set_item (new);
+}
+
+static void
+menu_move_left (void)
+{
+  int level = lw_menu_level ();
+  int l = level;
+  widget_value *current;
+
+  while (level-- >= 3)
+    lw_pop_menu ();
+
+  menu_move_up ();
+  current = lw_get_entries (False);
+  if (l > 2 && current->contents)
+    lw_push_menu (current->contents);
+}
+
+static void
+menu_move_right (void)
+{
+  int level = lw_menu_level ();
+  int l = level;
+  widget_value *current;
+
+  while (level-- >= 3)
+    lw_pop_menu ();
+
+  menu_move_down ();
+  current = lw_get_entries (False);
+  if (l > 2 && current->contents)
+    lw_push_menu (current->contents);
+}
+
+static void
+menu_select_item (widget_value *val)
+{
+  if (val == NULL)
+    val = lw_get_entries (False);
+
+  /* is match a submenu? */
+
+  if (val->contents)
+    {
+      /* enter the submenu */
+
+      lw_set_item (val);
+      lw_push_menu (val->contents);
+    }
+  else
+    {
+      /* Execute the menu entry by calling the menu's `select'
+        callback function
+      */
+      lw_kill_menus (val);
+    }
+}
+
+Lisp_Object
+command_builder_operate_menu_accelerator (struct command_builder *builder)
+{
+  /* this function can GC */
+
+  struct console *con = XCONSOLE (Vselected_console);
+  Lisp_Object evee = builder->most_current_event;
+  Lisp_Object binding;
+  widget_value *entries;
+
+  extern int lw_menu_accelerate; /* lwlib.c */
+
+#if 0
+  {
+    int i;
+    Lisp_Object t;
+    char buf[50];
+
+    t = builder->current_events;
+    i = 0;
+    while (!NILP (t))
+      {
+       i++;
+       sprintf (buf,"OPERATE (%d): ",i);
+       write_c_string (buf, Qexternal_debugging_output);
+       print_internal (t, Qexternal_debugging_output, 1);
+       write_c_string ("\n", Qexternal_debugging_output);
+       t = XEVENT_NEXT (t);
+      }
+  }
+#endif /* 0 */
+
+  /* menu accelerator keys don't go into keyboard macros */
+  if (!NILP (con->defining_kbd_macro) && NILP (Vexecuting_macro))
+    con->kbd_macro_ptr = con->kbd_macro_end;
+
+  /* don't echo menu accelerator keys */
+  /*reset_key_echo (builder, 1);*/
+
+  if (!lw_menu_accelerate)
+    {
+      /* `convert' mouse display to keyboard display
+        by entering the open submenu
+      */
+      entries = lw_get_entries (False);
+      if (entries->contents)
+       {
+         lw_push_menu (entries->contents);
+         lw_display_menu (CurrentTime);
+       }
+    }
+
+  /* compare event to the current menu accelerators */
+
+  entries=lw_get_entries (True);
+
+  while (entries)
+    {
+      Lisp_Object accel;
+      VOID_TO_LISP (accel, entries->accel);
+      if (entries->name && !NILP (accel))
+       {
+         if (event_matches_key_specifier_p (XEVENT (evee), accel))
+           {
+             /* a match! */
+
+             menu_select_item (entries);
+
+             if (lw_menu_active) lw_display_menu (CurrentTime);
+
+             reset_this_command_keys (Vselected_console, 1);
+             /*reset_command_builder_event_chain (builder);*/
+             return Vmenu_accelerator_map;
+           }
+       }
+      entries = entries->next;
+    }
+
+  /* try to look up event in menu-accelerator-map */
+
+  binding = event_binding_in (evee, Vmenu_accelerator_map, 1);
+
+  if (NILP (binding))
+    {
+      /* beep at user for undefined key */
+      return Qnil;
+    }
+  else
+    {
+      if (EQ (binding, Qmenu_quit))
+       {
+         /* turn off menus and set quit flag */
+         lw_kill_menus (NULL);
+         Vquit_flag = Qt;
+       }
+      else if (EQ (binding, Qmenu_up))
+       {
+         int level = lw_menu_level ();
+         if (level > 2)
+           menu_move_up ();
+       }
+      else if (EQ (binding, Qmenu_down))
+       {
+         int level = lw_menu_level ();
+         if (level > 2)
+           menu_move_down ();
+         else
+           menu_select_item (NULL);
+       }
+      else if (EQ (binding, Qmenu_left))
+       {
+         int level = lw_menu_level ();
+         if (level > 3)
+           {
+             lw_pop_menu ();
+             lw_display_menu (CurrentTime);
+           }
+         else
+           menu_move_left ();
+       }
+      else if (EQ (binding, Qmenu_right))
+       {
+         int level = lw_menu_level ();
+         if (level > 2 &&
+             lw_get_entries (False)->contents)
+           {
+             widget_value *current = lw_get_entries (False);
+             if (current->contents)
+               menu_select_item (NULL);
+           }
+         else
+           menu_move_right ();
+       }
+      else if (EQ (binding, Qmenu_select))
+       menu_select_item (NULL);
+      else if (EQ (binding, Qmenu_escape))
+       {
+         int level = lw_menu_level ();
+
+         if (level > 2)
+           {
+             lw_pop_menu ();
+             lw_display_menu (CurrentTime);
+           }
+         else
+           {
+             /* turn off menus quietly */
+             lw_kill_menus (NULL);
+           }
+       }
+      else if (KEYMAPP (binding))
+       {
+         /* prefix key */
+         reset_this_command_keys (Vselected_console, 1);
+         /*reset_command_builder_event_chain (builder);*/
+         return binding;
+       }
+      else
+       {
+         /* turn off menus and execute binding */
+         lw_kill_menus (NULL);
+         reset_this_command_keys (Vselected_console, 1);
+         /*reset_command_builder_event_chain (builder);*/
+         return binding;
+       }
+    }
+
+  if (lw_menu_active) lw_display_menu (CurrentTime);
+
+  reset_this_command_keys (Vselected_console, 1);
+  /*reset_command_builder_event_chain (builder);*/
+
+  return Vmenu_accelerator_map;
+}
+
+static Lisp_Object
+menu_accelerator_junk_on_error (Lisp_Object errordata, Lisp_Object ignored)
+{
+  Vmenu_accelerator_prefix    = Qnil;
+  Vmenu_accelerator_modifiers = Qnil;
+  Vmenu_accelerator_enabled   = Qnil;
+  if (!NILP (errordata))
+    {
+      Lisp_Object args[2];
+
+      args[0] = build_string ("Error in menu accelerators (setting to nil)");
+      /* #### This should call
+        (with-output-to-string (display-error errordata))
+        but that stuff is all in Lisp currently. */
+      args[1] = errordata;
+      warn_when_safe_lispobj
+       (Qerror, Qwarning,
+        emacs_doprnt_string_lisp ((const Bufbyte *) "%s: %s",
+                                  Qnil, -1, 2, args));
+    }
+
+  return Qnil;
+}
+
+static Lisp_Object
+menu_accelerator_safe_compare (Lisp_Object event0)
+{
+  if (CONSP (Vmenu_accelerator_prefix))
+    {
+      Lisp_Object t;
+      t=Vmenu_accelerator_prefix;
+      while (!NILP (t)
+            && !NILP (event0)
+            && event_matches_key_specifier_p (XEVENT (event0), Fcar (t)))
+       {
+         t = Fcdr (t);
+         event0 = XEVENT_NEXT (event0);
+       }
+      if (!NILP (t))
+       return Qnil;
+    }
+  else if (NILP (event0))
+    return Qnil;
+  else if (event_matches_key_specifier_p (XEVENT (event0), Vmenu_accelerator_prefix))
+    event0 = XEVENT_NEXT (event0);
+  else
+    return Qnil;
+  return event0;
+}
+
+static Lisp_Object
+menu_accelerator_safe_mod_compare (Lisp_Object cons)
+{
+  return (event_matches_key_specifier_p (XEVENT (XCAR (cons)), XCDR (cons))
+         ? Qt
+         : Qnil);
+}
+
+Lisp_Object
+command_builder_find_menu_accelerator (struct command_builder *builder)
+{
+  /* this function can GC */
+  Lisp_Object event0 = builder->current_events;
+  struct console *con = XCONSOLE (Vselected_console);
+  struct frame *f = XFRAME (CONSOLE_SELECTED_FRAME (con));
+  Widget menubar_widget;
+
+  /* compare entries in event0 against the menu prefix */
+
+  if ((!CONSOLE_X_P (XCONSOLE (builder->console))) || NILP (event0) ||
+      XEVENT (event0)->event_type != key_press_event)
+    return Qnil;
+
+  if (!NILP (Vmenu_accelerator_prefix))
+    {
+      event0 = condition_case_1 (Qerror,
+                                menu_accelerator_safe_compare,
+                                event0,
+                                menu_accelerator_junk_on_error,
+                                Qnil);
+    }
+
+  if (NILP (event0))
+    return Qnil;
+
+  menubar_widget = FRAME_X_MENUBAR_WIDGET (f);
+  if (menubar_widget
+      && CONSP (Vmenu_accelerator_modifiers))
+    {
+      Lisp_Object fake = Qnil;
+      Lisp_Object last = Qnil;
+      struct gcpro gcpro1;
+      Lisp_Object matchp;
+
+      widget_value *val;
+      LWLIB_ID id = XPOPUP_DATA (f->menubar_data)->id;
+
+      val = lw_get_all_values (id);
+      if (val)
+       {
+         val = val->contents;
+
+         fake = Fcopy_sequence (Vmenu_accelerator_modifiers);
+         last = fake;
+
+         while (!NILP (Fcdr (last)))
+           last = Fcdr (last);
+
+         Fsetcdr (last, Fcons (Qnil, Qnil));
+         last = Fcdr (last);
+       }
+
+      fake = Fcons (Qnil, fake);
+
+      GCPRO1 (fake);
+
+      while (val)
+       {
+         Lisp_Object accel;
+         VOID_TO_LISP (accel, val->accel);
+         if (val->name && !NILP (accel))
+           {
+             Fsetcar (last, accel);
+             Fsetcar (fake, event0);
+             matchp = condition_case_1 (Qerror,
+                                        menu_accelerator_safe_mod_compare,
+                                        fake,
+                                        menu_accelerator_junk_on_error,
+                                        Qnil);
+             if (!NILP (matchp))
+               {
+                 /* we found one! */
+
+                 lw_set_menu (menubar_widget, val);
+                 /* yah - yet another hack.
+                    pretend emacs timestamp is the same as an X timestamp,
+                    which for the moment it is.  (read events.h)
+                    */
+                 lw_map_menu (XEVENT (event0)->timestamp);
+
+                 if (val->contents)
+                   lw_push_menu (val->contents);
+
+                 lw_display_menu (CurrentTime);
+
+                 /* menu accelerator keys don't go into keyboard macros */
+                 if (!NILP (con->defining_kbd_macro)
+                     && NILP (Vexecuting_macro))
+                   con->kbd_macro_ptr = con->kbd_macro_end;
+
+                 /* don't echo menu accelerator keys */
+                 /*reset_key_echo (builder, 1);*/
+                 reset_this_command_keys (Vselected_console, 1);
+                 UNGCPRO;
+
+                 return Vmenu_accelerator_map;
+               }
+           }
+
+         val = val->next;
+       }
+
+      UNGCPRO;
+    }
+  return Qnil;
+}
+
+int
+x_kludge_lw_menu_active (void)
+{
+  return lw_menu_active;
+}
+
+DEFUN ("accelerate-menu", Faccelerate_menu, 0, 0, "_", /*
+Make the menubar active.  Menu items can be selected using menu accelerators
+or by actions defined in menu-accelerator-map.
+*/
+       ())
+{
+  struct console *con = XCONSOLE (Vselected_console);
+  struct frame *f = XFRAME (CONSOLE_SELECTED_FRAME (con));
+  LWLIB_ID id;
+  widget_value *val;
+
+  if (NILP (f->menubar_data))
+    error ("Frame has no menubar.");
+
+  id = XPOPUP_DATA (f->menubar_data)->id;
+  val = lw_get_all_values (id);
+  val = val->contents;
+  lw_set_menu (FRAME_X_MENUBAR_WIDGET (f), val);
+  lw_map_menu (CurrentTime);
+
+  lw_display_menu (CurrentTime);
+
+  /* menu accelerator keys don't go into keyboard macros */
+  if (!NILP (con->defining_kbd_macro) && NILP (Vexecuting_macro))
+    con->kbd_macro_ptr = con->kbd_macro_end;
+
+  return Qnil;
+}
+#endif /* LWLIB_MENUBARS_LUCID */
+
+\f
+void
+syms_of_menubar_x (void)
+{
+#if defined(LWLIB_MENUBARS_LUCID)
+  DEFSUBR (Faccelerate_menu);
+#endif
+}
+
+void
+console_type_create_menubar_x (void)
+{
+  CONSOLE_HAS_METHOD (x, update_frame_menubars);
+  CONSOLE_HAS_METHOD (x, free_frame_menubars);
+  CONSOLE_HAS_METHOD (x, popup_menu);
+}
+
+void
+reinit_vars_of_menubar_x (void)
+{
+  last_popup_menu_selection_callback_id = (LWLIB_ID) -1;
+}
+
+void
+vars_of_menubar_x (void)
+{
+  reinit_vars_of_menubar_x ();
+
+#if defined (LWLIB_MENUBARS_LUCID)
+  Fprovide (intern ("lucid-menubars"));
+#elif defined (LWLIB_MENUBARS_MOTIF)
+  Fprovide (intern ("motif-menubars"));
+#elif defined (LWLIB_MENUBARS_ATHENA)
+  Fprovide (intern ("athena-menubars"));
+#endif
+}