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