Contents in 1999-06-04-13 of release-21-2.
[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->accel = LISP_TO_VOID (Qnil);
469           wv->contents = xmalloc_widget_value ();
470           wv->contents->type = TEXT_TYPE;
471           wv->contents->name = (char *) "No menu";
472           wv->contents->next = NULL;
473           wv->contents->accel = LISP_TO_VOID (Qnil);
474         }
475       assert (wv && wv->type == CASCADE_TYPE && wv->contents);
476       replace_widget_value_tree (hack_wv, wv->contents);
477       free_popup_widget_value_tree (wv);
478     }
479   else if (!POPUP_DATAP (FRAME_MENUBAR_DATA (f)))
480     return;
481   else
482     {
483 #if 0 /* Unused, see comment below. */
484       int any_changes;
485
486       /* #### - this menubar update mechanism is expensively anti-social and
487          the activate-menubar-hook is now mostly obsolete. */
488       any_changes = my_run_hook (Qactivate_menubar_hook, 1);
489
490       /* #### - It is necessary to *ALWAYS* call set_frame_menubar() now that
491          incremental menus are implemented.  If a subtree of a menu has been
492          updated incrementally (a destructive operation), then that subtree
493          must somehow be wiped.
494
495          It is difficult to undo the destructive operation in lwlib because
496          a pointer back to lisp data needs to be hidden away somewhere.  So
497          that an INCREMENTAL_TYPE widget_value can be recreated...  Hmmmmm. */
498       if (any_changes ||
499           !XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date)
500         set_frame_menubar (f, 1, 0);
501 #else
502       run_hook (Qactivate_menubar_hook);
503       set_frame_menubar (f, 1, 0);
504 #endif
505       DEVICE_X_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) =
506         DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) =
507         x_focus_timestamp_really_sucks_fix_me_better;
508     }
509 }
510
511 static widget_value *
512 compute_menubar_data (struct frame *f, Lisp_Object menubar, int deep_p)
513 {
514   widget_value *data;
515
516   if (NILP (menubar))
517     data = 0;
518   else
519     {
520       Lisp_Object old_buffer;
521       int count = specpdl_depth ();
522
523       old_buffer = Fcurrent_buffer ();
524       record_unwind_protect (Fset_buffer, old_buffer);
525       Fset_buffer ( XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer);
526       data = menu_item_descriptor_to_widget_value (menubar, MENUBAR_TYPE,
527                                                    deep_p, 0);
528       Fset_buffer (old_buffer);
529       unbind_to (count, Qnil);
530     }
531   return data;
532 }
533
534 static int
535 set_frame_menubar (struct frame *f, int deep_p, int first_time_p)
536 {
537   widget_value *data;
538   Lisp_Object menubar;
539   int menubar_visible;
540   long id;
541   /* As for the toolbar, the minibuffer does not have its own menubar. */
542   struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f));
543
544   if (! FRAME_X_P (f))
545     return 0;
546
547   /***** first compute the contents of the menubar *****/
548
549   if (! first_time_p)
550     {
551       /* evaluate `current-menubar' in the buffer of the selected window
552          of the frame in question. */
553       menubar = symbol_value_in_buffer (Qcurrent_menubar, w->buffer);
554     }
555   else
556     {
557       /* That's a little tricky the first time since the frame isn't
558          fully initialized yet. */
559       menubar = Fsymbol_value (Qcurrent_menubar);
560     }
561
562   if (NILP (menubar))
563     {
564       menubar = Vblank_menubar;
565       menubar_visible = 0;
566     }
567   else
568     menubar_visible = !NILP (w->menubar_visible_p);
569
570   data = compute_menubar_data (f, menubar, deep_p);
571   if (!data || (!data->next && !data->contents))
572     abort ();
573
574   if (NILP (FRAME_MENUBAR_DATA (f)))
575     {
576       struct popup_data *mdata =
577         alloc_lcrecord_type (struct popup_data, &lrecord_popup_data);
578
579       mdata->id = new_lwlib_id ();
580       mdata->last_menubar_buffer = Qnil;
581       mdata->menubar_contents_up_to_date = 0;
582       XSETPOPUP_DATA (FRAME_MENUBAR_DATA (f), mdata);
583     }
584
585   /***** now store into the menubar widget, creating it if necessary *****/
586
587   id = XFRAME_MENUBAR_DATA (f)->id;
588   if (!FRAME_X_MENUBAR_WIDGET (f))
589     {
590       Widget parent = FRAME_X_CONTAINER_WIDGET (f);
591
592       assert (first_time_p);
593
594       /* It's the first time we've mapped the menubar so compute its
595          contents completely once.  This makes sure that the menubar
596          components are created with the right type. */
597       if (!deep_p)
598         {
599           free_popup_widget_value_tree (data);
600           data = compute_menubar_data (f, menubar, 1);
601         }
602
603
604       FRAME_X_MENUBAR_WIDGET (f) =
605         lw_create_widget ("menubar", "menubar", id, data, parent,
606                           0, pre_activate_callback,
607                           popup_selection_callback, 0);
608
609     }
610   else
611     {
612       lw_modify_all_widgets (id, data, deep_p ? True : False);
613     }
614   free_popup_widget_value_tree (data);
615
616   XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date = deep_p;
617   XFRAME_MENUBAR_DATA (f)->last_menubar_buffer =
618     XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer;
619   return menubar_visible;
620 }
621
622
623 /* Called from x_create_widgets() to create the initial menubar of a frame
624    before it is mapped, so that the window is mapped with the menubar already
625    there instead of us tacking it on later and thrashing the window after it
626    is visible. */
627 int
628 x_initialize_frame_menubar (struct frame *f)
629 {
630   return set_frame_menubar (f, 1, 1);
631 }
632
633
634 static LWLIB_ID last_popup_menu_selection_callback_id;
635
636 static void
637 popup_menu_selection_callback (Widget widget, LWLIB_ID id,
638                                XtPointer client_data)
639 {
640   last_popup_menu_selection_callback_id = id;
641   popup_selection_callback (widget, id, client_data);
642   /* lw_destroy_all_widgets() will be called from popup_down_callback() */
643 }
644
645 static void
646 popup_menu_down_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
647 {
648   if (popup_handled_p (id))
649     return;
650   assert (popup_up_p != 0);
651   ungcpro_popup_callbacks (id);
652   popup_up_p--;
653   /* if this isn't called immediately after the selection callback, then
654      there wasn't a menu selection. */
655   if (id != last_popup_menu_selection_callback_id)
656     popup_selection_callback (widget, id, (XtPointer) -1);
657   lw_destroy_all_widgets (id);
658 }
659
660 \f
661 static void
662 make_dummy_xbutton_event (XEvent *dummy,
663                           Widget daddy,
664                           struct Lisp_Event *eev)
665      /* NULL for eev means query pointer */
666 {
667   XButtonPressedEvent *btn = (XButtonPressedEvent *) dummy;
668
669   btn->type = ButtonPress;
670   btn->serial = 0;
671   btn->send_event = 0;
672   btn->display = XtDisplay (daddy);
673   btn->window = XtWindow (daddy);
674   if (eev)
675     {
676       Position shellx, shelly, framex, framey;
677       Widget shell = XtParent (daddy);
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       XtSetArg (al [0], XtNx, &shellx);
686       XtSetArg (al [1], XtNy, &shelly);
687       XtGetValues (shell, al, 2);
688       XtSetArg (al [0], XtNx, &framex);
689       XtSetArg (al [1], XtNy, &framey);
690       XtGetValues (daddy, al, 2);
691       btn->x_root = shellx + framex + btn->x;
692       btn->y_root = shelly + framey + btn->y;
693       btn->state = ButtonPressMask; /* all buttons pressed */
694     }
695   else
696     {
697       /* CurrentTime is just ZERO, so it's worthless for
698          determining relative click times. */
699       struct device *d = get_device_from_display (XtDisplay (daddy));
700       btn->time = DEVICE_X_MOUSE_TIMESTAMP (d); /* event-Xt maintains this */
701       btn->button = 0;
702       XQueryPointer (btn->display, btn->window, &btn->root,
703                      &btn->subwindow, &btn->x_root, &btn->y_root,
704                      &btn->x, &btn->y, &btn->state);
705     }
706 }
707
708 \f
709
710 static void
711 x_update_frame_menubar_internal (struct frame *f)
712 {
713   /* We assume the menubar contents has changed if the global flag is set,
714      or if the current buffer has changed, or if the menubar has never
715      been updated before.
716    */
717   int menubar_contents_changed =
718     (f->menubar_changed
719      || NILP (FRAME_MENUBAR_DATA (f))
720      || (!EQ (XFRAME_MENUBAR_DATA (f)->last_menubar_buffer,
721               XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer)));
722
723   Boolean menubar_was_visible = XtIsManaged (FRAME_X_MENUBAR_WIDGET (f));
724   Boolean menubar_will_be_visible = menubar_was_visible;
725   Boolean menubar_visibility_changed;
726
727   if (menubar_contents_changed)
728     menubar_will_be_visible = set_frame_menubar (f, 0, 0);
729
730   menubar_visibility_changed = menubar_was_visible != menubar_will_be_visible;
731
732   if (!menubar_visibility_changed)
733     return;
734
735   /* Set menubar visibility */
736   (menubar_will_be_visible ? XtManageChild : XtUnmanageChild)
737     (FRAME_X_MENUBAR_WIDGET (f));
738
739   MARK_FRAME_SIZE_SLIPPED (f);
740 }
741
742 static void
743 x_update_frame_menubars (struct frame *f)
744 {
745   assert (FRAME_X_P (f));
746
747   x_update_frame_menubar_internal (f);
748
749   /* #### This isn't going to work right now that this function works on
750      a per-frame, not per-device basis.  Guess what?  I don't care. */
751 }
752
753 static void
754 x_free_frame_menubars (struct frame *f)
755 {
756   Widget menubar_widget;
757
758   assert (FRAME_X_P (f));
759
760   menubar_widget = FRAME_X_MENUBAR_WIDGET (f);
761   if (menubar_widget)
762     {
763       LWLIB_ID id = XFRAME_MENUBAR_DATA (f)->id;
764       lw_destroy_all_widgets (id);
765       XFRAME_MENUBAR_DATA (f)->id = 0;
766     }
767 }
768
769 static void
770 x_popup_menu (Lisp_Object menu_desc, Lisp_Object event)
771 {
772   int menu_id;
773   struct frame *f = selected_frame ();
774   widget_value *data;
775   Widget parent;
776   Widget menu;
777   struct Lisp_Event *eev = NULL;
778   XEvent xev;
779   Lisp_Object frame;
780
781   XSETFRAME (frame, f);
782   CHECK_X_FRAME (frame);
783   parent = FRAME_X_SHELL_WIDGET (f);
784
785   if (!NILP (event))
786     {
787       CHECK_LIVE_EVENT (event);
788       eev= XEVENT (event);
789       if (eev->event_type != button_press_event
790           && eev->event_type != button_release_event)
791         wrong_type_argument (Qmouse_event_p, event);
792     }
793   else if (!NILP (Vthis_command_keys))
794     {
795       /* if an event wasn't passed, use the last event of the event sequence
796          currently being executed, if that event is a mouse event */
797       eev = XEVENT (Vthis_command_keys); /* last event first */
798       if (eev->event_type != button_press_event
799           && eev->event_type != button_release_event)
800         eev = NULL;
801     }
802   make_dummy_xbutton_event (&xev, parent, eev);
803
804   if (SYMBOLP (menu_desc))
805     menu_desc = Fsymbol_value (menu_desc);
806   CHECK_CONS (menu_desc);
807   CHECK_STRING (XCAR (menu_desc));
808   data = menu_item_descriptor_to_widget_value (menu_desc, POPUP_TYPE, 1, 1);
809
810   if (! data) error ("no menu");
811
812   menu_id = new_lwlib_id ();
813   menu = lw_create_widget ("popup", "popup" /* data->name */, menu_id, data,
814                            parent, 1, 0,
815                            popup_menu_selection_callback,
816                            popup_menu_down_callback);
817   free_popup_widget_value_tree (data);
818
819   gcpro_popup_callbacks (menu_id);
820
821   /* Setting zmacs-region-stays is necessary here because executing a command
822      from a menu is really a two-command process: the first command (bound to
823      the button-click) simply pops up the menu, and returns.  This causes a
824      sequence of magic-events (destined for the popup-menu widget) to begin.
825      Eventually, a menu item is selected, and a menu-event blip is pushed onto
826      the end of the input stream, which is then executed by the event loop.
827
828      So there are two command-events, with a bunch of magic-events between
829      them.  We don't want the *first* command event to alter the state of the
830      region, so that the region can be available as an argument for the second
831      command.
832    */
833   if (zmacs_regions)
834     zmacs_region_stays = 1;
835
836   popup_up_p++;
837   lw_popup_menu (menu, &xev);
838   /* this speeds up display of pop-up menus */
839   XFlush (XtDisplay (parent));
840 }
841
842 \f
843 void
844 syms_of_menubar_x (void)
845 {
846 }
847
848 void
849 console_type_create_menubar_x (void)
850 {
851   CONSOLE_HAS_METHOD (x, update_frame_menubars);
852   CONSOLE_HAS_METHOD (x, free_frame_menubars);
853   CONSOLE_HAS_METHOD (x, popup_menu);
854 }
855
856 void
857 vars_of_menubar_x (void)
858 {
859   last_popup_menu_selection_callback_id = (LWLIB_ID) -1;
860
861 #if defined (LWLIB_MENUBARS_LUCID)
862   Fprovide (intern ("lucid-menubars"));
863 #elif defined (LWLIB_MENUBARS_MOTIF)
864   Fprovide (intern ("motif-menubars"));
865 #elif defined (LWLIB_MENUBARS_ATHENA)
866   Fprovide (intern ("athena-menubars"));
867 #endif
868 }