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