XEmacs 21.2.27 "Hera".
[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   int count = specpdl_depth ();
98   int partition_seen = 0;
99   widget_value *wv = xmalloc_widget_value ();
100   Lisp_Object wv_closure = make_opaque_ptr (wv);
101
102   record_unwind_protect (widget_value_unwind, wv_closure);
103
104   if (STRINGP (desc))
105     {
106       char *string_chars = (char *) XSTRING_DATA (desc);
107       wv->type = (separator_string_p (string_chars) ? SEPARATOR_TYPE :
108                   TEXT_TYPE);
109 #if 1
110       /* #### - should internationalize with X resources instead.
111          Not so! --ben */
112       string_chars = GETTEXT (string_chars);
113 #endif
114       if (wv->type == SEPARATOR_TYPE)
115         {
116           wv->value = menu_separator_style (string_chars);
117         }
118       else
119         {
120           wv->name = xstrdup (string_chars);
121           wv->enabled = 1;
122           /* dverna Dec. 98: command_builder_operate_menu_accelerator will
123              manipulate the accel as a Lisp_Object if the widget has a name.
124              Since simple labels have a name, but no accel, we *must* set it
125              to nil */
126           wv->accel = LISP_TO_VOID (Qnil);
127         }
128     }
129   else if (VECTORP (desc))
130     {
131       Lisp_Object gui_item = gui_parse_item_keywords (desc);
132       if (!button_item_to_widget_value (gui_item, wv, 1,
133                                         (menu_type == MENUBAR_TYPE
134                                          && depth <= 1)))
135         {
136           /* :included form was nil */
137           wv = NULL;
138           goto menu_item_done;
139         }
140     }
141   else if (CONSP (desc))
142     {
143       Lisp_Object incremental_data = desc;
144       widget_value *prev = 0;
145
146       if (STRINGP (XCAR (desc)))
147         {
148           Lisp_Object key, val;
149           Lisp_Object include_p = Qnil, hook_fn = Qnil, config_tag = Qnil;
150           Lisp_Object active_p = Qt;
151           Lisp_Object accel;
152           int included_spec = 0;
153           int active_spec = 0;
154           wv->type = CASCADE_TYPE;
155           wv->enabled = 1;
156           wv->name = (char *) XSTRING_DATA (LISP_GETTEXT (XCAR (desc)));
157           wv->name = xstrdup (wv->name);
158
159           accel = gui_name_accelerator (LISP_GETTEXT (XCAR (desc)));
160           wv->accel = LISP_TO_VOID (accel);
161
162           desc = Fcdr (desc);
163
164           while (key = Fcar (desc), KEYWORDP (key))
165             {
166               Lisp_Object cascade = desc;
167               desc = Fcdr (desc);
168               if (NILP (desc))
169                 signal_simple_error ("Keyword in menu lacks a value",
170                                      cascade);
171               val = Fcar (desc);
172               desc = Fcdr (desc);
173               if (EQ (key, Q_included))
174                 include_p = val, included_spec = 1;
175               else if (EQ (key, Q_config))
176                 config_tag = val;
177               else if (EQ (key, Q_filter))
178                 hook_fn = val;
179               else if (EQ (key, Q_active))
180                 active_p = val, active_spec = 1;
181               else if (EQ (key, Q_accelerator))
182                 {
183                   if ( SYMBOLP (val)
184                        || CHARP (val))
185                     wv->accel = LISP_TO_VOID (val);
186                   else
187                     signal_simple_error ("bad keyboard accelerator", val);
188                 }
189               else if (EQ (key, Q_label))
190                 {
191                   /* implement in 21.2 */
192                 }
193               else
194                 signal_simple_error ("Unknown menu cascade keyword", cascade);
195             }
196
197           if ((!NILP (config_tag)
198                && NILP (Fmemq (config_tag, Vmenubar_configuration)))
199               || (included_spec && NILP (Feval (include_p))))
200             {
201               wv = NULL;
202               goto menu_item_done;
203             }
204
205           if (active_spec)
206             active_p = Feval (active_p);
207
208           if (!NILP (hook_fn) && !NILP (active_p))
209             {
210 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
211               if (filter_p || depth == 0)
212                 {
213 #endif
214                   desc = call1_trapping_errors ("Error in menubar filter",
215                                                 hook_fn, desc);
216                   if (UNBOUNDP (desc))
217                     desc = Qnil;
218 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
219                 }
220               else
221                 {
222                   widget_value *incr_wv = xmalloc_widget_value ();
223                   wv->contents = incr_wv;
224                   incr_wv->type = INCREMENTAL_TYPE;
225                   incr_wv->enabled = 1;
226                   incr_wv->name = wv->name;
227                   incr_wv->name = xstrdup (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 = xstrdup (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 = xstrdup ("(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 = xstrdup ("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 = xstrdup ("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   if (NILP (menubar))
515     return 0;
516   else
517     {
518       widget_value *data;
519       int count = specpdl_depth ();
520
521       record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
522       Fset_buffer (XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer);
523       data = menu_item_descriptor_to_widget_value (menubar, MENUBAR_TYPE,
524                                                    deep_p, 0);
525       unbind_to (count, Qnil);
526
527       return data;
528     }
529 }
530
531 static int
532 set_frame_menubar (struct frame *f, int deep_p, int first_time_p)
533 {
534   widget_value *data;
535   Lisp_Object menubar;
536   int menubar_visible;
537   long id;
538   /* As with the toolbar, the minibuffer does not have its own menubar. */
539   struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f));
540
541   if (! FRAME_X_P (f))
542     return 0;
543
544   /***** first compute the contents of the menubar *****/
545
546   if (! first_time_p)
547     {
548       /* evaluate `current-menubar' in the buffer of the selected window
549          of the frame in question. */
550       menubar = symbol_value_in_buffer (Qcurrent_menubar, w->buffer);
551     }
552   else
553     {
554       /* That's a little tricky the first time since the frame isn't
555          fully initialized yet. */
556       menubar = Fsymbol_value (Qcurrent_menubar);
557     }
558
559   if (NILP (menubar))
560     {
561       menubar = Vblank_menubar;
562       menubar_visible = 0;
563     }
564   else
565     menubar_visible = !NILP (w->menubar_visible_p);
566
567   data = compute_menubar_data (f, menubar, deep_p);
568   if (!data || (!data->next && !data->contents))
569     abort ();
570
571   if (NILP (FRAME_MENUBAR_DATA (f)))
572     {
573       struct popup_data *mdata =
574         alloc_lcrecord_type (struct popup_data, &lrecord_popup_data);
575
576       mdata->id = new_lwlib_id ();
577       mdata->last_menubar_buffer = Qnil;
578       mdata->menubar_contents_up_to_date = 0;
579       XSETPOPUP_DATA (FRAME_MENUBAR_DATA (f), mdata);
580     }
581
582   /***** now store into the menubar widget, creating it if necessary *****/
583
584   id = XFRAME_MENUBAR_DATA (f)->id;
585   if (!FRAME_X_MENUBAR_WIDGET (f))
586     {
587       Widget parent = FRAME_X_CONTAINER_WIDGET (f);
588
589       assert (first_time_p);
590
591       /* It's the first time we've mapped the menubar so compute its
592          contents completely once.  This makes sure that the menubar
593          components are created with the right type. */
594       if (!deep_p)
595         {
596           free_popup_widget_value_tree (data);
597           data = compute_menubar_data (f, menubar, 1);
598         }
599
600
601       FRAME_X_MENUBAR_WIDGET (f) =
602         lw_create_widget ("menubar", "menubar", id, data, parent,
603                           0, pre_activate_callback,
604                           popup_selection_callback, 0);
605
606     }
607   else
608     {
609       lw_modify_all_widgets (id, data, deep_p ? True : False);
610     }
611   free_popup_widget_value_tree (data);
612
613   XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date = deep_p;
614   XFRAME_MENUBAR_DATA (f)->last_menubar_buffer =
615     XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer;
616   return menubar_visible;
617 }
618
619
620 /* Called from x_create_widgets() to create the initial menubar of a frame
621    before it is mapped, so that the window is mapped with the menubar already
622    there instead of us tacking it on later and thrashing the window after it
623    is visible. */
624 int
625 x_initialize_frame_menubar (struct frame *f)
626 {
627   return set_frame_menubar (f, 1, 1);
628 }
629
630
631 static LWLIB_ID last_popup_menu_selection_callback_id;
632
633 static void
634 popup_menu_selection_callback (Widget widget, LWLIB_ID id,
635                                XtPointer client_data)
636 {
637   last_popup_menu_selection_callback_id = id;
638   popup_selection_callback (widget, id, client_data);
639   /* lw_destroy_all_widgets() will be called from popup_down_callback() */
640 }
641
642 static void
643 popup_menu_down_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
644 {
645   if (popup_handled_p (id))
646     return;
647   assert (popup_up_p != 0);
648   ungcpro_popup_callbacks (id);
649   popup_up_p--;
650   /* if this isn't called immediately after the selection callback, then
651      there wasn't a menu selection. */
652   if (id != last_popup_menu_selection_callback_id)
653     popup_selection_callback (widget, id, (XtPointer) -1);
654   lw_destroy_all_widgets (id);
655 }
656
657 \f
658 static void
659 make_dummy_xbutton_event (XEvent *dummy,
660                           Widget daddy,
661                           struct Lisp_Event *eev)
662      /* NULL for eev means query pointer */
663 {
664   XButtonPressedEvent *btn = (XButtonPressedEvent *) dummy;
665
666   btn->type = ButtonPress;
667   btn->serial = 0;
668   btn->send_event = 0;
669   btn->display = XtDisplay (daddy);
670   btn->window = XtWindow (daddy);
671   if (eev)
672     {
673       Position shellx, shelly, framex, framey;
674       Arg al [2];
675       btn->time = eev->timestamp;
676       btn->button = eev->event.button.button;
677       btn->root = RootWindowOfScreen (XtScreen (daddy));
678       btn->subwindow = (Window) NULL;
679       btn->x = eev->event.button.x;
680       btn->y = eev->event.button.y;
681       shellx = shelly = 0;
682 #ifndef HAVE_WMCOMMAND
683       {
684         Widget shell = XtParent (daddy);
685
686         XtSetArg (al [0], XtNx, &shellx);
687         XtSetArg (al [1], XtNy, &shelly);
688         XtGetValues (shell, al, 2);
689       }
690 #endif
691       XtSetArg (al [0], XtNx, &framex);
692       XtSetArg (al [1], XtNy, &framey);
693       XtGetValues (daddy, al, 2);
694       btn->x_root = shellx + framex + btn->x;
695       btn->y_root = shelly + framey + btn->y;
696       btn->state = ButtonPressMask; /* all buttons pressed */
697     }
698   else
699     {
700       /* CurrentTime is just ZERO, so it's worthless for
701          determining relative click times. */
702       struct device *d = get_device_from_display (XtDisplay (daddy));
703       btn->time = DEVICE_X_MOUSE_TIMESTAMP (d); /* event-Xt maintains this */
704       btn->button = 0;
705       XQueryPointer (btn->display, btn->window, &btn->root,
706                      &btn->subwindow, &btn->x_root, &btn->y_root,
707                      &btn->x, &btn->y, &btn->state);
708     }
709 }
710
711 \f
712
713 static void
714 x_update_frame_menubar_internal (struct frame *f)
715 {
716   /* We assume the menubar contents has changed if the global flag is set,
717      or if the current buffer has changed, or if the menubar has never
718      been updated before.
719    */
720   int menubar_contents_changed =
721     (f->menubar_changed
722      || NILP (FRAME_MENUBAR_DATA (f))
723      || (!EQ (XFRAME_MENUBAR_DATA (f)->last_menubar_buffer,
724               XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer)));
725
726   Boolean menubar_was_visible = XtIsManaged (FRAME_X_MENUBAR_WIDGET (f));
727   Boolean menubar_will_be_visible = menubar_was_visible;
728   Boolean menubar_visibility_changed;
729
730   if (menubar_contents_changed)
731     menubar_will_be_visible = set_frame_menubar (f, 0, 0);
732
733   menubar_visibility_changed = menubar_was_visible != menubar_will_be_visible;
734
735   if (!menubar_visibility_changed)
736     return;
737
738   /* Set menubar visibility */
739   (menubar_will_be_visible ? XtManageChild : XtUnmanageChild)
740     (FRAME_X_MENUBAR_WIDGET (f));
741
742   MARK_FRAME_SIZE_SLIPPED (f);
743 }
744
745 static void
746 x_update_frame_menubars (struct frame *f)
747 {
748   assert (FRAME_X_P (f));
749
750   x_update_frame_menubar_internal (f);
751
752   /* #### This isn't going to work right now that this function works on
753      a per-frame, not per-device basis.  Guess what?  I don't care. */
754 }
755
756 static void
757 x_free_frame_menubars (struct frame *f)
758 {
759   Widget menubar_widget;
760
761   assert (FRAME_X_P (f));
762
763   menubar_widget = FRAME_X_MENUBAR_WIDGET (f);
764   if (menubar_widget)
765     {
766       LWLIB_ID id = XFRAME_MENUBAR_DATA (f)->id;
767       lw_destroy_all_widgets (id);
768       XFRAME_MENUBAR_DATA (f)->id = 0;
769     }
770 }
771
772 static void
773 x_popup_menu (Lisp_Object menu_desc, Lisp_Object event)
774 {
775   int menu_id;
776   struct frame *f = selected_frame ();
777   widget_value *data;
778   Widget parent;
779   Widget menu;
780   struct Lisp_Event *eev = NULL;
781   XEvent xev;
782   Lisp_Object frame;
783
784   XSETFRAME (frame, f);
785   CHECK_X_FRAME (frame);
786   parent = FRAME_X_SHELL_WIDGET (f);
787
788   if (!NILP (event))
789     {
790       CHECK_LIVE_EVENT (event);
791       eev= XEVENT (event);
792       if (eev->event_type != button_press_event
793           && eev->event_type != button_release_event)
794         wrong_type_argument (Qmouse_event_p, event);
795     }
796   else if (!NILP (Vthis_command_keys))
797     {
798       /* if an event wasn't passed, use the last event of the event sequence
799          currently being executed, if that event is a mouse event */
800       eev = XEVENT (Vthis_command_keys); /* last event first */
801       if (eev->event_type != button_press_event
802           && eev->event_type != button_release_event)
803         eev = NULL;
804     }
805   make_dummy_xbutton_event (&xev, parent, eev);
806
807   if (SYMBOLP (menu_desc))
808     menu_desc = Fsymbol_value (menu_desc);
809   CHECK_CONS (menu_desc);
810   CHECK_STRING (XCAR (menu_desc));
811   data = menu_item_descriptor_to_widget_value (menu_desc, POPUP_TYPE, 1, 1);
812
813   if (! data) error ("no menu");
814
815   menu_id = new_lwlib_id ();
816   menu = lw_create_widget ("popup", "popup" /* data->name */, menu_id, data,
817                            parent, 1, 0,
818                            popup_menu_selection_callback,
819                            popup_menu_down_callback);
820   free_popup_widget_value_tree (data);
821
822   gcpro_popup_callbacks (menu_id);
823
824   /* Setting zmacs-region-stays is necessary here because executing a command
825      from a menu is really a two-command process: the first command (bound to
826      the button-click) simply pops up the menu, and returns.  This causes a
827      sequence of magic-events (destined for the popup-menu widget) to begin.
828      Eventually, a menu item is selected, and a menu-event blip is pushed onto
829      the end of the input stream, which is then executed by the event loop.
830
831      So there are two command-events, with a bunch of magic-events between
832      them.  We don't want the *first* command event to alter the state of the
833      region, so that the region can be available as an argument for the second
834      command.
835    */
836   if (zmacs_regions)
837     zmacs_region_stays = 1;
838
839   popup_up_p++;
840   lw_popup_menu (menu, &xev);
841   /* this speeds up display of pop-up menus */
842   XFlush (XtDisplay (parent));
843 }
844
845 \f
846 void
847 syms_of_menubar_x (void)
848 {
849 }
850
851 void
852 console_type_create_menubar_x (void)
853 {
854   CONSOLE_HAS_METHOD (x, update_frame_menubars);
855   CONSOLE_HAS_METHOD (x, free_frame_menubars);
856   CONSOLE_HAS_METHOD (x, popup_menu);
857 }
858
859 void
860 reinit_vars_of_menubar_x (void)
861 {
862   last_popup_menu_selection_callback_id = (LWLIB_ID) -1;
863 }
864
865 void
866 vars_of_menubar_x (void)
867 {
868   reinit_vars_of_menubar_x ();
869
870 #if defined (LWLIB_MENUBARS_LUCID)
871   Fprovide (intern ("lucid-menubars"));
872 #elif defined (LWLIB_MENUBARS_MOTIF)
873   Fprovide (intern ("motif-menubars"));
874 #elif defined (LWLIB_MENUBARS_ATHENA)
875   Fprovide (intern ("athena-menubars"));
876 #endif
877 }