XEmacs 21.2.26 "Millenium".
[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 = xstrdup (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           wv->name = xstrdup (wv->name);
161
162           accel = gui_name_accelerator (LISP_GETTEXT (XCAR (desc)));
163           wv->accel = LISP_TO_VOID (accel);
164
165           desc = Fcdr (desc);
166
167           while (key = Fcar (desc), KEYWORDP (key))
168             {
169               Lisp_Object cascade = desc;
170               desc = Fcdr (desc);
171               if (NILP (desc))
172                 signal_simple_error ("Keyword in menu lacks a value",
173                                      cascade);
174               val = Fcar (desc);
175               desc = Fcdr (desc);
176               if (EQ (key, Q_included))
177                 include_p = val, included_spec = 1;
178               else if (EQ (key, Q_config))
179                 config_tag = val;
180               else if (EQ (key, Q_filter))
181                 hook_fn = val;
182               else if (EQ (key, Q_active))
183                 active_p = val, active_spec = 1;
184               else if (EQ (key, Q_accelerator))
185                 {
186                   if ( SYMBOLP (val)
187                        || CHARP (val))
188                     wv->accel = LISP_TO_VOID (val);
189                   else
190                     signal_simple_error ("bad keyboard accelerator", val);
191                 }
192               else if (EQ (key, Q_label))
193                 {
194                   /* implement in 21.2 */
195                 }
196               else
197                 signal_simple_error ("Unknown menu cascade keyword", cascade);
198             }
199
200           if ((!NILP (config_tag)
201                && NILP (Fmemq (config_tag, Vmenubar_configuration)))
202               || (included_spec && NILP (Feval (include_p))))
203             {
204               wv = NULL;
205               goto menu_item_done;
206             }
207
208           if (active_spec)
209             active_p = Feval (active_p);
210
211           if (!NILP (hook_fn) && !NILP (active_p))
212             {
213 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
214               if (filter_p || depth == 0)
215                 {
216 #endif
217                   desc = call1_trapping_errors ("Error in menubar filter",
218                                                 hook_fn, desc);
219                   if (UNBOUNDP (desc))
220                     desc = Qnil;
221 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
222                 }
223               else
224                 {
225                   widget_value *incr_wv = xmalloc_widget_value ();
226                   wv->contents = incr_wv;
227                   incr_wv->type = INCREMENTAL_TYPE;
228                   incr_wv->enabled = 1;
229                   incr_wv->name = wv->name;
230                   incr_wv->name = xstrdup (wv->name);
231                   /* This is automatically GC protected through
232                      the call to lw_map_widget_values(); no need
233                      to worry. */
234                   incr_wv->call_data = LISP_TO_VOID (incremental_data);
235                   goto menu_item_done;
236                 }
237 #endif /* LWLIB_MENUBARS_LUCID || LWLIB_MENUBARS_MOTIF */
238             }
239           if (menu_type == POPUP_TYPE && popup_menu_titles && depth == 0)
240             {
241               /* Simply prepend three more widget values to the contents of
242                  the menu: a label, and two separators (to get a double
243                  line). */
244               widget_value *title_wv = xmalloc_widget_value ();
245               widget_value *sep_wv = xmalloc_widget_value ();
246               title_wv->type = TEXT_TYPE;
247               title_wv->name = xstrdup (wv->name);
248               title_wv->enabled = 1;
249               title_wv->next = sep_wv;
250               sep_wv->type = SEPARATOR_TYPE;
251               sep_wv->value = menu_separator_style ("==");
252               sep_wv->next = 0;
253
254               wv->contents = title_wv;
255               prev = sep_wv;
256             }
257           wv->enabled = ! NILP (active_p);
258           if (deep_p && !wv->enabled  && !NILP (desc))
259             {
260               widget_value *dummy;
261               /* Add a fake entry so the menus show up */
262               wv->contents = dummy = xmalloc_widget_value ();
263               dummy->name = xstrdup ("(inactive)");
264               dummy->accel = LISP_TO_VOID (Qnil);
265               dummy->enabled = 0;
266               dummy->selected = 0;
267               dummy->value = NULL;
268               dummy->type = BUTTON_TYPE;
269               dummy->call_data = NULL;
270               dummy->next = NULL;
271
272               goto menu_item_done;
273         }
274
275         }
276       else if (menubar_root_p)
277         {
278           wv->name = xstrdup ("menubar");
279           wv->type = CASCADE_TYPE; /* Well, nothing else seems to fit and
280                                       this is ignored anyway...  */
281         }
282       else
283         {
284           signal_simple_error ("Menu name (first element) must be a string",
285                                desc);
286         }
287
288       if (deep_p || menubar_root_p)
289         {
290           widget_value *next;
291           for (; !NILP (desc); desc = Fcdr (desc))
292             {
293               Lisp_Object child = Fcar (desc);
294               if (menubar_root_p && NILP (child))       /* the partition */
295                 {
296                   if (partition_seen)
297                     error (
298                      "More than one partition (nil) in menubar description");
299                   partition_seen = 1;
300                   next = xmalloc_widget_value ();
301                   next->type = PUSHRIGHT_TYPE;
302                 }
303               else
304                 {
305                   next = menu_item_descriptor_to_widget_value_1
306                     (child, menu_type, deep_p, filter_p, depth + 1);
307                 }
308               if (! next)
309                 continue;
310               else if (prev)
311                 prev->next = next;
312               else
313                 wv->contents = next;
314               prev = next;
315             }
316         }
317       if (deep_p && !wv->contents)
318         wv = NULL;
319     }
320   else if (NILP (desc))
321     error ("nil may not appear in menu descriptions");
322   else
323     signal_simple_error ("Unrecognized menu descriptor", desc);
324
325 menu_item_done:
326
327   if (wv)
328     {
329       /* Completed normally.  Clear out the object that widget_value_unwind()
330          will be called with to tell it not to free the wv (as we are
331          returning it.) */
332       set_opaque_ptr (wv_closure, 0);
333     }
334
335   unbind_to (count, Qnil);
336   return wv;
337 }
338
339 static widget_value *
340 menu_item_descriptor_to_widget_value (Lisp_Object desc,
341                                       int menu_type, /* if this is a menubar,
342                                                      popup or sub menu */
343                                       int deep_p,    /*  */
344                                       int filter_p)  /* if :filter forms
345                                                         should run now */
346 {
347   widget_value *wv;
348   int count = specpdl_depth ();
349   record_unwind_protect (restore_gc_inhibit,
350                          make_int (gc_currently_forbidden));
351   gc_currently_forbidden = 1;
352   /* Can't GC! */
353   wv = menu_item_descriptor_to_widget_value_1 (desc, menu_type, deep_p,
354                                                filter_p, 0);
355   unbind_to (count, Qnil);
356   return wv;
357 }
358
359
360 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
361 int in_menu_callback;
362
363 static Lisp_Object
364 restore_in_menu_callback (Lisp_Object val)
365 {
366     in_menu_callback = XINT(val);
367     return Qnil;
368 }
369 #endif /* LWLIB_MENUBARS_LUCID || LWLIB_MENUBARS_MOTIF */
370
371 #if 0
372 /* #### Sort of a hack needed to process Vactivate_menubar_hook
373    correctly wrt buffer-local values.  A correct solution would
374    involve adding a callback mechanism to run_hook().  This function
375    is currently unused.  */
376 static int
377 my_run_hook (Lisp_Object hooksym, int allow_global_p)
378 {
379   /* This function can GC */
380   Lisp_Object tail;
381   Lisp_Object value = Fsymbol_value (hooksym);
382   int changes = 0;
383
384   if (!NILP (value) && (!CONSP (value) || EQ (XCAR (value), Qlambda)))
385     return !EQ (call0 (value), Qt);
386
387   EXTERNAL_LIST_LOOP (tail, value)
388     {
389       if (allow_global_p && EQ (XCAR (tail), Qt))
390         changes |= my_run_hook (Fdefault_value (hooksym), 0);
391       if (!EQ (call0 (XCAR (tail)), Qt))
392         changes = 1;
393     }
394   return changes;
395 }
396 #endif
397
398
399 /* The order in which callbacks are run is funny to say the least.
400    It's sometimes tricky to avoid running a callback twice, and to
401    avoid returning prematurely.  So, this function returns true
402    if the menu's callbacks are no longer gc protected.  So long
403    as we unprotect them before allowing other callbacks to run,
404    everything should be ok.
405
406    The pre_activate_callback() *IS* intentionally called multiple times.
407    If client_data == NULL, then it's being called before the menu is posted.
408    If client_data != NULL, then client_data is a (widget_value *) and
409    client_data->data is a Lisp_Object pointing to a lisp submenu description
410    that must be converted into widget_values.  *client_data is destructively
411    modified.
412
413    #### Stig thinks that there may be a GC problem here due to the
414    fact that pre_activate_callback() is called multiple times, but I
415    think he's wrong.
416
417    */
418
419 static void
420 pre_activate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
421 {
422   /* This function can GC */
423   struct device *d = get_device_from_display (XtDisplay (widget));
424   struct frame *f = x_any_window_to_frame (d, XtWindow (widget));
425   Lisp_Object frame;
426   int count;
427
428   /* set in lwlib to the time stamp associated with the most recent menu
429      operation */
430   extern Time x_focus_timestamp_really_sucks_fix_me_better;
431
432   if (!f)
433     f = x_any_window_to_frame (d, XtWindow (XtParent (widget)));
434   if (!f)
435     return;
436
437   /* make sure f is the selected frame */
438   XSETFRAME (frame, f);
439   Fselect_frame (frame);
440
441   if (client_data)
442     {
443       /* this is an incremental menu construction callback */
444       widget_value *hack_wv = (widget_value *) client_data;
445       Lisp_Object submenu_desc;
446       widget_value *wv;
447
448       assert (hack_wv->type == INCREMENTAL_TYPE);
449       VOID_TO_LISP (submenu_desc, hack_wv->call_data);
450
451       /*
452        * #### Fix the menu code so this isn't necessary.
453        *
454        * Protect against reentering the menu code otherwise we will
455        * crash later when the code gets confused at the state
456        * changes.
457        */
458       count = specpdl_depth ();
459       record_unwind_protect (restore_in_menu_callback,
460                              make_int (in_menu_callback));
461       in_menu_callback = 1;
462       wv = menu_item_descriptor_to_widget_value (submenu_desc, SUBMENU_TYPE,
463                                                  1, 0);
464       unbind_to (count, Qnil);
465
466       if (!wv)
467         {
468           wv = xmalloc_widget_value ();
469           wv->type = CASCADE_TYPE;
470           wv->next = NULL;
471           wv->accel = LISP_TO_VOID (Qnil);
472           wv->contents = xmalloc_widget_value ();
473           wv->contents->type = TEXT_TYPE;
474           wv->contents->name = xstrdup ("No menu");
475           wv->contents->next = NULL;
476           wv->contents->accel = LISP_TO_VOID (Qnil);
477         }
478       assert (wv && wv->type == CASCADE_TYPE && wv->contents);
479       replace_widget_value_tree (hack_wv, wv->contents);
480       free_popup_widget_value_tree (wv);
481     }
482   else if (!POPUP_DATAP (FRAME_MENUBAR_DATA (f)))
483     return;
484   else
485     {
486 #if 0 /* Unused, see comment below. */
487       int any_changes;
488
489       /* #### - this menubar update mechanism is expensively anti-social and
490          the activate-menubar-hook is now mostly obsolete. */
491       any_changes = my_run_hook (Qactivate_menubar_hook, 1);
492
493       /* #### - It is necessary to *ALWAYS* call set_frame_menubar() now that
494          incremental menus are implemented.  If a subtree of a menu has been
495          updated incrementally (a destructive operation), then that subtree
496          must somehow be wiped.
497
498          It is difficult to undo the destructive operation in lwlib because
499          a pointer back to lisp data needs to be hidden away somewhere.  So
500          that an INCREMENTAL_TYPE widget_value can be recreated...  Hmmmmm. */
501       if (any_changes ||
502           !XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date)
503         set_frame_menubar (f, 1, 0);
504 #else
505       run_hook (Qactivate_menubar_hook);
506       set_frame_menubar (f, 1, 0);
507 #endif
508       DEVICE_X_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) =
509         DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) =
510         x_focus_timestamp_really_sucks_fix_me_better;
511     }
512 }
513
514 static widget_value *
515 compute_menubar_data (struct frame *f, Lisp_Object menubar, int deep_p)
516 {
517   widget_value *data;
518
519   if (NILP (menubar))
520     data = 0;
521   else
522     {
523       Lisp_Object old_buffer;
524       int count = specpdl_depth ();
525
526       old_buffer = Fcurrent_buffer ();
527       record_unwind_protect (Fset_buffer, old_buffer);
528       Fset_buffer ( XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer);
529       data = menu_item_descriptor_to_widget_value (menubar, MENUBAR_TYPE,
530                                                    deep_p, 0);
531       Fset_buffer (old_buffer);
532       unbind_to (count, Qnil);
533     }
534   return data;
535 }
536
537 static int
538 set_frame_menubar (struct frame *f, int deep_p, int first_time_p)
539 {
540   widget_value *data;
541   Lisp_Object menubar;
542   int menubar_visible;
543   long id;
544   /* As for the toolbar, the minibuffer does not have its own menubar. */
545   struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f));
546
547   if (! FRAME_X_P (f))
548     return 0;
549
550   /***** first compute the contents of the menubar *****/
551
552   if (! first_time_p)
553     {
554       /* evaluate `current-menubar' in the buffer of the selected window
555          of the frame in question. */
556       menubar = symbol_value_in_buffer (Qcurrent_menubar, w->buffer);
557     }
558   else
559     {
560       /* That's a little tricky the first time since the frame isn't
561          fully initialized yet. */
562       menubar = Fsymbol_value (Qcurrent_menubar);
563     }
564
565   if (NILP (menubar))
566     {
567       menubar = Vblank_menubar;
568       menubar_visible = 0;
569     }
570   else
571     menubar_visible = !NILP (w->menubar_visible_p);
572
573   data = compute_menubar_data (f, menubar, deep_p);
574   if (!data || (!data->next && !data->contents))
575     abort ();
576
577   if (NILP (FRAME_MENUBAR_DATA (f)))
578     {
579       struct popup_data *mdata =
580         alloc_lcrecord_type (struct popup_data, &lrecord_popup_data);
581
582       mdata->id = new_lwlib_id ();
583       mdata->last_menubar_buffer = Qnil;
584       mdata->menubar_contents_up_to_date = 0;
585       XSETPOPUP_DATA (FRAME_MENUBAR_DATA (f), mdata);
586     }
587
588   /***** now store into the menubar widget, creating it if necessary *****/
589
590   id = XFRAME_MENUBAR_DATA (f)->id;
591   if (!FRAME_X_MENUBAR_WIDGET (f))
592     {
593       Widget parent = FRAME_X_CONTAINER_WIDGET (f);
594
595       assert (first_time_p);
596
597       /* It's the first time we've mapped the menubar so compute its
598          contents completely once.  This makes sure that the menubar
599          components are created with the right type. */
600       if (!deep_p)
601         {
602           free_popup_widget_value_tree (data);
603           data = compute_menubar_data (f, menubar, 1);
604         }
605
606
607       FRAME_X_MENUBAR_WIDGET (f) =
608         lw_create_widget ("menubar", "menubar", id, data, parent,
609                           0, pre_activate_callback,
610                           popup_selection_callback, 0);
611
612     }
613   else
614     {
615       lw_modify_all_widgets (id, data, deep_p ? True : False);
616     }
617   free_popup_widget_value_tree (data);
618
619   XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date = deep_p;
620   XFRAME_MENUBAR_DATA (f)->last_menubar_buffer =
621     XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer;
622   return menubar_visible;
623 }
624
625
626 /* Called from x_create_widgets() to create the initial menubar of a frame
627    before it is mapped, so that the window is mapped with the menubar already
628    there instead of us tacking it on later and thrashing the window after it
629    is visible. */
630 int
631 x_initialize_frame_menubar (struct frame *f)
632 {
633   return set_frame_menubar (f, 1, 1);
634 }
635
636
637 static LWLIB_ID last_popup_menu_selection_callback_id;
638
639 static void
640 popup_menu_selection_callback (Widget widget, LWLIB_ID id,
641                                XtPointer client_data)
642 {
643   last_popup_menu_selection_callback_id = id;
644   popup_selection_callback (widget, id, client_data);
645   /* lw_destroy_all_widgets() will be called from popup_down_callback() */
646 }
647
648 static void
649 popup_menu_down_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
650 {
651   if (popup_handled_p (id))
652     return;
653   assert (popup_up_p != 0);
654   ungcpro_popup_callbacks (id);
655   popup_up_p--;
656   /* if this isn't called immediately after the selection callback, then
657      there wasn't a menu selection. */
658   if (id != last_popup_menu_selection_callback_id)
659     popup_selection_callback (widget, id, (XtPointer) -1);
660   lw_destroy_all_widgets (id);
661 }
662
663 \f
664 static void
665 make_dummy_xbutton_event (XEvent *dummy,
666                           Widget daddy,
667                           struct Lisp_Event *eev)
668      /* NULL for eev means query pointer */
669 {
670   XButtonPressedEvent *btn = (XButtonPressedEvent *) dummy;
671
672   btn->type = ButtonPress;
673   btn->serial = 0;
674   btn->send_event = 0;
675   btn->display = XtDisplay (daddy);
676   btn->window = XtWindow (daddy);
677   if (eev)
678     {
679       Position shellx, shelly, framex, framey;
680       Arg al [2];
681       btn->time = eev->timestamp;
682       btn->button = eev->event.button.button;
683       btn->root = RootWindowOfScreen (XtScreen (daddy));
684       btn->subwindow = (Window) NULL;
685       btn->x = eev->event.button.x;
686       btn->y = eev->event.button.y;
687       shellx = shelly = 0;
688 #ifndef HAVE_WMCOMMAND
689       {
690         Widget shell = XtParent (daddy);
691
692         XtSetArg (al [0], XtNx, &shellx);
693         XtSetArg (al [1], XtNy, &shelly);
694         XtGetValues (shell, al, 2);
695       }
696 #endif      
697       XtSetArg (al [0], XtNx, &framex);
698       XtSetArg (al [1], XtNy, &framey);
699       XtGetValues (daddy, al, 2);
700       btn->x_root = shellx + framex + btn->x;
701       btn->y_root = shelly + framey + btn->y;
702       btn->state = ButtonPressMask; /* all buttons pressed */
703     }
704   else
705     {
706       /* CurrentTime is just ZERO, so it's worthless for
707          determining relative click times. */
708       struct device *d = get_device_from_display (XtDisplay (daddy));
709       btn->time = DEVICE_X_MOUSE_TIMESTAMP (d); /* event-Xt maintains this */
710       btn->button = 0;
711       XQueryPointer (btn->display, btn->window, &btn->root,
712                      &btn->subwindow, &btn->x_root, &btn->y_root,
713                      &btn->x, &btn->y, &btn->state);
714     }
715 }
716
717 \f
718
719 static void
720 x_update_frame_menubar_internal (struct frame *f)
721 {
722   /* We assume the menubar contents has changed if the global flag is set,
723      or if the current buffer has changed, or if the menubar has never
724      been updated before.
725    */
726   int menubar_contents_changed =
727     (f->menubar_changed
728      || NILP (FRAME_MENUBAR_DATA (f))
729      || (!EQ (XFRAME_MENUBAR_DATA (f)->last_menubar_buffer,
730               XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer)));
731
732   Boolean menubar_was_visible = XtIsManaged (FRAME_X_MENUBAR_WIDGET (f));
733   Boolean menubar_will_be_visible = menubar_was_visible;
734   Boolean menubar_visibility_changed;
735
736   if (menubar_contents_changed)
737     menubar_will_be_visible = set_frame_menubar (f, 0, 0);
738
739   menubar_visibility_changed = menubar_was_visible != menubar_will_be_visible;
740
741   if (!menubar_visibility_changed)
742     return;
743
744   /* Set menubar visibility */
745   (menubar_will_be_visible ? XtManageChild : XtUnmanageChild)
746     (FRAME_X_MENUBAR_WIDGET (f));
747
748   MARK_FRAME_SIZE_SLIPPED (f);
749 }
750
751 static void
752 x_update_frame_menubars (struct frame *f)
753 {
754   assert (FRAME_X_P (f));
755
756   x_update_frame_menubar_internal (f);
757
758   /* #### This isn't going to work right now that this function works on
759      a per-frame, not per-device basis.  Guess what?  I don't care. */
760 }
761
762 static void
763 x_free_frame_menubars (struct frame *f)
764 {
765   Widget menubar_widget;
766
767   assert (FRAME_X_P (f));
768
769   menubar_widget = FRAME_X_MENUBAR_WIDGET (f);
770   if (menubar_widget)
771     {
772       LWLIB_ID id = XFRAME_MENUBAR_DATA (f)->id;
773       lw_destroy_all_widgets (id);
774       XFRAME_MENUBAR_DATA (f)->id = 0;
775     }
776 }
777
778 static void
779 x_popup_menu (Lisp_Object menu_desc, Lisp_Object event)
780 {
781   int menu_id;
782   struct frame *f = selected_frame ();
783   widget_value *data;
784   Widget parent;
785   Widget menu;
786   struct Lisp_Event *eev = NULL;
787   XEvent xev;
788   Lisp_Object frame;
789
790   XSETFRAME (frame, f);
791   CHECK_X_FRAME (frame);
792   parent = FRAME_X_SHELL_WIDGET (f);
793
794   if (!NILP (event))
795     {
796       CHECK_LIVE_EVENT (event);
797       eev= XEVENT (event);
798       if (eev->event_type != button_press_event
799           && eev->event_type != button_release_event)
800         wrong_type_argument (Qmouse_event_p, event);
801     }
802   else if (!NILP (Vthis_command_keys))
803     {
804       /* if an event wasn't passed, use the last event of the event sequence
805          currently being executed, if that event is a mouse event */
806       eev = XEVENT (Vthis_command_keys); /* last event first */
807       if (eev->event_type != button_press_event
808           && eev->event_type != button_release_event)
809         eev = NULL;
810     }
811   make_dummy_xbutton_event (&xev, parent, eev);
812
813   if (SYMBOLP (menu_desc))
814     menu_desc = Fsymbol_value (menu_desc);
815   CHECK_CONS (menu_desc);
816   CHECK_STRING (XCAR (menu_desc));
817   data = menu_item_descriptor_to_widget_value (menu_desc, POPUP_TYPE, 1, 1);
818
819   if (! data) error ("no menu");
820
821   menu_id = new_lwlib_id ();
822   menu = lw_create_widget ("popup", "popup" /* data->name */, menu_id, data,
823                            parent, 1, 0,
824                            popup_menu_selection_callback,
825                            popup_menu_down_callback);
826   free_popup_widget_value_tree (data);
827
828   gcpro_popup_callbacks (menu_id);
829
830   /* Setting zmacs-region-stays is necessary here because executing a command
831      from a menu is really a two-command process: the first command (bound to
832      the button-click) simply pops up the menu, and returns.  This causes a
833      sequence of magic-events (destined for the popup-menu widget) to begin.
834      Eventually, a menu item is selected, and a menu-event blip is pushed onto
835      the end of the input stream, which is then executed by the event loop.
836
837      So there are two command-events, with a bunch of magic-events between
838      them.  We don't want the *first* command event to alter the state of the
839      region, so that the region can be available as an argument for the second
840      command.
841    */
842   if (zmacs_regions)
843     zmacs_region_stays = 1;
844
845   popup_up_p++;
846   lw_popup_menu (menu, &xev);
847   /* this speeds up display of pop-up menus */
848   XFlush (XtDisplay (parent));
849 }
850
851 \f
852 void
853 syms_of_menubar_x (void)
854 {
855 }
856
857 void
858 console_type_create_menubar_x (void)
859 {
860   CONSOLE_HAS_METHOD (x, update_frame_menubars);
861   CONSOLE_HAS_METHOD (x, free_frame_menubars);
862   CONSOLE_HAS_METHOD (x, popup_menu);
863 }
864
865 void
866 reinit_vars_of_menubar_x (void)
867 {
868   last_popup_menu_selection_callback_id = (LWLIB_ID) -1;
869 }
870
871 void
872 vars_of_menubar_x (void)
873 {
874   reinit_vars_of_menubar_x ();
875
876 #if defined (LWLIB_MENUBARS_LUCID)
877   Fprovide (intern ("lucid-menubars"));
878 #elif defined (LWLIB_MENUBARS_MOTIF)
879   Fprovide (intern ("motif-menubars"));
880 #elif defined (LWLIB_MENUBARS_ATHENA)
881   Fprovide (intern ("athena-menubars"));
882 #endif
883 }