fecb4a7b0cc6b2b888eb991595de34f1d446a2e7
[chise/xemacs-chise.git.1] / src / menubar-x.c
1 /* Implements an elisp-programmable menubar -- X interface.
2    Copyright (C) 1993, 1994 Free Software Foundation, Inc.
3    Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Synched up with: Not in FSF. */
23
24 /* created 16-dec-91 by jwz */
25
26 #include <config.h>
27 #include "lisp.h"
28
29 #include "console-x.h"
30 #include "EmacsFrame.h"
31 #include "gui-x.h"
32
33 #include "buffer.h"
34 #include "commands.h"           /* zmacs_regions */
35 #include "gui.h"
36 #include "events.h"
37 #include "frame.h"
38 #include "opaque.h"
39 #include "window.h"
40
41 static int set_frame_menubar (struct frame *f,
42                               int deep_p,
43                               int first_time_p);
44
45 #define FRAME_MENUBAR_DATA(frame) ((frame)->menubar_data)
46 #define XFRAME_MENUBAR_DATA(frame) XPOPUP_DATA ((frame)->menubar_data)
47
48 #define MENUBAR_TYPE    0
49 #define SUBMENU_TYPE    1
50 #define POPUP_TYPE      2
51
52 \f
53 /* Converting Lisp menu tree descriptions to lwlib's `widget_value' form.
54
55    menu_item_descriptor_to_widget_value() converts a lisp description of a
56    menubar into a tree of widget_value structures.  It allocates widget_values
57    with malloc_widget_value() and allocates other storage only for the `key'
58    slot.  All other slots are filled with pointers to Lisp_String data.  We
59    allocate a widget_value description of the menu or menubar, and hand it to
60    lwlib, which then makes a copy of it, which it manages internally.  We then
61    immediately free our widget_value tree; it will not be referenced again.
62
63    Incremental menu construction callbacks operate just a bit differently.
64    They allocate widget_values and call replace_widget_value_tree() to tell
65    lwlib to destructively modify the incremental stub (subtree) of its
66    separate widget_value tree.
67
68    This function is highly recursive (it follows the menu trees) and may call
69    eval.  The reason we keep pointers to lisp string data instead of copying
70    it and freeing it later is to avoid the speed penalty that would entail
71    (since this needs to be fast, in the simple cases at least).  (The reason
72    we malloc/free the keys slot is because there's not a lisp string around
73    for us to use in that case.)
74
75    Since we keep pointers to lisp strings, and we call eval, we could lose if
76    GC relocates (or frees) those strings.  It's not easy to gc protect the
77    strings because of the recursive nature of this function, and the fact that
78    it returns a data structure that gets freed later.  So...  we do the
79    sleaziest thing possible and inhibit GC for the duration.  This is probably
80    not a big deal...
81
82    We do not have to worry about the pointers to Lisp_String data after
83    this function successfully finishes.  lwlib copies all such data with
84    strdup().  */
85
86 static widget_value *
87 menu_item_descriptor_to_widget_value_1 (Lisp_Object desc,
88                                         int menu_type, int deep_p,
89                                         int filter_p,
90                                         int depth)
91 {
92   /* This function cannot GC.
93      It is only called from menu_item_descriptor_to_widget_value, which
94      prohibits GC. */
95   /* !!#### This function has not been Mule-ized */
96   int menubar_root_p = (menu_type == MENUBAR_TYPE && depth == 0);
97   widget_value *wv;
98   Lisp_Object wv_closure;
99   int count = specpdl_depth ();
100   int partition_seen = 0;
101
102   wv = xmalloc_widget_value ();
103
104   wv_closure = make_opaque_ptr (wv);
105   record_unwind_protect (widget_value_unwind, wv_closure);
106
107   if (STRINGP (desc))
108     {
109       char *string_chars = (char *) XSTRING_DATA (desc);
110       wv->type = (separator_string_p (string_chars) ? SEPARATOR_TYPE :
111                   TEXT_TYPE);
112 #if 1
113       /* #### - should internationalize with X resources instead.
114          Not so! --ben */
115       string_chars = GETTEXT (string_chars);
116 #endif
117       if (wv->type == SEPARATOR_TYPE)
118         {
119           wv->value = menu_separator_style (string_chars);
120         }
121       else
122         {
123           wv->name = string_chars;
124           wv->enabled = 1;
125           /* dverna Dec. 98: command_builder_operate_menu_accelerator will
126              manipulate the accel as a Lisp_Object if the widget has a name.
127              Since simple labels have a name, but no accel, we *must* set it
128              to nil */
129           wv->accel = LISP_TO_VOID (Qnil);
130         }
131     }
132   else if (VECTORP (desc))
133     {
134       Lisp_Object gui_item = gui_parse_item_keywords (desc);
135       if (!button_item_to_widget_value (gui_item, wv, 1,
136                                         (menu_type == MENUBAR_TYPE
137                                          && depth <= 1)))
138         {
139           /* :included form was nil */
140           wv = NULL;
141           goto menu_item_done;
142         }
143     }
144   else if (CONSP (desc))
145     {
146       Lisp_Object incremental_data = desc;
147       widget_value *prev = 0;
148
149       if (STRINGP (XCAR (desc)))
150         {
151           Lisp_Object key, val;
152           Lisp_Object include_p = Qnil, hook_fn = Qnil, config_tag = Qnil;
153           Lisp_Object active_p = Qt;
154           Lisp_Object accel;
155           int included_spec = 0;
156           int active_spec = 0;
157           wv->type = CASCADE_TYPE;
158           wv->enabled = 1;
159           wv->name = (char *) XSTRING_DATA (LISP_GETTEXT (XCAR (desc)));
160
161           accel = gui_name_accelerator (LISP_GETTEXT (XCAR (desc)));
162           wv->accel = LISP_TO_VOID (accel);
163
164           desc = Fcdr (desc);
165
166           while (key = Fcar (desc), KEYWORDP (key))
167             {
168               Lisp_Object cascade = desc;
169               desc = Fcdr (desc);
170               if (NILP (desc))
171                 signal_simple_error ("Keyword in menu lacks a value",
172                                      cascade);
173               val = Fcar (desc);
174               desc = Fcdr (desc);
175               if (EQ (key, Q_included))
176                 include_p = val, included_spec = 1;
177               else if (EQ (key, Q_config))
178                 config_tag = val;
179               else if (EQ (key, Q_filter))
180                 hook_fn = val;
181               else if (EQ (key, Q_active))
182                 active_p = val, active_spec = 1;
183               else if (EQ (key, Q_accelerator))
184                 {
185                   if ( SYMBOLP (val)
186                        || CHARP (val))
187                     wv->accel = LISP_TO_VOID (val);
188                   else
189                     signal_simple_error ("bad keyboard accelerator", val);
190                 }
191               else if (EQ (key, Q_label))
192                 {
193                   /* implement in 21.2 */
194                 }
195               else
196                 signal_simple_error ("Unknown menu cascade keyword", cascade);
197             }
198
199           if ((!NILP (config_tag)
200                && NILP (Fmemq (config_tag, Vmenubar_configuration)))
201               || (included_spec && NILP (Feval (include_p))))
202             {
203               wv = NULL;
204               goto menu_item_done;
205             }
206
207           if (active_spec)
208             active_p = Feval (active_p);
209
210           if (!NILP (hook_fn) && !NILP (active_p))
211             {
212 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
213               if (filter_p || depth == 0)
214                 {
215 #endif
216                   desc = call1_trapping_errors ("Error in menubar filter",
217                                                 hook_fn, desc);
218                   if (UNBOUNDP (desc))
219                     desc = Qnil;
220 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
221                 }
222               else
223                 {
224                   widget_value *incr_wv = xmalloc_widget_value ();
225                   wv->contents = incr_wv;
226                   incr_wv->type = INCREMENTAL_TYPE;
227                   incr_wv->enabled = 1;
228                   incr_wv->name = wv->name;
229                   /* This is automatically GC protected through
230                      the call to lw_map_widget_values(); no need
231                      to worry. */
232                   incr_wv->call_data = LISP_TO_VOID (incremental_data);
233                   goto menu_item_done;
234                 }
235 #endif /* LWLIB_MENUBARS_LUCID || LWLIB_MENUBARS_MOTIF */
236             }
237           if (menu_type == POPUP_TYPE && popup_menu_titles && depth == 0)
238             {
239               /* Simply prepend three more widget values to the contents of
240                  the menu: a label, and two separators (to get a double
241                  line). */
242               widget_value *title_wv = xmalloc_widget_value ();
243               widget_value *sep_wv = xmalloc_widget_value ();
244               title_wv->type = TEXT_TYPE;
245               title_wv->name = wv->name;
246               title_wv->enabled = 1;
247               title_wv->next = sep_wv;
248               sep_wv->type = SEPARATOR_TYPE;
249               sep_wv->value = menu_separator_style ("==");
250               sep_wv->next = 0;
251
252               wv->contents = title_wv;
253               prev = sep_wv;
254             }
255           wv->enabled = ! NILP (active_p);
256           if (deep_p && !wv->enabled  && !NILP (desc))
257             {
258               widget_value *dummy;
259               /* Add a fake entry so the menus show up */
260               wv->contents = dummy = xmalloc_widget_value ();
261               dummy->name = "(inactive)";
262               dummy->accel = LISP_TO_VOID (Qnil);
263               dummy->enabled = 0;
264               dummy->selected = 0;
265               dummy->value = NULL;
266               dummy->type = BUTTON_TYPE;
267               dummy->call_data = NULL;
268               dummy->next = NULL;
269
270               goto menu_item_done;
271         }
272
273         }
274       else if (menubar_root_p)
275         {
276           wv->name = (char *) "menubar";
277           wv->type = CASCADE_TYPE; /* Well, nothing else seems to fit and
278                                       this is ignored anyway...  */
279         }
280       else
281         {
282           signal_simple_error ("Menu name (first element) must be a string",
283                                desc);
284         }
285
286       if (deep_p || menubar_root_p)
287         {
288           widget_value *next;
289           for (; !NILP (desc); desc = Fcdr (desc))
290             {
291               Lisp_Object child = Fcar (desc);
292               if (menubar_root_p && NILP (child))       /* the partition */
293                 {
294                   if (partition_seen)
295                     error (
296                      "More than one partition (nil) in menubar description");
297                   partition_seen = 1;
298                   next = xmalloc_widget_value ();
299                   next->type = PUSHRIGHT_TYPE;
300                 }
301               else
302                 {
303                   next = menu_item_descriptor_to_widget_value_1
304                     (child, menu_type, deep_p, filter_p, depth + 1);
305                 }
306               if (! next)
307                 continue;
308               else if (prev)
309                 prev->next = next;
310               else
311                 wv->contents = next;
312               prev = next;
313             }
314         }
315       if (deep_p && !wv->contents)
316         wv = NULL;
317     }
318   else if (NILP (desc))
319     error ("nil may not appear in menu descriptions");
320   else
321     signal_simple_error ("Unrecognized menu descriptor", desc);
322
323 menu_item_done:
324
325   if (wv)
326     {
327       /* Completed normally.  Clear out the object that widget_value_unwind()
328          will be called with to tell it not to free the wv (as we are
329          returning it.) */
330       set_opaque_ptr (wv_closure, 0);
331     }
332
333   unbind_to (count, Qnil);
334   return wv;
335 }
336
337 static widget_value *
338 menu_item_descriptor_to_widget_value (Lisp_Object desc,
339                                       int menu_type, /* if this is a menubar,
340                                                      popup or sub menu */
341                                       int deep_p,    /*  */
342                                       int filter_p)  /* if :filter forms
343                                                         should run now */
344 {
345   widget_value *wv;
346   int count = specpdl_depth ();
347   record_unwind_protect (restore_gc_inhibit,
348                          make_int (gc_currently_forbidden));
349   gc_currently_forbidden = 1;
350   /* Can't GC! */
351   wv = menu_item_descriptor_to_widget_value_1 (desc, menu_type, deep_p,
352                                                filter_p, 0);
353   unbind_to (count, Qnil);
354   return wv;
355 }
356
357
358 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
359 int in_menu_callback;
360
361 static Lisp_Object
362 restore_in_menu_callback (Lisp_Object val)
363 {
364     in_menu_callback = XINT(val);
365     return Qnil;
366 }
367 #endif /* LWLIB_MENUBARS_LUCID || LWLIB_MENUBARS_MOTIF */
368
369 #if 0
370 /* #### Sort of a hack needed to process Vactivate_menubar_hook
371    correctly wrt buffer-local values.  A correct solution would
372    involve adding a callback mechanism to run_hook().  This function
373    is currently unused.  */
374 static int
375 my_run_hook (Lisp_Object hooksym, int allow_global_p)
376 {
377   /* This function can GC */
378   Lisp_Object tail;
379   Lisp_Object value = Fsymbol_value (hooksym);
380   int changes = 0;
381
382   if (!NILP (value) && (!CONSP (value) || EQ (XCAR (value), Qlambda)))
383     return !EQ (call0 (value), Qt);
384
385   EXTERNAL_LIST_LOOP (tail, value)
386     {
387       if (allow_global_p && EQ (XCAR (tail), Qt))
388         changes |= my_run_hook (Fdefault_value (hooksym), 0);
389       if (!EQ (call0 (XCAR (tail)), Qt))
390         changes = 1;
391     }
392   return changes;
393 }
394 #endif
395
396
397 /* The order in which callbacks are run is funny to say the least.
398    It's sometimes tricky to avoid running a callback twice, and to
399    avoid returning prematurely.  So, this function returns true
400    if the menu's callbacks are no longer gc protected.  So long
401    as we unprotect them before allowing other callbacks to run,
402    everything should be ok.
403
404    The pre_activate_callback() *IS* intentionally called multiple times.
405    If client_data == NULL, then it's being called before the menu is posted.
406    If client_data != NULL, then client_data is a (widget_value *) and
407    client_data->data is a Lisp_Object pointing to a lisp submenu description
408    that must be converted into widget_values.  *client_data is destructively
409    modified.
410
411    #### Stig thinks that there may be a GC problem here due to the
412    fact that pre_activate_callback() is called multiple times, but I
413    think he's wrong.
414
415    */
416
417 static void
418 pre_activate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
419 {
420   /* This function can GC */
421   struct device *d = get_device_from_display (XtDisplay (widget));
422   struct frame *f = x_any_window_to_frame (d, XtWindow (widget));
423   Lisp_Object frame;
424   int count;
425
426   /* set in lwlib to the time stamp associated with the most recent menu
427      operation */
428   extern Time x_focus_timestamp_really_sucks_fix_me_better;
429
430   if (!f)
431     f = x_any_window_to_frame (d, XtWindow (XtParent (widget)));
432   if (!f)
433     return;
434
435   /* make sure f is the selected frame */
436   XSETFRAME (frame, f);
437   Fselect_frame (frame);
438
439   if (client_data)
440     {
441       /* this is an incremental menu construction callback */
442       widget_value *hack_wv = (widget_value *) client_data;
443       Lisp_Object submenu_desc;
444       widget_value *wv;
445
446       assert (hack_wv->type == INCREMENTAL_TYPE);
447       VOID_TO_LISP (submenu_desc, hack_wv->call_data);
448
449       /*
450        * #### Fix the menu code so this isn't necessary.
451        *
452        * Protect against reentering the menu code otherwise we will
453        * crash later when the code gets confused at the state
454        * changes.
455        */
456       count = specpdl_depth ();
457       record_unwind_protect (restore_in_menu_callback,
458                              make_int (in_menu_callback));
459       in_menu_callback = 1;
460       wv = menu_item_descriptor_to_widget_value (submenu_desc, SUBMENU_TYPE,
461                                                  1, 0);
462       unbind_to (count, Qnil);
463
464       if (!wv)
465         {
466           wv = xmalloc_widget_value ();
467           wv->type = CASCADE_TYPE;
468           wv->next = NULL;
469           wv->accel = LISP_TO_VOID (Qnil);
470           wv->contents = xmalloc_widget_value ();
471           wv->contents->type = TEXT_TYPE;
472           wv->contents->name = (char *) "No menu";
473           wv->contents->next = NULL;
474           wv->contents->accel = LISP_TO_VOID (Qnil);
475         }
476       assert (wv && wv->type == CASCADE_TYPE && wv->contents);
477       replace_widget_value_tree (hack_wv, wv->contents);
478       free_popup_widget_value_tree (wv);
479     }
480   else if (!POPUP_DATAP (FRAME_MENUBAR_DATA (f)))
481     return;
482   else
483     {
484 #if 0 /* Unused, see comment below. */
485       int any_changes;
486
487       /* #### - this menubar update mechanism is expensively anti-social and
488          the activate-menubar-hook is now mostly obsolete. */
489       any_changes = my_run_hook (Qactivate_menubar_hook, 1);
490
491       /* #### - It is necessary to *ALWAYS* call set_frame_menubar() now that
492          incremental menus are implemented.  If a subtree of a menu has been
493          updated incrementally (a destructive operation), then that subtree
494          must somehow be wiped.
495
496          It is difficult to undo the destructive operation in lwlib because
497          a pointer back to lisp data needs to be hidden away somewhere.  So
498          that an INCREMENTAL_TYPE widget_value can be recreated...  Hmmmmm. */
499       if (any_changes ||
500           !XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date)
501         set_frame_menubar (f, 1, 0);
502 #else
503       run_hook (Qactivate_menubar_hook);
504       set_frame_menubar (f, 1, 0);
505 #endif
506       DEVICE_X_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) =
507         DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) =
508         x_focus_timestamp_really_sucks_fix_me_better;
509     }
510 }
511
512 static widget_value *
513 compute_menubar_data (struct frame *f, Lisp_Object menubar, int deep_p)
514 {
515   widget_value *data;
516
517   if (NILP (menubar))
518     data = 0;
519   else
520     {
521       Lisp_Object old_buffer;
522       int count = specpdl_depth ();
523
524       old_buffer = Fcurrent_buffer ();
525       record_unwind_protect (Fset_buffer, old_buffer);
526       Fset_buffer ( XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer);
527       data = menu_item_descriptor_to_widget_value (menubar, MENUBAR_TYPE,
528                                                    deep_p, 0);
529       Fset_buffer (old_buffer);
530       unbind_to (count, Qnil);
531     }
532   return data;
533 }
534
535 static int
536 set_frame_menubar (struct frame *f, int deep_p, int first_time_p)
537 {
538   widget_value *data;
539   Lisp_Object menubar;
540   int menubar_visible;
541   long id;
542   /* As for the toolbar, the minibuffer does not have its own menubar. */
543   struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f));
544
545   if (! FRAME_X_P (f))
546     return 0;
547
548   /***** first compute the contents of the menubar *****/
549
550   if (! first_time_p)
551     {
552       /* evaluate `current-menubar' in the buffer of the selected window
553          of the frame in question. */
554       menubar = symbol_value_in_buffer (Qcurrent_menubar, w->buffer);
555     }
556   else
557     {
558       /* That's a little tricky the first time since the frame isn't
559          fully initialized yet. */
560       menubar = Fsymbol_value (Qcurrent_menubar);
561     }
562
563   if (NILP (menubar))
564     {
565       menubar = Vblank_menubar;
566       menubar_visible = 0;
567     }
568   else
569     menubar_visible = !NILP (w->menubar_visible_p);
570
571   data = compute_menubar_data (f, menubar, deep_p);
572   if (!data || (!data->next && !data->contents))
573     abort ();
574
575   if (NILP (FRAME_MENUBAR_DATA (f)))
576     {
577       struct popup_data *mdata =
578         alloc_lcrecord_type (struct popup_data, &lrecord_popup_data);
579
580       mdata->id = new_lwlib_id ();
581       mdata->last_menubar_buffer = Qnil;
582       mdata->menubar_contents_up_to_date = 0;
583       XSETPOPUP_DATA (FRAME_MENUBAR_DATA (f), mdata);
584     }
585
586   /***** now store into the menubar widget, creating it if necessary *****/
587
588   id = XFRAME_MENUBAR_DATA (f)->id;
589   if (!FRAME_X_MENUBAR_WIDGET (f))
590     {
591       Widget parent = FRAME_X_CONTAINER_WIDGET (f);
592
593       assert (first_time_p);
594
595       /* It's the first time we've mapped the menubar so compute its
596          contents completely once.  This makes sure that the menubar
597          components are created with the right type. */
598       if (!deep_p)
599         {
600           free_popup_widget_value_tree (data);
601           data = compute_menubar_data (f, menubar, 1);
602         }
603
604
605       FRAME_X_MENUBAR_WIDGET (f) =
606         lw_create_widget ("menubar", "menubar", id, data, parent,
607                           0, pre_activate_callback,
608                           popup_selection_callback, 0);
609
610     }
611   else
612     {
613       lw_modify_all_widgets (id, data, deep_p ? True : False);
614     }
615   free_popup_widget_value_tree (data);
616
617   XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date = deep_p;
618   XFRAME_MENUBAR_DATA (f)->last_menubar_buffer =
619     XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer;
620   return menubar_visible;
621 }
622
623
624 /* Called from x_create_widgets() to create the initial menubar of a frame
625    before it is mapped, so that the window is mapped with the menubar already
626    there instead of us tacking it on later and thrashing the window after it
627    is visible. */
628 int
629 x_initialize_frame_menubar (struct frame *f)
630 {
631   return set_frame_menubar (f, 1, 1);
632 }
633
634
635 static LWLIB_ID last_popup_menu_selection_callback_id;
636
637 static void
638 popup_menu_selection_callback (Widget widget, LWLIB_ID id,
639                                XtPointer client_data)
640 {
641   last_popup_menu_selection_callback_id = id;
642   popup_selection_callback (widget, id, client_data);
643   /* lw_destroy_all_widgets() will be called from popup_down_callback() */
644 }
645
646 static void
647 popup_menu_down_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
648 {
649   if (popup_handled_p (id))
650     return;
651   assert (popup_up_p != 0);
652   ungcpro_popup_callbacks (id);
653   popup_up_p--;
654   /* if this isn't called immediately after the selection callback, then
655      there wasn't a menu selection. */
656   if (id != last_popup_menu_selection_callback_id)
657     popup_selection_callback (widget, id, (XtPointer) -1);
658   lw_destroy_all_widgets (id);
659 }
660
661 \f
662 static void
663 make_dummy_xbutton_event (XEvent *dummy,
664                           Widget daddy,
665                           struct Lisp_Event *eev)
666      /* NULL for eev means query pointer */
667 {
668   XButtonPressedEvent *btn = (XButtonPressedEvent *) dummy;
669
670   btn->type = ButtonPress;
671   btn->serial = 0;
672   btn->send_event = 0;
673   btn->display = XtDisplay (daddy);
674   btn->window = XtWindow (daddy);
675   if (eev)
676     {
677       Position shellx, shelly, framex, framey;
678       Arg al [2];
679       btn->time = eev->timestamp;
680       btn->button = eev->event.button.button;
681       btn->root = RootWindowOfScreen (XtScreen (daddy));
682       btn->subwindow = (Window) NULL;
683       btn->x = eev->event.button.x;
684       btn->y = eev->event.button.y;
685       shellx = shelly = 0;
686 #ifndef HAVE_WMCOMMAND
687       {
688         Widget shell = XtParent (daddy);
689
690         XtSetArg (al [0], XtNx, &shellx);
691         XtSetArg (al [1], XtNy, &shelly);
692         XtGetValues (shell, al, 2);
693       }
694 #endif      
695       XtSetArg (al [0], XtNx, &framex);
696       XtSetArg (al [1], XtNy, &framey);
697       XtGetValues (daddy, al, 2);
698       btn->x_root = shellx + framex + btn->x;
699       btn->y_root = shelly + framey + btn->y;
700       btn->state = ButtonPressMask; /* all buttons pressed */
701     }
702   else
703     {
704       /* CurrentTime is just ZERO, so it's worthless for
705          determining relative click times. */
706       struct device *d = get_device_from_display (XtDisplay (daddy));
707       btn->time = DEVICE_X_MOUSE_TIMESTAMP (d); /* event-Xt maintains this */
708       btn->button = 0;
709       XQueryPointer (btn->display, btn->window, &btn->root,
710                      &btn->subwindow, &btn->x_root, &btn->y_root,
711                      &btn->x, &btn->y, &btn->state);
712     }
713 }
714
715 \f
716
717 static void
718 x_update_frame_menubar_internal (struct frame *f)
719 {
720   /* We assume the menubar contents has changed if the global flag is set,
721      or if the current buffer has changed, or if the menubar has never
722      been updated before.
723    */
724   int menubar_contents_changed =
725     (f->menubar_changed
726      || NILP (FRAME_MENUBAR_DATA (f))
727      || (!EQ (XFRAME_MENUBAR_DATA (f)->last_menubar_buffer,
728               XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer)));
729
730   Boolean menubar_was_visible = XtIsManaged (FRAME_X_MENUBAR_WIDGET (f));
731   Boolean menubar_will_be_visible = menubar_was_visible;
732   Boolean menubar_visibility_changed;
733
734   if (menubar_contents_changed)
735     menubar_will_be_visible = set_frame_menubar (f, 0, 0);
736
737   menubar_visibility_changed = menubar_was_visible != menubar_will_be_visible;
738
739   if (!menubar_visibility_changed)
740     return;
741
742   /* Set menubar visibility */
743   (menubar_will_be_visible ? XtManageChild : XtUnmanageChild)
744     (FRAME_X_MENUBAR_WIDGET (f));
745
746   MARK_FRAME_SIZE_SLIPPED (f);
747 }
748
749 static void
750 x_update_frame_menubars (struct frame *f)
751 {
752   assert (FRAME_X_P (f));
753
754   x_update_frame_menubar_internal (f);
755
756   /* #### This isn't going to work right now that this function works on
757      a per-frame, not per-device basis.  Guess what?  I don't care. */
758 }
759
760 static void
761 x_free_frame_menubars (struct frame *f)
762 {
763   Widget menubar_widget;
764
765   assert (FRAME_X_P (f));
766
767   menubar_widget = FRAME_X_MENUBAR_WIDGET (f);
768   if (menubar_widget)
769     {
770       LWLIB_ID id = XFRAME_MENUBAR_DATA (f)->id;
771       lw_destroy_all_widgets (id);
772       XFRAME_MENUBAR_DATA (f)->id = 0;
773     }
774 }
775
776 static void
777 x_popup_menu (Lisp_Object menu_desc, Lisp_Object event)
778 {
779   int menu_id;
780   struct frame *f = selected_frame ();
781   widget_value *data;
782   Widget parent;
783   Widget menu;
784   struct Lisp_Event *eev = NULL;
785   XEvent xev;
786   Lisp_Object frame;
787
788   XSETFRAME (frame, f);
789   CHECK_X_FRAME (frame);
790   parent = FRAME_X_SHELL_WIDGET (f);
791
792   if (!NILP (event))
793     {
794       CHECK_LIVE_EVENT (event);
795       eev= XEVENT (event);
796       if (eev->event_type != button_press_event
797           && eev->event_type != button_release_event)
798         wrong_type_argument (Qmouse_event_p, event);
799     }
800   else if (!NILP (Vthis_command_keys))
801     {
802       /* if an event wasn't passed, use the last event of the event sequence
803          currently being executed, if that event is a mouse event */
804       eev = XEVENT (Vthis_command_keys); /* last event first */
805       if (eev->event_type != button_press_event
806           && eev->event_type != button_release_event)
807         eev = NULL;
808     }
809   make_dummy_xbutton_event (&xev, parent, eev);
810
811   if (SYMBOLP (menu_desc))
812     menu_desc = Fsymbol_value (menu_desc);
813   CHECK_CONS (menu_desc);
814   CHECK_STRING (XCAR (menu_desc));
815   data = menu_item_descriptor_to_widget_value (menu_desc, POPUP_TYPE, 1, 1);
816
817   if (! data) error ("no menu");
818
819   menu_id = new_lwlib_id ();
820   menu = lw_create_widget ("popup", "popup" /* data->name */, menu_id, data,
821                            parent, 1, 0,
822                            popup_menu_selection_callback,
823                            popup_menu_down_callback);
824   free_popup_widget_value_tree (data);
825
826   gcpro_popup_callbacks (menu_id);
827
828   /* Setting zmacs-region-stays is necessary here because executing a command
829      from a menu is really a two-command process: the first command (bound to
830      the button-click) simply pops up the menu, and returns.  This causes a
831      sequence of magic-events (destined for the popup-menu widget) to begin.
832      Eventually, a menu item is selected, and a menu-event blip is pushed onto
833      the end of the input stream, which is then executed by the event loop.
834
835      So there are two command-events, with a bunch of magic-events between
836      them.  We don't want the *first* command event to alter the state of the
837      region, so that the region can be available as an argument for the second
838      command.
839    */
840   if (zmacs_regions)
841     zmacs_region_stays = 1;
842
843   popup_up_p++;
844   lw_popup_menu (menu, &xev);
845   /* this speeds up display of pop-up menus */
846   XFlush (XtDisplay (parent));
847 }
848
849 \f
850 void
851 syms_of_menubar_x (void)
852 {
853 }
854
855 void
856 console_type_create_menubar_x (void)
857 {
858   CONSOLE_HAS_METHOD (x, update_frame_menubars);
859   CONSOLE_HAS_METHOD (x, free_frame_menubars);
860   CONSOLE_HAS_METHOD (x, popup_menu);
861 }
862
863 void
864 reinit_vars_of_menubar_x (void)
865 {
866   last_popup_menu_selection_callback_id = (LWLIB_ID) -1;
867 }
868
869 void
870 vars_of_menubar_x (void)
871 {
872   reinit_vars_of_menubar_x ();
873
874 #if defined (LWLIB_MENUBARS_LUCID)
875   Fprovide (intern ("lucid-menubars"));
876 #elif defined (LWLIB_MENUBARS_MOTIF)
877   Fprovide (intern ("motif-menubars"));
878 #elif defined (LWLIB_MENUBARS_ATHENA)
879   Fprovide (intern ("athena-menubars"));
880 #endif
881 }