XEmacs 21.2.32 "Kastor & Polydeukes".
[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 /* Authorship:
25
26    Created 16-dec-91 by Jamie Zawinski.
27    Menu filters and many other keywords added by Stig for 19.12.
28    Original device-abstraction work and GC cleanup work by Ben Wing for 19.13.
29    Menu accelerators c. 1997? by ??.  Moved here from event-stream.c.
30    Other work post-1996 by ??.
31 */
32
33 #include <config.h>
34 #include "lisp.h"
35
36 #include "console-x.h"
37 #include "EmacsFrame.h"
38 #include "gui-x.h"
39 #include "../lwlib/lwlib.h"
40
41 #include "buffer.h"
42 #include "commands.h"           /* zmacs_regions */
43 #include "events.h"
44 #include "frame.h"
45 #include "gui.h"
46 #include "keymap.h"
47 #include "menubar.h"
48 #include "opaque.h"
49 #include "window.h"
50
51 static int set_frame_menubar (struct frame *f,
52                               int deep_p,
53                               int first_time_p);
54
55 #define FRAME_MENUBAR_DATA(frame) ((frame)->menubar_data)
56 #define XFRAME_MENUBAR_DATA(frame) XPOPUP_DATA ((frame)->menubar_data)
57
58 #define MENUBAR_TYPE    0
59 #define SUBMENU_TYPE    1
60 #define POPUP_TYPE      2
61
62 \f
63 /* Converting Lisp menu tree descriptions to lwlib's `widget_value' form.
64
65    menu_item_descriptor_to_widget_value() converts a lisp description of a
66    menubar into a tree of widget_value structures.  It allocates widget_values
67    with malloc_widget_value() and allocates other storage only for the `key'
68    slot.  All other slots are filled with pointers to Lisp_String data.  We
69    allocate a widget_value description of the menu or menubar, and hand it to
70    lwlib, which then makes a copy of it, which it manages internally.  We then
71    immediately free our widget_value tree; it will not be referenced again.
72
73    Incremental menu construction callbacks operate just a bit differently.
74    They allocate widget_values and call replace_widget_value_tree() to tell
75    lwlib to destructively modify the incremental stub (subtree) of its
76    separate widget_value tree.
77
78    This function is highly recursive (it follows the menu trees) and may call
79    eval.  The reason we keep pointers to lisp string data instead of copying
80    it and freeing it later is to avoid the speed penalty that would entail
81    (since this needs to be fast, in the simple cases at least).  (The reason
82    we malloc/free the keys slot is because there's not a lisp string around
83    for us to use in that case.)
84
85    Since we keep pointers to lisp strings, and we call eval, we could lose if
86    GC relocates (or frees) those strings.  It's not easy to gc protect the
87    strings because of the recursive nature of this function, and the fact that
88    it returns a data structure that gets freed later.  So...  we do the
89    sleaziest thing possible and inhibit GC for the duration.  This is probably
90    not a big deal...
91
92    We do not have to worry about the pointers to Lisp_String data after
93    this function successfully finishes.  lwlib copies all such data with
94    strdup().  */
95
96 static widget_value *
97 menu_item_descriptor_to_widget_value_1 (Lisp_Object desc,
98                                         int menu_type, int deep_p,
99                                         int filter_p,
100                                         int depth)
101 {
102   /* This function cannot GC.
103      It is only called from menu_item_descriptor_to_widget_value, which
104      prohibits GC. */
105   /* !!#### This function has not been Mule-ized */
106   int menubar_root_p = (menu_type == MENUBAR_TYPE && depth == 0);
107   int count = specpdl_depth ();
108   int partition_seen = 0;
109   widget_value *wv = xmalloc_widget_value ();
110   Lisp_Object wv_closure = make_opaque_ptr (wv);
111
112   record_unwind_protect (widget_value_unwind, wv_closure);
113
114   if (STRINGP (desc))
115     {
116       char *string_chars = (char *) XSTRING_DATA (desc);
117       wv->type = (separator_string_p (string_chars) ? SEPARATOR_TYPE :
118                   TEXT_TYPE);
119 #if 1
120       /* #### - should internationalize with X resources instead.
121          Not so! --ben */
122       string_chars = GETTEXT (string_chars);
123 #endif
124       if (wv->type == SEPARATOR_TYPE)
125         {
126           wv->value = menu_separator_style (string_chars);
127         }
128       else
129         {
130           wv->name = xstrdup (string_chars);
131           wv->enabled = 1;
132           /* dverna Dec. 98: command_builder_operate_menu_accelerator will
133              manipulate the accel as a Lisp_Object if the widget has a name.
134              Since simple labels have a name, but no accel, we *must* set it
135              to nil */
136           wv->accel = LISP_TO_VOID (Qnil);
137         }
138     }
139   else if (VECTORP (desc))
140     {
141       Lisp_Object gui_item = gui_parse_item_keywords (desc);
142       if (!button_item_to_widget_value (gui_item, wv, 1,
143                                         (menu_type == MENUBAR_TYPE
144                                          && depth <= 1)))
145         {
146           /* :included form was nil */
147           wv = NULL;
148           goto menu_item_done;
149         }
150     }
151   else if (CONSP (desc))
152     {
153       Lisp_Object incremental_data = desc;
154       widget_value *prev = 0;
155
156       if (STRINGP (XCAR (desc)))
157         {
158           Lisp_Object key, val;
159           Lisp_Object include_p = Qnil, hook_fn = Qnil, config_tag = Qnil;
160           Lisp_Object active_p = Qt;
161           Lisp_Object accel;
162           int included_spec = 0;
163           int active_spec = 0;
164           wv->type = CASCADE_TYPE;
165           wv->enabled = 1;
166           wv->name = (char *) XSTRING_DATA (LISP_GETTEXT (XCAR (desc)));
167           wv->name = strdup_and_add_accel (wv->name);
168
169           accel = gui_name_accelerator (LISP_GETTEXT (XCAR (desc)));
170           wv->accel = LISP_TO_VOID (accel);
171
172           desc = Fcdr (desc);
173
174           while (key = Fcar (desc), KEYWORDP (key))
175             {
176               Lisp_Object cascade = desc;
177               desc = Fcdr (desc);
178               if (NILP (desc))
179                 signal_simple_error ("Keyword in menu lacks a value",
180                                      cascade);
181               val = Fcar (desc);
182               desc = Fcdr (desc);
183               if (EQ (key, Q_included))
184                 include_p = val, included_spec = 1;
185               else if (EQ (key, Q_config))
186                 config_tag = val;
187               else if (EQ (key, Q_filter))
188                 hook_fn = val;
189               else if (EQ (key, Q_active))
190                 active_p = val, active_spec = 1;
191               else if (EQ (key, Q_accelerator))
192                 {
193                   if ( SYMBOLP (val)
194                        || CHARP (val))
195                     wv->accel = LISP_TO_VOID (val);
196                   else
197                     signal_simple_error ("bad keyboard accelerator", val);
198                 }
199               else if (EQ (key, Q_label))
200                 {
201                   /* implement in 21.2 */
202                 }
203               else
204                 signal_simple_error ("Unknown menu cascade keyword", cascade);
205             }
206
207           if ((!NILP (config_tag)
208                && NILP (Fmemq (config_tag, Vmenubar_configuration)))
209               || (included_spec && NILP (Feval (include_p))))
210             {
211               wv = NULL;
212               goto menu_item_done;
213             }
214
215           if (active_spec)
216             active_p = Feval (active_p);
217
218           if (!NILP (hook_fn) && !NILP (active_p))
219             {
220 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
221               if (filter_p || depth == 0)
222                 {
223 #endif
224                   desc = call1_trapping_errors ("Error in menubar filter",
225                                                 hook_fn, desc);
226                   if (UNBOUNDP (desc))
227                     desc = Qnil;
228 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
229                 }
230               else
231                 {
232                   widget_value *incr_wv = xmalloc_widget_value ();
233                   wv->contents = incr_wv;
234                   incr_wv->type = INCREMENTAL_TYPE;
235                   incr_wv->enabled = 1;
236                   incr_wv->name = wv->name;
237                   incr_wv->name = xstrdup (wv->name);
238                   /* This is automatically GC protected through
239                      the call to lw_map_widget_values(); no need
240                      to worry. */
241                   incr_wv->call_data = LISP_TO_VOID (incremental_data);
242                   goto menu_item_done;
243                 }
244 #endif /* LWLIB_MENUBARS_LUCID || LWLIB_MENUBARS_MOTIF */
245             }
246           if (menu_type == POPUP_TYPE && popup_menu_titles && depth == 0)
247             {
248               /* Simply prepend three more widget values to the contents of
249                  the menu: a label, and two separators (to get a double
250                  line). */
251               widget_value *title_wv = xmalloc_widget_value ();
252               widget_value *sep_wv = xmalloc_widget_value ();
253               title_wv->type = TEXT_TYPE;
254               title_wv->name = xstrdup (wv->name);
255               title_wv->enabled = 1;
256               title_wv->next = sep_wv;
257               sep_wv->type = SEPARATOR_TYPE;
258               sep_wv->value = menu_separator_style ("==");
259               sep_wv->next = 0;
260
261               wv->contents = title_wv;
262               prev = sep_wv;
263             }
264           wv->enabled = ! NILP (active_p);
265           if (deep_p && !wv->enabled  && !NILP (desc))
266             {
267               widget_value *dummy;
268               /* Add a fake entry so the menus show up */
269               wv->contents = dummy = xmalloc_widget_value ();
270               dummy->name = xstrdup ("(inactive)");
271               dummy->accel = LISP_TO_VOID (Qnil);
272               dummy->enabled = 0;
273               dummy->selected = 0;
274               dummy->value = NULL;
275               dummy->type = BUTTON_TYPE;
276               dummy->call_data = NULL;
277               dummy->next = NULL;
278
279               goto menu_item_done;
280             }
281
282         }
283       else if (menubar_root_p)
284         {
285           wv->name = xstrdup ("menubar");
286           wv->type = CASCADE_TYPE; /* Well, nothing else seems to fit and
287                                       this is ignored anyway...  */
288         }
289       else
290         {
291           signal_simple_error ("Menu name (first element) must be a string",
292                                desc);
293         }
294
295       if (deep_p || menubar_root_p)
296         {
297           widget_value *next;
298           for (; !NILP (desc); desc = Fcdr (desc))
299             {
300               Lisp_Object child = Fcar (desc);
301               if (menubar_root_p && NILP (child))       /* the partition */
302                 {
303                   if (partition_seen)
304                     error (
305                            "More than one partition (nil) in menubar description");
306                   partition_seen = 1;
307                   next = xmalloc_widget_value ();
308                   next->type = PUSHRIGHT_TYPE;
309                 }
310               else
311                 {
312                   next = menu_item_descriptor_to_widget_value_1
313                     (child, menu_type, deep_p, filter_p, depth + 1);
314                 }
315               if (! next)
316                 continue;
317               else if (prev)
318                 prev->next = next;
319               else
320                 wv->contents = next;
321               prev = next;
322             }
323         }
324       if (deep_p && !wv->contents)
325         wv = NULL;
326     }
327   else if (NILP (desc))
328     error ("nil may not appear in menu descriptions");
329   else
330     signal_simple_error ("Unrecognized menu descriptor", desc);
331
332  menu_item_done:
333
334   if (wv)
335     {
336       /* Completed normally.  Clear out the object that widget_value_unwind()
337          will be called with to tell it not to free the wv (as we are
338          returning it.) */
339       set_opaque_ptr (wv_closure, 0);
340     }
341
342   unbind_to (count, Qnil);
343   return wv;
344 }
345
346 static widget_value *
347 menu_item_descriptor_to_widget_value (Lisp_Object desc,
348                                       int menu_type, /* if this is a menubar,
349                                                         popup or sub menu */
350                                       int deep_p,    /*  */
351                                       int filter_p)  /* if :filter forms
352                                                         should run now */
353 {
354   widget_value *wv;
355   int count = specpdl_depth ();
356   record_unwind_protect (restore_gc_inhibit,
357                          make_int (gc_currently_forbidden));
358   gc_currently_forbidden = 1;
359   /* Can't GC! */
360   wv = menu_item_descriptor_to_widget_value_1 (desc, menu_type, deep_p,
361                                                filter_p, 0);
362   unbind_to (count, Qnil);
363   return wv;
364 }
365
366
367 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF
368 int in_menu_callback;
369
370 static Lisp_Object
371 restore_in_menu_callback (Lisp_Object val)
372 {
373   in_menu_callback = XINT (val);
374   return Qnil;
375 }
376 #endif /* LWLIB_MENUBARS_LUCID || LWLIB_MENUBARS_MOTIF */
377
378 #if 0
379 /* #### Sort of a hack needed to process Vactivate_menubar_hook
380    correctly wrt buffer-local values.  A correct solution would
381    involve adding a callback mechanism to run_hook().  This function
382    is currently unused.  */
383 static int
384 my_run_hook (Lisp_Object hooksym, int allow_global_p)
385 {
386   /* This function can GC */
387   Lisp_Object tail;
388   Lisp_Object value = Fsymbol_value (hooksym);
389   int changes = 0;
390
391   if (!NILP (value) && (!CONSP (value) || EQ (XCAR (value), Qlambda)))
392     return !EQ (call0 (value), Qt);
393
394   EXTERNAL_LIST_LOOP (tail, value)
395     {
396       if (allow_global_p && EQ (XCAR (tail), Qt))
397         changes |= my_run_hook (Fdefault_value (hooksym), 0);
398       if (!EQ (call0 (XCAR (tail)), Qt))
399         changes = 1;
400     }
401   return changes;
402 }
403 #endif
404
405
406 /* The order in which callbacks are run is funny to say the least.
407    It's sometimes tricky to avoid running a callback twice, and to
408    avoid returning prematurely.  So, this function returns true
409    if the menu's callbacks are no longer gc protected.  So long
410    as we unprotect them before allowing other callbacks to run,
411    everything should be ok.
412
413    The pre_activate_callback() *IS* intentionally called multiple times.
414    If client_data == NULL, then it's being called before the menu is posted.
415    If client_data != NULL, then client_data is a (widget_value *) and
416    client_data->data is a Lisp_Object pointing to a lisp submenu description
417    that must be converted into widget_values.  *client_data is destructively
418    modified.
419
420    #### Stig thinks that there may be a GC problem here due to the
421    fact that pre_activate_callback() is called multiple times, but I
422    think he's wrong.
423
424    */
425
426 static void
427 pre_activate_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
428 {
429   /* This function can GC */
430   struct device *d = get_device_from_display (XtDisplay (widget));
431   struct frame *f = x_any_window_to_frame (d, XtWindow (widget));
432   Lisp_Object frame;
433   int count;
434
435   /* set in lwlib to the time stamp associated with the most recent menu
436      operation */
437   extern Time x_focus_timestamp_really_sucks_fix_me_better;
438
439   if (!f)
440     f = x_any_window_to_frame (d, XtWindow (XtParent (widget)));
441   if (!f)
442     return;
443
444   /* make sure f is the selected frame */
445   XSETFRAME (frame, f);
446   Fselect_frame (frame);
447
448   if (client_data)
449     {
450       /* this is an incremental menu construction callback */
451       widget_value *hack_wv = (widget_value *) client_data;
452       Lisp_Object submenu_desc;
453       widget_value *wv;
454
455       assert (hack_wv->type == INCREMENTAL_TYPE);
456       VOID_TO_LISP (submenu_desc, hack_wv->call_data);
457
458       /*
459        * #### Fix the menu code so this isn't necessary.
460        *
461        * Protect against reentering the menu code otherwise we will
462        * crash later when the code gets confused at the state
463        * changes.
464        */
465       count = specpdl_depth ();
466       record_unwind_protect (restore_in_menu_callback,
467                              make_int (in_menu_callback));
468       in_menu_callback = 1;
469       wv = menu_item_descriptor_to_widget_value (submenu_desc, SUBMENU_TYPE,
470                                                  1, 0);
471       unbind_to (count, Qnil);
472
473       if (!wv)
474         {
475           wv = xmalloc_widget_value ();
476           wv->type = CASCADE_TYPE;
477           wv->next = NULL;
478           wv->accel = LISP_TO_VOID (Qnil);
479           wv->contents = xmalloc_widget_value ();
480           wv->contents->type = TEXT_TYPE;
481           wv->contents->name = xstrdup ("No menu");
482           wv->contents->next = NULL;
483           wv->contents->accel = LISP_TO_VOID (Qnil);
484         }
485       assert (wv && wv->type == CASCADE_TYPE && wv->contents);
486       replace_widget_value_tree (hack_wv, wv->contents);
487       free_popup_widget_value_tree (wv);
488     }
489   else if (!POPUP_DATAP (FRAME_MENUBAR_DATA (f)))
490     return;
491   else
492     {
493 #if 0 /* Unused, see comment below. */
494       int any_changes;
495
496       /* #### - this menubar update mechanism is expensively anti-social and
497          the activate-menubar-hook is now mostly obsolete. */
498       any_changes = my_run_hook (Qactivate_menubar_hook, 1);
499
500       /* #### - It is necessary to *ALWAYS* call set_frame_menubar() now that
501          incremental menus are implemented.  If a subtree of a menu has been
502          updated incrementally (a destructive operation), then that subtree
503          must somehow be wiped.
504
505          It is difficult to undo the destructive operation in lwlib because
506          a pointer back to lisp data needs to be hidden away somewhere.  So
507          that an INCREMENTAL_TYPE widget_value can be recreated...  Hmmmmm. */
508       if (any_changes ||
509           !XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date)
510         set_frame_menubar (f, 1, 0);
511 #else
512       run_hook (Qactivate_menubar_hook);
513       set_frame_menubar (f, 1, 0);
514 #endif
515       DEVICE_X_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) =
516         DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) =
517         x_focus_timestamp_really_sucks_fix_me_better;
518     }
519 }
520
521 static widget_value *
522 compute_menubar_data (struct frame *f, Lisp_Object menubar, int deep_p)
523 {
524   if (NILP (menubar))
525     return 0;
526   else
527     {
528       widget_value *data;
529       int count = specpdl_depth ();
530
531       record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
532       Fset_buffer (XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer);
533       data = menu_item_descriptor_to_widget_value (menubar, MENUBAR_TYPE,
534                                                    deep_p, 0);
535       unbind_to (count, Qnil);
536
537       return data;
538     }
539 }
540
541 static int
542 set_frame_menubar (struct frame *f, int deep_p, int first_time_p)
543 {
544   widget_value *data;
545   Lisp_Object menubar;
546   int menubar_visible;
547   long id;
548   /* As with the toolbar, the minibuffer does not have its own menubar. */
549   struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f));
550
551   if (! FRAME_X_P (f))
552     return 0;
553
554   /***** first compute the contents of the menubar *****/
555
556   if (! first_time_p)
557     {
558       /* evaluate `current-menubar' in the buffer of the selected window
559          of the frame in question. */
560       menubar = symbol_value_in_buffer (Qcurrent_menubar, w->buffer);
561     }
562   else
563     {
564       /* That's a little tricky the first time since the frame isn't
565          fully initialized yet. */
566       menubar = Fsymbol_value (Qcurrent_menubar);
567     }
568
569   if (NILP (menubar))
570     {
571       menubar = Vblank_menubar;
572       menubar_visible = 0;
573     }
574   else
575     menubar_visible = !NILP (w->menubar_visible_p);
576
577   data = compute_menubar_data (f, menubar, deep_p);
578   if (!data || (!data->next && !data->contents))
579     abort ();
580
581   if (NILP (FRAME_MENUBAR_DATA (f)))
582     {
583       struct popup_data *mdata =
584         alloc_lcrecord_type (struct popup_data, &lrecord_popup_data);
585
586       mdata->id = new_lwlib_id ();
587       mdata->last_menubar_buffer = Qnil;
588       mdata->menubar_contents_up_to_date = 0;
589       XSETPOPUP_DATA (FRAME_MENUBAR_DATA (f), mdata);
590     }
591
592   /***** now store into the menubar widget, creating it if necessary *****/
593
594   id = XFRAME_MENUBAR_DATA (f)->id;
595   if (!FRAME_X_MENUBAR_WIDGET (f))
596     {
597       Widget parent = FRAME_X_CONTAINER_WIDGET (f);
598
599       assert (first_time_p);
600
601       /* It's the first time we've mapped the menubar so compute its
602          contents completely once.  This makes sure that the menubar
603          components are created with the right type. */
604       if (!deep_p)
605         {
606           free_popup_widget_value_tree (data);
607           data = compute_menubar_data (f, menubar, 1);
608         }
609
610
611       FRAME_X_MENUBAR_WIDGET (f) =
612         lw_create_widget ("menubar", "menubar", id, data, parent,
613                           0, pre_activate_callback,
614                           popup_selection_callback, 0);
615
616     }
617   else
618     {
619       lw_modify_all_widgets (id, data, deep_p ? True : False);
620     }
621   free_popup_widget_value_tree (data);
622
623   XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date = deep_p;
624   XFRAME_MENUBAR_DATA (f)->last_menubar_buffer =
625     XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer;
626   return menubar_visible;
627 }
628
629
630 /* Called from x_create_widgets() to create the initial menubar of a frame
631    before it is mapped, so that the window is mapped with the menubar already
632    there instead of us tacking it on later and thrashing the window after it
633    is visible. */
634 int
635 x_initialize_frame_menubar (struct frame *f)
636 {
637   return set_frame_menubar (f, 1, 1);
638 }
639
640
641 static LWLIB_ID last_popup_menu_selection_callback_id;
642
643 static void
644 popup_menu_selection_callback (Widget widget, LWLIB_ID id,
645                                XtPointer client_data)
646 {
647   last_popup_menu_selection_callback_id = id;
648   popup_selection_callback (widget, id, client_data);
649   /* lw_destroy_all_widgets() will be called from popup_down_callback() */
650 }
651
652 static void
653 popup_menu_down_callback (Widget widget, LWLIB_ID id, XtPointer client_data)
654 {
655   if (popup_handled_p (id))
656     return;
657   assert (popup_up_p != 0);
658   ungcpro_popup_callbacks (id);
659   popup_up_p--;
660   /* if this isn't called immediately after the selection callback, then
661      there wasn't a menu selection. */
662   if (id != last_popup_menu_selection_callback_id)
663     popup_selection_callback (widget, id, (XtPointer) -1);
664   lw_destroy_all_widgets (id);
665 }
666
667 \f
668 static void
669 make_dummy_xbutton_event (XEvent *dummy, Widget daddy, Lisp_Event *eev)
670      /* NULL for eev means query pointer */
671 {
672   XButtonPressedEvent *btn = (XButtonPressedEvent *) dummy;
673
674   btn->type = ButtonPress;
675   btn->serial = 0;
676   btn->send_event = 0;
677   btn->display = XtDisplay (daddy);
678   btn->window = XtWindow (daddy);
679   if (eev)
680     {
681       Position shellx, shelly, framex, framey;
682       Arg al [2];
683       btn->time = eev->timestamp;
684       btn->button = eev->event.button.button;
685       btn->root = RootWindowOfScreen (XtScreen (daddy));
686       btn->subwindow = (Window) NULL;
687       btn->x = eev->event.button.x;
688       btn->y = eev->event.button.y;
689       shellx = shelly = 0;
690 #ifndef HAVE_WMCOMMAND
691       {
692         Widget shell = XtParent (daddy);
693
694         XtSetArg (al [0], XtNx, &shellx);
695         XtSetArg (al [1], XtNy, &shelly);
696         XtGetValues (shell, al, 2);
697       }
698 #endif
699       XtSetArg (al [0], XtNx, &framex);
700       XtSetArg (al [1], XtNy, &framey);
701       XtGetValues (daddy, al, 2);
702       btn->x_root = shellx + framex + btn->x;
703       btn->y_root = shelly + framey + btn->y;
704       btn->state = ButtonPressMask; /* all buttons pressed */
705     }
706   else
707     {
708       /* CurrentTime is just ZERO, so it's worthless for
709          determining relative click times. */
710       struct device *d = get_device_from_display (XtDisplay (daddy));
711       btn->time = DEVICE_X_MOUSE_TIMESTAMP (d); /* event-Xt maintains this */
712       btn->button = 0;
713       XQueryPointer (btn->display, btn->window, &btn->root,
714                      &btn->subwindow, &btn->x_root, &btn->y_root,
715                      &btn->x, &btn->y, &btn->state);
716     }
717 }
718
719 \f
720
721 static void
722 x_update_frame_menubar_internal (struct frame *f)
723 {
724   /* We assume the menubar contents has changed if the global flag is set,
725      or if the current buffer has changed, or if the menubar has never
726      been updated before.
727    */
728   int menubar_contents_changed =
729     (f->menubar_changed
730      || NILP (FRAME_MENUBAR_DATA (f))
731      || (!EQ (XFRAME_MENUBAR_DATA (f)->last_menubar_buffer,
732               XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer)));
733
734   Boolean menubar_was_visible = XtIsManaged (FRAME_X_MENUBAR_WIDGET (f));
735   Boolean menubar_will_be_visible = menubar_was_visible;
736   Boolean menubar_visibility_changed;
737
738   if (menubar_contents_changed)
739     menubar_will_be_visible = set_frame_menubar (f, 0, 0);
740
741   menubar_visibility_changed = menubar_was_visible != menubar_will_be_visible;
742
743   if (!menubar_visibility_changed)
744     return;
745
746   /* Set menubar visibility */
747   (menubar_will_be_visible ? XtManageChild : XtUnmanageChild)
748     (FRAME_X_MENUBAR_WIDGET (f));
749
750   MARK_FRAME_SIZE_SLIPPED (f);
751 }
752
753 static void
754 x_update_frame_menubars (struct frame *f)
755 {
756   assert (FRAME_X_P (f));
757
758   x_update_frame_menubar_internal (f);
759
760   /* #### This isn't going to work right now that this function works on
761      a per-frame, not per-device basis.  Guess what?  I don't care. */
762 }
763
764 static void
765 x_free_frame_menubars (struct frame *f)
766 {
767   Widget menubar_widget;
768
769   assert (FRAME_X_P (f));
770
771   menubar_widget = FRAME_X_MENUBAR_WIDGET (f);
772   if (menubar_widget)
773     {
774       LWLIB_ID id = XFRAME_MENUBAR_DATA (f)->id;
775       lw_destroy_all_widgets (id);
776       XFRAME_MENUBAR_DATA (f)->id = 0;
777     }
778 }
779
780 static void
781 x_popup_menu (Lisp_Object menu_desc, Lisp_Object event)
782 {
783   int menu_id;
784   struct frame *f = selected_frame ();
785   widget_value *data;
786   Widget parent;
787   Widget menu;
788   Lisp_Event *eev = NULL;
789   XEvent xev;
790   Lisp_Object frame;
791
792   XSETFRAME (frame, f);
793   CHECK_X_FRAME (frame);
794   parent = FRAME_X_SHELL_WIDGET (f);
795
796   if (!NILP (event))
797     {
798       CHECK_LIVE_EVENT (event);
799       eev= XEVENT (event);
800       if (eev->event_type != button_press_event
801           && eev->event_type != button_release_event)
802         wrong_type_argument (Qmouse_event_p, event);
803     }
804   else if (!NILP (Vthis_command_keys))
805     {
806       /* if an event wasn't passed, use the last event of the event sequence
807          currently being executed, if that event is a mouse event */
808       eev = XEVENT (Vthis_command_keys); /* last event first */
809       if (eev->event_type != button_press_event
810           && eev->event_type != button_release_event)
811         eev = NULL;
812     }
813   make_dummy_xbutton_event (&xev, parent, eev);
814
815   if (SYMBOLP (menu_desc))
816     menu_desc = Fsymbol_value (menu_desc);
817   CHECK_CONS (menu_desc);
818   CHECK_STRING (XCAR (menu_desc));
819   data = menu_item_descriptor_to_widget_value (menu_desc, POPUP_TYPE, 1, 1);
820
821   if (! data) error ("no menu");
822
823   menu_id = new_lwlib_id ();
824   menu = lw_create_widget ("popup", "popup" /* data->name */, menu_id, data,
825                            parent, 1, 0,
826                            popup_menu_selection_callback,
827                            popup_menu_down_callback);
828   free_popup_widget_value_tree (data);
829
830   gcpro_popup_callbacks (menu_id);
831
832   /* Setting zmacs-region-stays is necessary here because executing a command
833      from a menu is really a two-command process: the first command (bound to
834      the button-click) simply pops up the menu, and returns.  This causes a
835      sequence of magic-events (destined for the popup-menu widget) to begin.
836      Eventually, a menu item is selected, and a menu-event blip is pushed onto
837      the end of the input stream, which is then executed by the event loop.
838
839      So there are two command-events, with a bunch of magic-events between
840      them.  We don't want the *first* command event to alter the state of the
841      region, so that the region can be available as an argument for the second
842      command.
843   */
844   if (zmacs_regions)
845     zmacs_region_stays = 1;
846
847   popup_up_p++;
848   lw_popup_menu (menu, &xev);
849   /* this speeds up display of pop-up menus */
850   XFlush (XtDisplay (parent));
851 }
852
853 \f
854
855 #if defined(LWLIB_MENUBARS_LUCID)
856 static void
857 menu_move_up (void)
858 {
859   widget_value *current = lw_get_entries (False);
860   widget_value *entries = lw_get_entries (True);
861   widget_value *prev    = NULL;
862
863   while (entries != current)
864     {
865       if (entries->name /*&& entries->enabled*/) prev = entries;
866       entries = entries->next;
867       assert (entries);
868     }
869
870   if (!prev)
871     /* move to last item */
872     {
873       while (entries->next)
874         {
875           if (entries->name /*&& entries->enabled*/) prev = entries;
876           entries = entries->next;
877         }
878       if (prev)
879         {
880           if (entries->name /*&& entries->enabled*/)
881             prev = entries;
882         }
883       else
884         {
885           /* no selectable items in this menu, pop up to previous level */
886           lw_pop_menu ();
887           return;
888         }
889     }
890   lw_set_item (prev);
891 }
892
893 static void
894 menu_move_down (void)
895 {
896   widget_value *current = lw_get_entries (False);
897   widget_value *new = current;
898
899   while (new->next)
900     {
901       new = new->next;
902       if (new->name /*&& new->enabled*/) break;
903     }
904
905   if (new==current||!(new->name/*||new->enabled*/))
906     {
907       new = lw_get_entries (True);
908       while (new!=current)
909         {
910           if (new->name /*&& new->enabled*/) break;
911           new = new->next;
912         }
913       if (new==current&&!(new->name /*|| new->enabled*/))
914         {
915           lw_pop_menu ();
916           return;
917         }
918     }
919
920   lw_set_item (new);
921 }
922
923 static void
924 menu_move_left (void)
925 {
926   int level = lw_menu_level ();
927   int l = level;
928   widget_value *current;
929
930   while (level-- >= 3)
931     lw_pop_menu ();
932
933   menu_move_up ();
934   current = lw_get_entries (False);
935   if (l > 2 && current->contents)
936     lw_push_menu (current->contents);
937 }
938
939 static void
940 menu_move_right (void)
941 {
942   int level = lw_menu_level ();
943   int l = level;
944   widget_value *current;
945
946   while (level-- >= 3)
947     lw_pop_menu ();
948
949   menu_move_down ();
950   current = lw_get_entries (False);
951   if (l > 2 && current->contents)
952     lw_push_menu (current->contents);
953 }
954
955 static void
956 menu_select_item (widget_value *val)
957 {
958   if (val == NULL)
959     val = lw_get_entries (False);
960
961   /* is match a submenu? */
962
963   if (val->contents)
964     {
965       /* enter the submenu */
966
967       lw_set_item (val);
968       lw_push_menu (val->contents);
969     }
970   else
971     {
972       /* Execute the menu entry by calling the menu's `select'
973          callback function
974       */
975       lw_kill_menus (val);
976     }
977 }
978
979 Lisp_Object
980 command_builder_operate_menu_accelerator (struct command_builder *builder)
981 {
982   /* this function can GC */
983
984   struct console *con = XCONSOLE (Vselected_console);
985   Lisp_Object evee = builder->most_current_event;
986   Lisp_Object binding;
987   widget_value *entries;
988
989   extern int lw_menu_accelerate; /* lwlib.c */
990
991 #if 0
992   {
993     int i;
994     Lisp_Object t;
995     char buf[50];
996
997     t = builder->current_events;
998     i = 0;
999     while (!NILP (t))
1000       {
1001         i++;
1002         sprintf (buf,"OPERATE (%d): ",i);
1003         write_c_string (buf, Qexternal_debugging_output);
1004         print_internal (t, Qexternal_debugging_output, 1);
1005         write_c_string ("\n", Qexternal_debugging_output);
1006         t = XEVENT_NEXT (t);
1007       }
1008   }
1009 #endif /* 0 */
1010
1011   /* menu accelerator keys don't go into keyboard macros */
1012   if (!NILP (con->defining_kbd_macro) && NILP (Vexecuting_macro))
1013     con->kbd_macro_ptr = con->kbd_macro_end;
1014
1015   /* don't echo menu accelerator keys */
1016   /*reset_key_echo (builder, 1);*/
1017
1018   if (!lw_menu_accelerate)
1019     {
1020       /* `convert' mouse display to keyboard display
1021          by entering the open submenu
1022       */
1023       entries = lw_get_entries (False);
1024       if (entries->contents)
1025         {
1026           lw_push_menu (entries->contents);
1027           lw_display_menu (CurrentTime);
1028         }
1029     }
1030
1031   /* compare event to the current menu accelerators */
1032
1033   entries=lw_get_entries (True);
1034
1035   while (entries)
1036     {
1037       Lisp_Object accel;
1038       VOID_TO_LISP (accel, entries->accel);
1039       if (entries->name && !NILP (accel))
1040         {
1041           if (event_matches_key_specifier_p (XEVENT (evee), accel))
1042             {
1043               /* a match! */
1044
1045               menu_select_item (entries);
1046
1047               if (lw_menu_active) lw_display_menu (CurrentTime);
1048
1049               reset_this_command_keys (Vselected_console, 1);
1050               /*reset_command_builder_event_chain (builder);*/
1051               return Vmenu_accelerator_map;
1052             }
1053         }
1054       entries = entries->next;
1055     }
1056
1057   /* try to look up event in menu-accelerator-map */
1058
1059   binding = event_binding_in (evee, Vmenu_accelerator_map, 1);
1060
1061   if (NILP (binding))
1062     {
1063       /* beep at user for undefined key */
1064       return Qnil;
1065     }
1066   else
1067     {
1068       if (EQ (binding, Qmenu_quit))
1069         {
1070           /* turn off menus and set quit flag */
1071           lw_kill_menus (NULL);
1072           Vquit_flag = Qt;
1073         }
1074       else if (EQ (binding, Qmenu_up))
1075         {
1076           int level = lw_menu_level ();
1077           if (level > 2)
1078             menu_move_up ();
1079         }
1080       else if (EQ (binding, Qmenu_down))
1081         {
1082           int level = lw_menu_level ();
1083           if (level > 2)
1084             menu_move_down ();
1085           else
1086             menu_select_item (NULL);
1087         }
1088       else if (EQ (binding, Qmenu_left))
1089         {
1090           int level = lw_menu_level ();
1091           if (level > 3)
1092             {
1093               lw_pop_menu ();
1094               lw_display_menu (CurrentTime);
1095             }
1096           else
1097             menu_move_left ();
1098         }
1099       else if (EQ (binding, Qmenu_right))
1100         {
1101           int level = lw_menu_level ();
1102           if (level > 2 &&
1103               lw_get_entries (False)->contents)
1104             {
1105               widget_value *current = lw_get_entries (False);
1106               if (current->contents)
1107                 menu_select_item (NULL);
1108             }
1109           else
1110             menu_move_right ();
1111         }
1112       else if (EQ (binding, Qmenu_select))
1113         menu_select_item (NULL);
1114       else if (EQ (binding, Qmenu_escape))
1115         {
1116           int level = lw_menu_level ();
1117
1118           if (level > 2)
1119             {
1120               lw_pop_menu ();
1121               lw_display_menu (CurrentTime);
1122             }
1123           else
1124             {
1125               /* turn off menus quietly */
1126               lw_kill_menus (NULL);
1127             }
1128         }
1129       else if (KEYMAPP (binding))
1130         {
1131           /* prefix key */
1132           reset_this_command_keys (Vselected_console, 1);
1133           /*reset_command_builder_event_chain (builder);*/
1134           return binding;
1135         }
1136       else
1137         {
1138           /* turn off menus and execute binding */
1139           lw_kill_menus (NULL);
1140           reset_this_command_keys (Vselected_console, 1);
1141           /*reset_command_builder_event_chain (builder);*/
1142           return binding;
1143         }
1144     }
1145
1146   if (lw_menu_active) lw_display_menu (CurrentTime);
1147
1148   reset_this_command_keys (Vselected_console, 1);
1149   /*reset_command_builder_event_chain (builder);*/
1150
1151   return Vmenu_accelerator_map;
1152 }
1153
1154 static Lisp_Object
1155 menu_accelerator_junk_on_error (Lisp_Object errordata, Lisp_Object ignored)
1156 {
1157   Vmenu_accelerator_prefix    = Qnil;
1158   Vmenu_accelerator_modifiers = Qnil;
1159   Vmenu_accelerator_enabled   = Qnil;
1160   if (!NILP (errordata))
1161     {
1162       Lisp_Object args[2];
1163
1164       args[0] = build_string ("Error in menu accelerators (setting to nil)");
1165       /* #### This should call
1166          (with-output-to-string (display-error errordata))
1167          but that stuff is all in Lisp currently. */
1168       args[1] = errordata;
1169       warn_when_safe_lispobj
1170         (Qerror, Qwarning,
1171          emacs_doprnt_string_lisp ((const Bufbyte *) "%s: %s",
1172                                    Qnil, -1, 2, args));
1173     }
1174
1175   return Qnil;
1176 }
1177
1178 static Lisp_Object
1179 menu_accelerator_safe_compare (Lisp_Object event0)
1180 {
1181   if (CONSP (Vmenu_accelerator_prefix))
1182     {
1183       Lisp_Object t;
1184       t=Vmenu_accelerator_prefix;
1185       while (!NILP (t)
1186              && !NILP (event0)
1187              && event_matches_key_specifier_p (XEVENT (event0), Fcar (t)))
1188         {
1189           t = Fcdr (t);
1190           event0 = XEVENT_NEXT (event0);
1191         }
1192       if (!NILP (t))
1193         return Qnil;
1194     }
1195   else if (NILP (event0))
1196     return Qnil;
1197   else if (event_matches_key_specifier_p (XEVENT (event0), Vmenu_accelerator_prefix))
1198     event0 = XEVENT_NEXT (event0);
1199   else
1200     return Qnil;
1201   return event0;
1202 }
1203
1204 static Lisp_Object
1205 menu_accelerator_safe_mod_compare (Lisp_Object cons)
1206 {
1207   return (event_matches_key_specifier_p (XEVENT (XCAR (cons)), XCDR (cons))
1208           ? Qt
1209           : Qnil);
1210 }
1211
1212 Lisp_Object
1213 command_builder_find_menu_accelerator (struct command_builder *builder)
1214 {
1215   /* this function can GC */
1216   Lisp_Object event0 = builder->current_events;
1217   struct console *con = XCONSOLE (Vselected_console);
1218   struct frame *f = XFRAME (CONSOLE_SELECTED_FRAME (con));
1219   Widget menubar_widget;
1220
1221   /* compare entries in event0 against the menu prefix */
1222
1223   if ((!CONSOLE_X_P (XCONSOLE (builder->console))) || NILP (event0) ||
1224       XEVENT (event0)->event_type != key_press_event)
1225     return Qnil;
1226
1227   if (!NILP (Vmenu_accelerator_prefix))
1228     {
1229       event0 = condition_case_1 (Qerror,
1230                                  menu_accelerator_safe_compare,
1231                                  event0,
1232                                  menu_accelerator_junk_on_error,
1233                                  Qnil);
1234     }
1235
1236   if (NILP (event0))
1237     return Qnil;
1238
1239   menubar_widget = FRAME_X_MENUBAR_WIDGET (f);
1240   if (menubar_widget
1241       && CONSP (Vmenu_accelerator_modifiers))
1242     {
1243       Lisp_Object fake;
1244       Lisp_Object last = Qnil;
1245       struct gcpro gcpro1;
1246       Lisp_Object matchp;
1247
1248       widget_value *val;
1249       LWLIB_ID id = XPOPUP_DATA (f->menubar_data)->id;
1250
1251       val = lw_get_all_values (id);
1252       if (val)
1253         {
1254           val = val->contents;
1255
1256           fake = Fcopy_sequence (Vmenu_accelerator_modifiers);
1257           last = fake;
1258
1259           while (!NILP (Fcdr (last)))
1260             last = Fcdr (last);
1261
1262           Fsetcdr (last, Fcons (Qnil, Qnil));
1263           last = Fcdr (last);
1264         }
1265
1266       fake = Fcons (Qnil, fake);
1267
1268       GCPRO1 (fake);
1269
1270       while (val)
1271         {
1272           Lisp_Object accel;
1273           VOID_TO_LISP (accel, val->accel);
1274           if (val->name && !NILP (accel))
1275             {
1276               Fsetcar (last, accel);
1277               Fsetcar (fake, event0);
1278               matchp = condition_case_1 (Qerror,
1279                                          menu_accelerator_safe_mod_compare,
1280                                          fake,
1281                                          menu_accelerator_junk_on_error,
1282                                          Qnil);
1283               if (!NILP (matchp))
1284                 {
1285                   /* we found one! */
1286
1287                   lw_set_menu (menubar_widget, val);
1288                   /* yah - yet another hack.
1289                      pretend emacs timestamp is the same as an X timestamp,
1290                      which for the moment it is.  (read events.h)
1291                      */
1292                   lw_map_menu (XEVENT (event0)->timestamp);
1293
1294                   if (val->contents)
1295                     lw_push_menu (val->contents);
1296
1297                   lw_display_menu (CurrentTime);
1298
1299                   /* menu accelerator keys don't go into keyboard macros */
1300                   if (!NILP (con->defining_kbd_macro)
1301                       && NILP (Vexecuting_macro))
1302                     con->kbd_macro_ptr = con->kbd_macro_end;
1303
1304                   /* don't echo menu accelerator keys */
1305                   /*reset_key_echo (builder, 1);*/
1306                   reset_this_command_keys (Vselected_console, 1);
1307                   UNGCPRO;
1308
1309                   return Vmenu_accelerator_map;
1310                 }
1311             }
1312
1313           val = val->next;
1314         }
1315
1316       UNGCPRO;
1317     }
1318   return Qnil;
1319 }
1320
1321 int
1322 x_kludge_lw_menu_active (void)
1323 {
1324   return lw_menu_active;
1325 }
1326
1327 DEFUN ("accelerate-menu", Faccelerate_menu, 0, 0, "_", /*
1328 Make the menubar active.  Menu items can be selected using menu accelerators
1329 or by actions defined in menu-accelerator-map.
1330 */
1331        ())
1332 {
1333   struct console *con = XCONSOLE (Vselected_console);
1334   struct frame *f = XFRAME (CONSOLE_SELECTED_FRAME (con));
1335   LWLIB_ID id;
1336   widget_value *val;
1337
1338   if (NILP (f->menubar_data))
1339     error ("Frame has no menubar.");
1340
1341   id = XPOPUP_DATA (f->menubar_data)->id;
1342   val = lw_get_all_values (id);
1343   val = val->contents;
1344   lw_set_menu (FRAME_X_MENUBAR_WIDGET (f), val);
1345   lw_map_menu (CurrentTime);
1346
1347   lw_display_menu (CurrentTime);
1348
1349   /* menu accelerator keys don't go into keyboard macros */
1350   if (!NILP (con->defining_kbd_macro) && NILP (Vexecuting_macro))
1351     con->kbd_macro_ptr = con->kbd_macro_end;
1352
1353   return Qnil;
1354 }
1355 #endif /* LWLIB_MENUBARS_LUCID */
1356
1357 \f
1358 void
1359 syms_of_menubar_x (void)
1360 {
1361 #if defined(LWLIB_MENUBARS_LUCID)
1362   DEFSUBR (Faccelerate_menu);
1363 #endif
1364 }
1365
1366 void
1367 console_type_create_menubar_x (void)
1368 {
1369   CONSOLE_HAS_METHOD (x, update_frame_menubars);
1370   CONSOLE_HAS_METHOD (x, free_frame_menubars);
1371   CONSOLE_HAS_METHOD (x, popup_menu);
1372 }
1373
1374 void
1375 reinit_vars_of_menubar_x (void)
1376 {
1377   last_popup_menu_selection_callback_id = (LWLIB_ID) -1;
1378 }
1379
1380 void
1381 vars_of_menubar_x (void)
1382 {
1383   reinit_vars_of_menubar_x ();
1384
1385 #if defined (LWLIB_MENUBARS_LUCID)
1386   Fprovide (intern ("lucid-menubars"));
1387 #elif defined (LWLIB_MENUBARS_MOTIF)
1388   Fprovide (intern ("motif-menubars"));
1389 #elif defined (LWLIB_MENUBARS_ATHENA)
1390   Fprovide (intern ("athena-menubars"));
1391 #endif
1392 }