374461aee440212ecf356a7afc619e2756a0dc45
[sxemacs] / src / ui / Gtk / menubar-gtk.c
1 /* Implements an elisp-programmable menubar -- Gtk 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 SXEmacs
6
7 SXEmacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 SXEmacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
19
20
21 /* Synched up with: Not in FSF. */
22
23 /* created 16-dec-91 by jwz */
24
25 #include <config.h>
26 #include "lisp.h"
27
28 #include "console-gtk.h"
29 #include "gui-gtk.h"
30
31 #include "buffer.h"
32 #include "commands.h"           /* zmacs_regions */
33 #include "ui-gtk.h"
34 #include "ui/gui.h"
35 #include "events/events.h"
36 #include "ui/frame.h"
37 #include "opaque.h"
38 #include "ui/window.h"
39
40 #ifdef HAVE_GNOME
41 #include <libgnomeui/libgnomeui.h>
42 #endif
43
44 #define MENUBAR_TYPE    0
45 #define SUBMENU_TYPE    1
46 #define POPUP_TYPE      2
47
48 static GtkWidget *menu_descriptor_to_widget_1(Lisp_Object descr,
49                                               GtkAccelGroup * accel_group);
50
51 #define FRAME_MENUBAR_DATA(frame) ((frame)->menubar_data)
52 #define XFRAME_MENUBAR_DATA_LASTBUFF(frame) (XCAR ((frame)->menubar_data))
53 #define XFRAME_MENUBAR_DATA_UPTODATE(frame) (XCDR ((frame)->menubar_data))
54 \f
55 /* This is a bogus subclass of GtkMenuBar so that the menu never tries
56 ** to be bigger than the text widget.  This prevents weird resizing
57 ** when jumping around between buffers with radically different menu
58 ** sizes.
59 */
60
61 #define GTK_XEMACS_MENUBAR(obj)         GTK_CHECK_CAST (obj, gtk_xemacs_menubar_get_type (), GtkXEmacsMenubar)
62 #define GTK_XEMACS_MENUBAR_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_xemacs_menubar_get_type (), GtkXEmacsMenubarClass)
63 #define GTK_IS_XEMACS_MENUBAR(obj)      GTK_CHECK_TYPE (obj, gtk_xemacs_menubar_get_type ())
64 #define GTK_XEMACS_MENUBAR_FRAME(obj)   GTK_XEMACS_MENUBAR (obj)->f
65
66 typedef struct _GtkXEmacsMenubar GtkXEmacsMenubar;
67 typedef struct _GtkXEmacsMenubarClass GtkXEmacsMenubarClass;
68
69 struct _GtkXEmacsMenubar {
70         GtkMenuBar menu;
71         struct frame *frame;
72 };
73
74 struct _GtkXEmacsMenubarClass {
75         GtkMenuBarClass parent_class;
76 };
77
78 guint gtk_xemacs_menubar_get_type(void);
79 GtkWidget *gtk_xemacs_menubar_new(struct frame *f);
80
81 static void gtk_xemacs_menubar_class_init(GtkXEmacsMenubarClass * klass);
82 static void gtk_xemacs_menubar_init(GtkXEmacsMenubar * xemacs);
83 static void gtk_xemacs_menubar_size_request(GtkWidget * widget,
84                                             GtkRequisition * requisition);
85
86 guint gtk_xemacs_menubar_get_type(void)
87 {
88         static guint xemacs_menubar_type;
89
90         if (!xemacs_menubar_type) {
91                 static const GtkTypeInfo xemacs_menubar_info = {
92                         "GtkXEmacsMenubar",
93                         sizeof(GtkXEmacsMenubar),
94                         sizeof(GtkXEmacsMenubarClass),
95                         (GtkClassInitFunc) gtk_xemacs_menubar_class_init,
96                         (GtkObjectInitFunc) gtk_xemacs_menubar_init,
97                         /* reserved_1 */ NULL,
98                         /* reserved_2 */ NULL,
99                         (GtkClassInitFunc) NULL,
100                 };
101
102                 xemacs_menubar_type =
103                     gtk_type_unique(gtk_menu_bar_get_type(),
104                                     &xemacs_menubar_info);
105         }
106
107         return xemacs_menubar_type;
108 }
109
110 static GtkWidgetClass *menubar_parent_class;
111
112 static void gtk_xemacs_menubar_class_init(GtkXEmacsMenubarClass * klass)
113 {
114         GtkWidgetClass *widget_class;
115
116         widget_class = (GtkWidgetClass *) klass;
117         menubar_parent_class =
118             (GtkWidgetClass *) gtk_type_class(gtk_menu_bar_get_type());
119
120         widget_class->size_request = gtk_xemacs_menubar_size_request;
121 }
122
123 static void gtk_xemacs_menubar_init(GtkXEmacsMenubar * xemacs)
124 {
125 }
126
127 static void gtk_xemacs_menubar_size_request(GtkWidget * widget,
128                                             GtkRequisition * requisition)
129 {
130         GtkXEmacsMenubar *x = GTK_XEMACS_MENUBAR(widget);
131         GtkRequisition frame_size;
132
133         menubar_parent_class->size_request(widget, requisition);
134
135         /* #### BILL!
136          ** We should really only do this if the menu has not been detached!
137          **
138          ** WMP 9/9/2000
139          */
140
141         gtk_widget_size_request(FRAME_GTK_TEXT_WIDGET(x->frame), &frame_size);
142
143         requisition->width = frame_size.width;
144 }
145
146 GtkWidget *gtk_xemacs_menubar_new(struct frame *f)
147 {
148         GtkXEmacsMenubar *menubar = gtk_type_new(gtk_xemacs_menubar_get_type());
149
150         menubar->frame = f;
151
152         return (GTK_WIDGET(menubar));
153 }
154 \f
155 /*
156  * Label with XEmacs accelerator character support.
157  *
158  * The default interfaces to GtkAccelLabel does not understand XEmacs
159  * keystroke printing conventions, nor is it convenient in the places where is
160  * it needed.  This subclass provides an alternative interface more suited to
161  * XEmacs needs but does not add new functionality.
162  */
163 #define GTK_TYPE_XEMACS_ACCEL_LABEL            (gtk_xemacs_accel_label_get_type ())
164 #define GTK_XEMACS_ACCEL_LABEL(obj)            (GTK_CHECK_CAST ((obj), GTK_TYPE_ACCEL_LABEL, GtkXEmacsAccelLabel))
165 #define GTK_XEMACS_ACCEL_LABEL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_ACCEL_LABEL, GtkXEmacsAccelLabelClass))
166 #define GTK_IS_XEMACS_ACCEL_LABEL(obj)         (GTK_CHECK_TYPE ((obj), GTK_TYPE_XEMACS_ACCEL_LABEL))
167 #define GTK_IS_XEMACS_ACCEL_LABEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_XEMACS_ACCEL_LABEL))
168
169 typedef struct _GtkXEmacsAccelLabel GtkXEmacsAccelLabel;
170 typedef struct _GtkXEmacsAccelLabelClass GtkXEmacsAccelLabelClass;
171
172 /* Instance structure. No additional fields required. */
173 struct _GtkXEmacsAccelLabel {
174         GtkAccelLabel label;
175 };
176
177 /* Class structure. No additional fields required. */
178 struct _GtkXEmacsAccelLabelClass {
179         GtkAccelLabelClass parent_class;
180 };
181
182 static GtkType gtk_xemacs_accel_label_get_type(void);
183 static GtkWidget *gtk_xemacs_accel_label_new(const gchar * string);
184 static void gtk_xemacs_set_accel_keys(GtkXEmacsAccelLabel * l,
185                                       Lisp_Object keys);
186 static void gtk_xemacs_accel_label_class_init(GtkXEmacsAccelLabelClass * klass);
187 static void gtk_xemacs_accel_label_init(GtkXEmacsAccelLabel * xemacs);
188
189 static GtkType gtk_xemacs_accel_label_get_type(void)
190 {
191         static GtkType xemacs_accel_label_type = 0;
192
193         if (!xemacs_accel_label_type) {
194                 static const GtkTypeInfo xemacs_accel_label_info = {
195                         "GtkXEmacsAccelLabel",
196                         sizeof(GtkXEmacsAccelLabel),
197                         sizeof(GtkXEmacsAccelLabelClass),
198                         (GtkClassInitFunc) gtk_xemacs_accel_label_class_init,
199                         (GtkObjectInitFunc) gtk_xemacs_accel_label_init,
200                         /* reserved_1 */ NULL,
201                         /* reserved_2 */ NULL,
202                         (GtkClassInitFunc) NULL,
203                 };
204
205                 xemacs_accel_label_type =
206                     gtk_type_unique(gtk_accel_label_get_type(),
207                                     &xemacs_accel_label_info);
208         }
209
210         return xemacs_accel_label_type;
211 }
212
213 static void gtk_xemacs_accel_label_class_init(GtkXEmacsAccelLabelClass * klass)
214 {
215         /* Nothing to do. */
216 }
217
218 static void gtk_xemacs_accel_label_init(GtkXEmacsAccelLabel * xemacs)
219 {
220         /* Nothing to do. */
221 }
222
223 static GtkWidget *gtk_xemacs_accel_label_new(const gchar * string)
224 {
225         GtkXEmacsAccelLabel *xemacs_accel_label;
226
227         xemacs_accel_label = gtk_type_new(GTK_TYPE_XEMACS_ACCEL_LABEL);
228
229         if (string && *string)
230                 gtk_label_set_text(GTK_LABEL(xemacs_accel_label), string);
231
232         return GTK_WIDGET(xemacs_accel_label);
233 }
234
235 /* Make the string <keys> the accelerator string for the label. */
236 static void gtk_xemacs_set_accel_keys(GtkXEmacsAccelLabel * l, Lisp_Object keys)
237 {
238         g_return_if_fail(l != NULL);
239         g_return_if_fail(GTK_IS_XEMACS_ACCEL_LABEL(l));
240
241         /* Disable the standard way of finding the accelerator string for the
242            label. */
243         gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(l), NULL);
244
245         /* Set the string straight from the object. */
246         if (STRINGP(keys) && XSTRING_LENGTH(keys)) {
247                 C_STRING_TO_EXTERNAL_MALLOC(XSTRING_DATA(keys),
248                                             l->label.accel_string, Qctext);
249         } else {
250                 /* l->label.accel_string = NULL; */
251         }
252 }
253 \f
254 /* We now return you to your regularly scheduled menus... */
255
256 int dockable_menubar;
257
258 /* #define TEAR_OFF_MENUS */
259
260 #ifdef TEAR_OFF_MENUS
261 int tear_off_menus;
262 #endif
263 \f
264 /* Converting from XEmacs to GTK representation */
265 static Lisp_Object menu_name_to_accelerator(char *name)
266 {
267         while (*name) {
268                 if (*name == '%') {
269                         ++name;
270                         if (!(*name))
271                                 return Qnil;
272                         if (*name == '_' && *(name + 1)) {
273                                 int accelerator =
274                                     (int)(unsigned char)(*(name + 1));
275                                 return make_char(tolower(accelerator));
276                         }
277                 }
278                 ++name;
279         }
280         return Qnil;
281 }
282
283 #define XEMACS_MENU_DESCR_TAG "xemacs::menu::description"
284 #define XEMACS_MENU_FILTER_TAG "xemacs::menu::filter"
285 #define XEMACS_MENU_GUIID_TAG "xemacs::menu::gui_id"
286 #define XEMACS_MENU_FIRSTTIME_TAG "xemacs::menu::first_time"
287
288 static void __activate_menu(GtkMenuItem *, gpointer);
289
290 #ifdef TEAR_OFF_MENUS
291 static void __torn_off_sir(GtkMenuItem * item, gpointer user_data)
292 {
293         GtkWidget *menu_item = GTK_WIDGET(user_data);
294
295         if (GTK_TEAROFF_MENU_ITEM(item)->torn_off) {
296                 /* Menu was just torn off */
297                 GUI_ID id = new_gui_id();
298                 Lisp_Object menu_desc = Qnil;
299                 GtkWidget *old_submenu = GTK_MENU_ITEM(menu_item)->submenu;
300
301                 VOID_TO_LISP(menu_desc,
302                              gtk_object_get_data(GTK_OBJECT(menu_item),
303                                                  XEMACS_MENU_DESCR_TAG));
304
305                 /* GCPRO all of our very own */
306                 gcpro_popup_callbacks(id, menu_desc);
307
308                 /* Hide the now detached menu from the attentions of
309                    __activate_menu destroying the old submenu */
310 #if 0
311                 gtk_widget_ref(old_submenu);
312                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item),
313                                           gtk_menu_new());
314                 gtk_widget_show_all(old_submenu);
315 #endif
316         }
317 }
318 #endif
319
320 /* This is called when a menu is about to be shown... this is what
321    does the delayed creation of the menu items.  We populate the
322    submenu and away we go. */
323 static void __maybe_destroy(GtkWidget * child, GtkWidget * precious)
324 {
325         if (GTK_IS_MENU_ITEM(child) && !GTK_IS_TEAROFF_MENU_ITEM(child)) {
326                 if (GTK_WIDGET_VISIBLE(child)) {
327                         /* If we delete the menu item that was 'active' when the
328                            menu was cancelled, GTK gets upset because it tries to
329                            remove the focus rectangle from a (now) dead widget.
330
331                            This widget will eventually get killed because it will
332                            not be visible the next time the window is shown.
333                          */
334                         gtk_widget_set_sensitive(child, FALSE);
335                         gtk_widget_hide_all(child);
336                 } else {
337                         gtk_widget_destroy(child);
338                 }
339         }
340 }
341
342 /* If user_data != 0x00 then we are using a hook to build the menu. */
343 static void __activate_menu(GtkMenuItem * item, gpointer user_data)
344 {
345         Lisp_Object desc;
346         gpointer force_clear =
347             gtk_object_get_data(GTK_OBJECT(item), XEMACS_MENU_FIRSTTIME_TAG);
348
349         gtk_object_set_data(GTK_OBJECT(item), XEMACS_MENU_FIRSTTIME_TAG, 0x00);
350
351         /* Delete the old contents of the menu if we are the top level menubar */
352         if (GTK_IS_MENU_BAR(GTK_WIDGET(item)->parent) || force_clear) {
353                 GtkWidget *selected =
354                     gtk_menu_get_active(GTK_MENU(item->submenu));
355
356                 gtk_container_foreach(GTK_CONTAINER(item->submenu),
357                                       (GtkCallback) __maybe_destroy, selected);
358         } else if (gtk_container_children(GTK_CONTAINER(item->submenu))) {
359                 return;
360         }
361
362         VOID_TO_LISP(desc,
363                      gtk_object_get_data(GTK_OBJECT(item),
364                                          XEMACS_MENU_DESCR_TAG));
365
366 #ifdef TEAR_OFF_MENUS
367         /* Lets stick in a detacher just for giggles */
368         if (tear_off_menus
369             && !gtk_container_children(GTK_CONTAINER(item->submenu))) {
370                 GtkWidget *w = gtk_tearoff_menu_item_new();
371                 gtk_widget_show(w);
372                 gtk_menu_append(GTK_MENU(item->submenu), w);
373                 gtk_signal_connect(GTK_OBJECT(w), "activate",
374                                    GTK_SIGNAL_FUNC(__torn_off_sir), item);
375         }
376 #endif
377
378         if (user_data) {
379                 GUI_ID id =
380                     (GUI_ID) gtk_object_get_data(GTK_OBJECT(item),
381                                                  XEMACS_MENU_GUIID_TAG);
382                 Lisp_Object hook_fn;
383                 struct gcpro gcpro1, gcpro2;
384
385                 VOID_TO_LISP(hook_fn,
386                              gtk_object_get_data(GTK_OBJECT(item),
387                                                  XEMACS_MENU_FILTER_TAG));
388
389                 GCPRO2(desc, hook_fn);
390
391                 desc = call1(hook_fn, desc);
392
393                 UNGCPRO;
394
395                 ungcpro_popup_callbacks(id);
396                 gcpro_popup_callbacks(id, desc);
397         }
398
399         /* Build the child widgets */
400         for (; !NILP(desc); desc = Fcdr(desc)) {
401                 GtkWidget *next = NULL;
402                 Lisp_Object child = Fcar(desc);
403
404                 if (NILP(child)) {      /* the partition */
405                         /* Signal an error here?  The NILP handling is handled a
406                            layer higher where appropriate */
407                 } else {
408                         next = menu_descriptor_to_widget_1(child,
409                                                            gtk_menu_ensure_uline_accel_group
410                                                            (GTK_MENU
411                                                             (item->submenu)));
412                 }
413
414                 if (!next) {
415                         continue;
416                 }
417
418                 gtk_widget_show_all(next);
419                 gtk_menu_append(GTK_MENU(item->submenu), next);
420         }
421 }
422
423 /* This is called whenever an item with a GUI_ID associated with it is
424    destroyed.  This allows us to remove the references in gui-gtk.c
425    that made sure callbacks and such were GCPRO-ed
426 */
427 static void __remove_gcpro_by_id(gpointer user_data)
428 {
429         ungcpro_popup_callbacks((GUI_ID) user_data);
430 }
431
432 static void __kill_stupid_gtk_timer(GtkObject * obj, gpointer user_data)
433 {
434         GtkMenuItem *mi = GTK_MENU_ITEM(obj);
435
436         if (mi->timer) {
437                 gtk_timeout_remove(mi->timer);
438                 mi->timer = 0;
439         }
440 }
441
442 /* Convert the XEmacs menu accelerator representation to Gtk mnemonic form. If
443   no accelerator has been provided, put one at the start of the string (this
444   mirrors the behaviour under X).  This algorithm is also found in
445   dialog-gtk.el:gtk-popup-convert-underscores.
446 */
447 static char *convert_underscores(const char *name)
448 {
449         char *rval;
450         int i, j;
451         int found_accel = FALSE;
452         int underscores = 0;
453
454         for (i = 0; name[i]; ++i)
455                 if (name[i] == '%' && name[i + 1] == '_') {
456                         found_accel = TRUE;
457                 } else if (name[i] == '_') {
458                         underscores++;
459                 }
460
461         /* Allocate space for the original string, plus zero byte plus extra space
462            for all quoted underscores plus possible additional leading accelerator. */
463         rval = xmalloc_and_zero(strlen(name) + 1 + underscores
464                                 + (found_accel ? 0 : 1));
465
466         if (!found_accel)
467                 rval[0] = '_';
468
469         for (i = 0, j = (found_accel ? 0 : 1); name[i]; i++) {
470                 if (name[i] == '%') {
471                         i++;
472                         if (!(name[i]))
473                                 continue;
474
475                         if ((name[i] != '_') && (name[i] != '%'))
476                                 i--;
477
478                         found_accel = TRUE;
479                 } else if (name[i] == '_') {
480                         rval[j++] = '_';
481                 }
482
483                 rval[j++] = name[i];
484         }
485
486         return rval;
487 }
488
489 /* Remove the XEmacs menu accellerator representation from a string. */
490 static char *remove_underscores(const char *name)
491 {
492         char *rval = xmalloc_and_zero(strlen(name) + 1);
493         int i, j;
494
495         for (i = 0, j = 0; name[i]; i++) {
496                 if (name[i] == '%') {
497                         i++;
498                         if (!(name[i]))
499                                 continue;
500
501                         if ((name[i] != '_') && (name[i] != '%'))
502                                 i--;
503                         else
504                                 continue;
505                 }
506                 rval[j++] = name[i];
507         }
508         return rval;
509 }
510
511 /* This converts an entire menu into a GtkMenuItem (with an attached
512    submenu).  A menu is a list of (STRING [:keyword value]+ [DESCR]+)
513    DESCR is either a list (meaning a submenu), a vector, or nil (if
514    you include a :filter keyword) */
515 static GtkWidget *menu_convert(Lisp_Object desc, GtkWidget * reuse,
516                                GtkAccelGroup * menubar_accel_group)
517 {
518         GtkWidget *menu_item = NULL;
519         GtkWidget *submenu = NULL;
520         Lisp_Object key, val;
521         Lisp_Object include_p = Qnil, hook_fn = Qnil, config_tag = Qnil;
522         Lisp_Object active_p = Qt;
523         Lisp_Object accel;
524         int included_spec = 0;
525         int active_spec = 0;
526
527         if (STRINGP(XCAR(desc))) {
528                 accel = menu_name_to_accelerator(XSTRING_DATA(XCAR(desc)));
529
530                 if (!reuse) {
531                         char *temp_menu_name =
532                             convert_underscores(XSTRING_DATA(XCAR(desc)));
533                         GtkWidget *accel_label =
534                             gtk_xemacs_accel_label_new(NULL);
535                         guint accel_key;
536
537                         gtk_misc_set_alignment(GTK_MISC(accel_label), 0.0, 0.5);
538                         accel_key =
539                             gtk_label_parse_uline(GTK_LABEL(accel_label),
540                                                   temp_menu_name);
541
542                         menu_item = gtk_menu_item_new();
543                         gtk_container_add(GTK_CONTAINER(menu_item),
544                                           accel_label);
545                         gtk_widget_show(accel_label);
546
547                         if (menubar_accel_group)
548                                 gtk_widget_add_accelerator(menu_item,
549                                                            "activate_item",
550                                                            menubar_accel_group,
551                                                            accel_key,
552                                                            GDK_MOD1_MASK,
553                                                            GTK_ACCEL_LOCKED);
554                         free(temp_menu_name);
555                 } else {
556                         menu_item = reuse;
557                 }
558
559                 submenu = gtk_menu_new();
560                 gtk_widget_show(menu_item);
561                 gtk_widget_show(submenu);
562
563                 if (!reuse)
564                         gtk_signal_connect(GTK_OBJECT(menu_item), "destroy",
565                                            GTK_SIGNAL_FUNC
566                                            (__kill_stupid_gtk_timer), NULL);
567
568                 /* Without this sometimes a submenu gets left on the screen -
569                  ** urk
570                  */
571                 if (GTK_MENU_ITEM(menu_item)->submenu) {
572                         gtk_widget_destroy(GTK_MENU_ITEM(menu_item)->submenu);
573                 }
574
575                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu);
576
577                 /* We put this bogus menu item in so that GTK does the right
578                  ** thing when the menu is near the screen border.
579                  **
580                  ** Aug 29, 2000
581                  */
582                 {
583                         GtkWidget *bogus_item =
584                             gtk_menu_item_new_with_label
585                             ("A suitably long label here...");
586
587                         gtk_object_set_data(GTK_OBJECT(menu_item),
588                                             XEMACS_MENU_FIRSTTIME_TAG,
589                                             (gpointer) 0x01);
590                         gtk_widget_show_all(bogus_item);
591                         gtk_menu_append(GTK_MENU(submenu), bogus_item);
592                 }
593
594                 desc = Fcdr(desc);
595
596                 while (key = Fcar(desc), KEYWORDP(key)) {
597                         Lisp_Object cascade = desc;
598                         desc = Fcdr(desc);
599                         if (NILP(desc))
600                                 signal_simple_error
601                                     ("keyword in menu lacks a value", cascade);
602                         val = Fcar(desc);
603                         desc = Fcdr(desc);
604                         if (EQ(key, Q_included))
605                                 include_p = val, included_spec = 1;
606                         else if (EQ(key, Q_config))
607                                 config_tag = val;
608                         else if (EQ(key, Q_filter))
609                                 hook_fn = val;
610                         else if (EQ(key, Q_active))
611                                 active_p = val, active_spec = 1;
612                         else if (EQ(key, Q_accelerator)) {
613 #if 0
614                                 if (SYMBOLP(val)
615                                     || CHARP(val))
616                                         wv->accel = LISP_TO_VOID(val);
617                                 else
618                                         signal_simple_error
619                                             ("bad keyboard accelerator", val);
620 #endif
621                         } else if (EQ(key, Q_label)) {
622                                 /* implement in 21.2 */
623                         } else
624                                 signal_simple_error
625                                     ("unknown menu cascade keyword", cascade);
626                 }
627
628                 gtk_object_set_data(GTK_OBJECT(menu_item),
629                                     XEMACS_MENU_DESCR_TAG, LISP_TO_VOID(desc));
630                 gtk_object_set_data(GTK_OBJECT(menu_item),
631                                     XEMACS_MENU_FILTER_TAG,
632                                     LISP_TO_VOID(hook_fn));
633
634                 if ((!NILP(config_tag)
635                      && NILP(Fmemq(config_tag, Vmenubar_configuration)))
636                     || (included_spec && NILP(Feval(include_p)))) {
637                         return (NULL);
638                 }
639
640                 if (active_spec)
641                         active_p = Feval(active_p);
642
643                 gtk_widget_set_sensitive(GTK_WIDGET(menu_item),
644                                          !NILP(active_p));
645         } else {
646                 signal_simple_error
647                     ("menu name (first element) must be a string", desc);
648         }
649
650         /* If we are reusing a widget, we need to make sure we clean
651          ** everything up.
652          */
653         if (reuse) {
654                 gpointer id =
655                     gtk_object_get_data(GTK_OBJECT(reuse),
656                                         XEMACS_MENU_GUIID_TAG);
657
658                 if (id) {
659                         /* If the menu item had a GUI_ID that means it was a filter menu */
660                         __remove_gcpro_by_id(id);
661                         gtk_signal_disconnect_by_func(GTK_OBJECT(reuse),
662                                                       GTK_SIGNAL_FUNC
663                                                       (__activate_menu),
664                                                       (gpointer) 0x01);
665                 } else {
666                         gtk_signal_disconnect_by_func(GTK_OBJECT(reuse),
667                                                       GTK_SIGNAL_FUNC
668                                                       (__activate_menu), NULL);
669                 }
670
671                 GTK_MENU_ITEM(reuse)->right_justify = 0;
672         }
673
674         if (NILP(hook_fn)) {
675                 /* Generic menu builder */
676                 gtk_signal_connect(GTK_OBJECT(menu_item), "activate",
677                                    GTK_SIGNAL_FUNC(__activate_menu), NULL);
678         } else {
679                 GUI_ID id = new_gui_id();
680
681                 gtk_object_set_data(GTK_OBJECT(menu_item),
682                                     XEMACS_MENU_GUIID_TAG, (gpointer) id);
683
684                 /* Make sure we gcpro the menu descriptions */
685                 gcpro_popup_callbacks(id, desc);
686                 gtk_object_weakref(GTK_OBJECT(menu_item), __remove_gcpro_by_id,
687                                    (gpointer) id);
688
689                 gtk_signal_connect(GTK_OBJECT(menu_item), "activate",
690                                    GTK_SIGNAL_FUNC(__activate_menu),
691                                    (gpointer) 0x01);
692         }
693
694         return (menu_item);
695 }
696
697 /* Called whenever a button, radio, or toggle is selected in the menu */
698 static void __generic_button_callback(GtkMenuItem * item, gpointer user_data)
699 {
700         Lisp_Object callback, function, data, channel;
701
702         XSETFRAME(channel, gtk_widget_to_frame(GTK_WIDGET(item)));
703
704         VOID_TO_LISP(callback, user_data);
705
706         get_gui_callback(callback, &function, &data);
707
708         signal_special_gtk_user_event(channel, function, data);
709 }
710
711 /* Convert a single menu item descriptor to a suitable GtkMenuItem */
712 /* This function cannot GC.
713    It is only called from menu_item_descriptor_to_widget_value, which
714    prohibits GC. */
715 static GtkWidget *menu_descriptor_to_widget_1(Lisp_Object descr,
716                                               GtkAccelGroup * accel_group)
717 {
718         if (STRINGP(descr)) {
719                 /* It is a separator.  Unfortunately GTK does not allow us to
720                    specify what our separators look like, so we can't do all the
721                    fancy stuff that the X code does.
722                  */
723                 return (gtk_menu_item_new());
724         } else if (LISTP(descr)) {
725                 /* It is a submenu */
726                 return (menu_convert(descr, NULL, accel_group));
727         } else if (VECTORP(descr)) {
728                 /* An actual menu item description!  This gets yucky. */
729                 Lisp_Object name = Qnil;
730                 Lisp_Object callback = Qnil;
731                 Lisp_Object suffix = Qnil;
732                 Lisp_Object active_p = Qt;
733                 Lisp_Object include_p = Qt;
734                 Lisp_Object selected_p = Qnil;
735                 Lisp_Object keys = Qnil;
736                 Lisp_Object style = Qnil;
737                 Lisp_Object config_tag = Qnil;
738                 Lisp_Object accel = Qnil;
739                 GtkWidget *main_label = NULL;
740                 int length = XVECTOR_LENGTH(descr);
741                 Lisp_Object *contents = XVECTOR_DATA(descr);
742                 int plist_p;
743                 int selected_spec = 0, included_spec = 0;
744                 GtkWidget *widget = NULL;
745                 guint accel_key;
746
747                 if (length < 2)
748                         signal_simple_error
749                             ("button descriptors must be at least 2 long",
750                              descr);
751
752                 /* length 2:              [ "name" callback ]
753                    length 3:              [ "name" callback active-p ]
754                    length 4:              [ "name" callback active-p suffix ]
755                    or                     [ "name" callback keyword  value  ]
756                    length 5+:             [ "name" callback [ keyword value ]+ ]
757                  */
758                 plist_p = (length >= 5
759                            || (length > 2 && KEYWORDP(contents[2])));
760
761                 if (!plist_p && length > 2)
762                         /* the old way */
763                 {
764                         name = contents[0];
765                         callback = contents[1];
766                         active_p = contents[2];
767                         if (length == 4)
768                                 suffix = contents[3];
769                 } else {
770                         /* the new way */
771                         int i;
772                         if (length & 1)
773                                 signal_simple_error
774                                     ("button descriptor has an odd number of keywords and values",
775                                      descr);
776
777                         name = contents[0];
778                         callback = contents[1];
779                         for (i = 2; i < length;) {
780                                 Lisp_Object key = contents[i++];
781                                 Lisp_Object val = contents[i++];
782                                 if (!KEYWORDP(key))
783                                         signal_simple_error_2("not a keyword",
784                                                               key, descr);
785
786                                 if (EQ(key, Q_active))
787                                         active_p = val;
788                                 else if (EQ(key, Q_suffix))
789                                         suffix = val;
790                                 else if (EQ(key, Q_keys))
791                                         keys = val;
792                                 else if (EQ(key, Q_key_sequence)) ;     /* ignored for FSF compat */
793                                 else if (EQ(key, Q_label)) ;    /* implement for 21.0 */
794                                 else if (EQ(key, Q_style))
795                                         style = val;
796                                 else if (EQ(key, Q_selected))
797                                         selected_p = val, selected_spec = 1;
798                                 else if (EQ(key, Q_included))
799                                         include_p = val, included_spec = 1;
800                                 else if (EQ(key, Q_config))
801                                         config_tag = val;
802                                 else if (EQ(key, Q_accelerator)) {
803                                         if (SYMBOLP(val) || CHARP(val))
804                                                 accel = val;
805                                         else
806                                                 signal_simple_error
807                                                     ("bad keyboard accelerator",
808                                                      val);
809                                 } else if (EQ(key, Q_filter))
810                                         signal_simple_error
811                                             (":filter keyword not permitted on leaf nodes",
812                                              descr);
813                                 else
814                                         signal_simple_error_2
815                                             ("unknown menu item keyword", key,
816                                              descr);
817                         }
818                 }
819
820 #ifdef HAVE_MENUBARS
821                 if ((!NILP(config_tag)
822                      && NILP(Fmemq(config_tag, Vmenubar_configuration)))
823                     || (included_spec && NILP(Feval(include_p)))) {
824                         /* the include specification says to ignore this item. */
825                         return 0;
826                 }
827 #endif                          /* HAVE_MENUBARS */
828
829                 CHECK_STRING(name);
830
831                 if (NILP(accel))
832                         accel = menu_name_to_accelerator(XSTRING_DATA(name));
833
834                 if (!NILP(suffix))
835                         suffix = Feval(suffix);
836
837                 if (!separator_string_p(XSTRING_DATA(name))) {
838                         char *label_buffer = NULL;
839                         char *temp_label = NULL;
840
841                         if (STRINGP(suffix) && XSTRING_LENGTH(suffix)) {
842                                 label_buffer =
843                                     alloca(XSTRING_LENGTH(name) + 15 +
844                                            XSTRING_LENGTH(suffix));
845                                 sprintf(label_buffer, "%s %s ",
846                                         XSTRING_DATA(name),
847                                         XSTRING_DATA(suffix));
848                         } else {
849                                 label_buffer =
850                                     alloca(XSTRING_LENGTH(name) + 15);
851                                 sprintf(label_buffer, "%s ",
852                                         XSTRING_DATA(name));
853                         }
854
855                         temp_label = convert_underscores(label_buffer);
856                         main_label = gtk_xemacs_accel_label_new(NULL);
857                         accel_key =
858                             gtk_label_parse_uline(GTK_LABEL(main_label),
859                                                   temp_label);
860                         free(temp_label);
861                 }
862
863                 /* Evaluate the selected and active items now */
864                 if (selected_spec) {
865                         if (NILP(selected_p) || EQ(selected_p, Qt)) {
866                                 /* Do nothing */
867                         } else {
868                                 selected_p = Feval(selected_p);
869                         }
870                 }
871
872                 if (NILP(active_p) || EQ(active_p, Qt)) {
873                         /* Do Nothing */
874                 } else {
875                         active_p = Feval(active_p);
876                 }
877
878                 if (0 ||
879 #ifdef HAVE_MENUBARS
880                     menubar_show_keybindings
881 #endif
882                     ) {
883                         /* Need to get keybindings */
884                         if (!NILP(keys)) {
885                                 /* User-specified string to generate key bindings with */
886                                 CHECK_STRING(keys);
887
888                                 keys = Fsubstitute_command_keys(keys);
889                         } else if (SYMBOLP(callback)) {
890                                 char buf[1024];
891
892                                 /* #### Warning, dependency here on current_buffer and point */
893                                 where_is_to_char(callback, buf);
894
895                                 keys = build_string(buf);
896                         }
897                 }
898
899                 /* Now we get down to the dirty business of creating the widgets */
900                 if (NILP(style) || EQ(style, Qtext) || EQ(style, Qbutton)) {
901                         /* A normal menu item */
902                         widget = gtk_menu_item_new();
903                 } else if (EQ(style, Qtoggle) || EQ(style, Qradio)) {
904                         /* They are radio or toggle buttons.
905
906                            XEmacs' menu descriptions are fairly lame in that they do
907                            not have the idea of a 'group' of radio buttons.  They
908                            are exactly like toggle buttons except that they get
909                            drawn differently.
910
911                            GTK rips us a new one again.  If you have a radio button
912                            in a group by itself, it always draws it as highlighted.
913                            So we dummy up and create a second radio button that does
914                            not get added to the menu, but gets invisibly set/unset
915                            when the other gets unset/set.  *sigh*
916
917                          */
918                         if (EQ(style, Qradio)) {
919                                 GtkWidget *dummy_sibling = NULL;
920                                 GSList *group = NULL;
921
922                                 dummy_sibling = gtk_radio_menu_item_new(group);
923                                 group =
924                                     gtk_radio_menu_item_group
925                                     (GTK_RADIO_MENU_ITEM(dummy_sibling));
926                                 widget = gtk_radio_menu_item_new(group);
927
928                                 /* We need to notice when the 'real' one gets destroyed
929                                    so we can clean up the dummy as well. */
930                                 gtk_object_weakref(GTK_OBJECT(widget),
931                                                    (GtkDestroyNotify)
932                                                    gtk_widget_destroy,
933                                                    dummy_sibling);
934                         } else {
935                                 widget = gtk_check_menu_item_new();
936                         }
937
938                         /* What horrible defaults you have GTK dear!  The default
939                            for a toggle menu item is to not show the toggle unless it
940                            is turned on or actively highlighted.  How absolutely
941                            hideous. */
942                         gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM
943                                                             (widget), TRUE);
944                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
945                                                        (widget),
946                                                        NILP(selected_p) ? FALSE
947                                                        : TRUE);
948                 } else {
949                         signal_simple_error_2("unknown style", style, descr);
950                 }
951
952                 gtk_widget_set_sensitive(widget, !NILP(active_p));
953
954                 gtk_signal_connect(GTK_OBJECT(widget), "activate-item",
955                                    GTK_SIGNAL_FUNC(__generic_button_callback),
956                                    LISP_TO_VOID(callback));
957
958                 gtk_signal_connect(GTK_OBJECT(widget), "activate",
959                                    GTK_SIGNAL_FUNC(__generic_button_callback),
960                                    LISP_TO_VOID(callback));
961
962                 /* Now that all the information about the menu item is know, set the
963                    remaining properties.
964                  */
965
966                 if (main_label) {
967                         gtk_container_add(GTK_CONTAINER(widget), main_label);
968
969                         gtk_misc_set_alignment(GTK_MISC(main_label), 0.0, 0.5);
970                         gtk_xemacs_set_accel_keys(GTK_XEMACS_ACCEL_LABEL
971                                                   (main_label), keys);
972
973                         if (accel_group)
974                                 gtk_widget_add_accelerator(widget,
975                                                            "activate_item",
976                                                            accel_group,
977                                                            accel_key, 0,
978                                                            GTK_ACCEL_LOCKED);
979                 }
980
981                 return (widget);
982         } else {
983                 return (NULL);
984                 /* abort (); ???? */
985         }
986 }
987
988 static GtkWidget *menu_descriptor_to_widget(Lisp_Object descr,
989                                             GtkAccelGroup * accel_group)
990 {
991         int count = specpdl_depth();
992         GtkWidget *rval = NULL;
993
994         record_unwind_protect(restore_gc_inhibit,
995                               make_int(gc_currently_forbidden));
996
997         gc_currently_forbidden = 1;
998
999         /* Cannot GC from here on out... */
1000         rval = menu_descriptor_to_widget_1(descr, accel_group);
1001         unbind_to(count, Qnil);
1002         return (rval);
1003
1004 }
1005
1006 static gboolean menu_can_reuse_widget(GtkWidget * child, const char *label)
1007 {
1008         /* Everything up at the top level was done using
1009          ** gtk_xemacs_accel_label_new(), but we still double check to make
1010          ** sure we don't seriously foobar ourselves.
1011          */
1012         gpointer possible_child =
1013             g_list_nth_data(gtk_container_children(GTK_CONTAINER(child)), 0);
1014         gboolean ret_val = FALSE;
1015
1016         if (possible_child && GTK_IS_LABEL(possible_child)) {
1017                 char *temp_label = remove_underscores(label);
1018
1019                 if (!strcmp(GTK_LABEL(possible_child)->label, temp_label))
1020                         ret_val = TRUE;
1021
1022                 free(temp_label);
1023         }
1024
1025         return ret_val;
1026 }
1027
1028 /* Converts a menubar description into a GtkMenuBar... a menubar is a
1029    list of menus or buttons 
1030 */
1031 static void menu_create_menubar(struct frame *f, Lisp_Object descr)
1032 {
1033         gboolean right_justify = FALSE;
1034         Lisp_Object tail = Qnil;
1035         Lisp_Object value = descr;
1036         Lisp_Object item_descr = Qnil;
1037         GtkWidget *menubar = FRAME_GTK_MENUBAR_WIDGET(f);
1038         GUI_ID id =
1039             (GUI_ID) gtk_object_get_data(GTK_OBJECT(menubar),
1040                                          XEMACS_MENU_GUIID_TAG);
1041         guint menu_position = 0;
1042         GtkAccelGroup *menubar_accel_group;
1043
1044         /* Remove any existing protection for old menu items */
1045         ungcpro_popup_callbacks(id);
1046
1047         /* GCPRO the whole damn thing */
1048         gcpro_popup_callbacks(id, descr);
1049
1050         menubar_accel_group = gtk_accel_group_new();
1051
1052         EXTERNAL_LIST_LOOP(tail, value) {
1053                 gpointer current_child =
1054                     g_list_nth_data(GTK_MENU_SHELL(menubar)->children,
1055                                     menu_position);
1056
1057                 item_descr = XCAR(tail);
1058
1059                 if (NILP(item_descr)) {
1060                         /* Need to start right-justifying menus */
1061                         right_justify = TRUE;
1062                         menu_position--;
1063                 } else if (VECTORP(item_descr)) {
1064                         /* It is a button description */
1065                         GtkWidget *item;
1066
1067                         item =
1068                             menu_descriptor_to_widget(item_descr,
1069                                                       menubar_accel_group);
1070                         gtk_widget_set_name(item, "XEmacsMenuButton");
1071
1072                         if (!item) {
1073                                 item =
1074                                     gtk_menu_item_new_with_label
1075                                     ("ITEM CREATION ERROR");
1076                         }
1077
1078                         gtk_widget_show_all(item);
1079                         if (current_child)
1080                                 gtk_widget_destroy(GTK_WIDGET(current_child));
1081                         gtk_menu_bar_insert(GTK_MENU_BAR(menubar), item,
1082                                             menu_position);
1083                 } else if (LISTP(item_descr)) {
1084                         /* Need to actually convert it into a menu and slap it in */
1085                         GtkWidget *widget;
1086                         gboolean reused_p = FALSE;
1087
1088                         /* We may be able to reuse the widget, let's at least check. */
1089                         if (current_child
1090                             && menu_can_reuse_widget(GTK_WIDGET(current_child),
1091                                                      XSTRING_DATA(XCAR
1092                                                                   (item_descr))))
1093                         {
1094                                 widget =
1095                                     menu_convert(item_descr,
1096                                                  GTK_WIDGET(current_child),
1097                                                  menubar_accel_group);
1098                                 reused_p = TRUE;
1099                         } else {
1100                                 widget =
1101                                     menu_convert(item_descr, NULL,
1102                                                  menubar_accel_group);
1103                                 if (current_child)
1104                                         gtk_widget_destroy(GTK_WIDGET
1105                                                            (current_child));
1106                                 gtk_menu_bar_insert(GTK_MENU_BAR(menubar),
1107                                                     widget, menu_position);
1108                         }
1109
1110                         if (widget) {
1111                                 if (right_justify)
1112                                         gtk_menu_item_right_justify
1113                                             (GTK_MENU_ITEM(widget));
1114                         } else {
1115                                 widget = gtk_menu_item_new_with_label("ERROR");
1116                                 /* abort() */
1117                         }
1118                         gtk_widget_show_all(widget);
1119                 } else if (STRINGP(item_descr)) {
1120                         /* Do I really want to be this careful?  Anything else in a
1121                            menubar description is illegal */
1122                 }
1123                 menu_position++;
1124         }
1125
1126         /* Need to delete any menu items that were past the bounds of the new one */
1127         {
1128                 GList *l = NULL;
1129
1130                 while ((l =
1131                         g_list_nth(GTK_MENU_SHELL(menubar)->children,
1132                                    menu_position))) {
1133                         gpointer data = l->data;
1134                         g_list_remove_link(GTK_MENU_SHELL(menubar)->children,
1135                                            l);
1136
1137                         if (data) {
1138                                 gtk_widget_destroy(GTK_WIDGET(data));
1139                         }
1140                 }
1141         }
1142
1143         /* Attach the new accelerator group to the frame. */
1144         gtk_window_add_accel_group(GTK_WINDOW(FRAME_GTK_SHELL_WIDGET(f)),
1145                                    menubar_accel_group);
1146 }
1147 \f
1148 /* Deal with getting/setting the menubar */
1149 #ifndef GNOME_IS_APP
1150 #define GNOME_IS_APP(x) 0
1151 #define gnome_app_set_menus(x,y)
1152 #endif
1153
1154 static gboolean
1155 run_menubar_hook(GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1156 {
1157         if (!GTK_MENU_SHELL(widget)->active) {
1158                 run_hook(Qactivate_menubar_hook);
1159         }
1160         return (FALSE);
1161 }
1162
1163 static void create_menubar_widget(struct frame *f)
1164 {
1165         GUI_ID id = new_gui_id();
1166         GtkWidget *handlebox = NULL;
1167         GtkWidget *menubar = gtk_xemacs_menubar_new(f);
1168
1169         if (GNOME_IS_APP(FRAME_GTK_SHELL_WIDGET(f))) {
1170                 gnome_app_set_menus(GNOME_APP(FRAME_GTK_SHELL_WIDGET(f)),
1171                                     GTK_MENU_BAR(menubar));
1172         } else if (dockable_menubar) {
1173                 handlebox = gtk_handle_box_new();
1174                 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(handlebox),
1175                                                    GTK_POS_LEFT);
1176                 gtk_container_add(GTK_CONTAINER(handlebox), menubar);
1177                 gtk_box_pack_start(GTK_BOX(FRAME_GTK_CONTAINER_WIDGET(f)),
1178                                    handlebox, FALSE, FALSE, 0);
1179         } else {
1180                 gtk_box_pack_start(GTK_BOX(FRAME_GTK_CONTAINER_WIDGET(f)),
1181                                    menubar, FALSE, FALSE, 0);
1182         }
1183
1184         gtk_signal_connect(GTK_OBJECT(menubar), "button-press-event",
1185                            GTK_SIGNAL_FUNC(run_menubar_hook), NULL);
1186
1187         FRAME_GTK_MENUBAR_WIDGET(f) = menubar;
1188         gtk_object_set_data(GTK_OBJECT(menubar), XEMACS_MENU_GUIID_TAG,
1189                             (gpointer) id);
1190         gtk_object_weakref(GTK_OBJECT(menubar), __remove_gcpro_by_id,
1191                            (gpointer) id);
1192 }
1193
1194 static int set_frame_menubar(struct frame *f, int first_time_p)
1195 {
1196         Lisp_Object menubar;
1197         int menubar_visible;
1198         /* As for the toolbar, the minibuffer does not have its own menubar. */
1199         struct window *w = XWINDOW(FRAME_LAST_NONMINIBUF_WINDOW(f));
1200
1201         if (!FRAME_GTK_P(f))
1202                 return 0;
1203
1204   /***** first compute the contents of the menubar *****/
1205
1206         if (!first_time_p) {
1207                 /* evaluate `current-menubar' in the buffer of the selected window
1208                    of the frame in question. */
1209                 menubar = symbol_value_in_buffer(Qcurrent_menubar, w->buffer);
1210         } else {
1211                 /* That's a little tricky the first time since the frame isn't
1212                    fully initialized yet. */
1213                 menubar = Fsymbol_value(Qcurrent_menubar);
1214         }
1215
1216         if (NILP(menubar)) {
1217                 menubar = Vblank_menubar;
1218                 menubar_visible = 0;
1219         } else {
1220                 menubar_visible = !NILP(w->menubar_visible_p);
1221         }
1222
1223         if (!FRAME_GTK_MENUBAR_WIDGET(f)) {
1224                 create_menubar_widget(f);
1225         }
1226
1227         /* Populate the menubar, but nothing is shown yet */
1228         {
1229                 Lisp_Object old_buffer;
1230                 int count = specpdl_depth();
1231
1232                 old_buffer = Fcurrent_buffer();
1233                 record_unwind_protect(Fset_buffer, old_buffer);
1234                 Fset_buffer(XWINDOW(FRAME_SELECTED_WINDOW(f))->buffer);
1235
1236                 menu_create_menubar(f, menubar);
1237
1238                 Fset_buffer(old_buffer);
1239                 unbind_to(count, Qnil);
1240         }
1241
1242         FRAME_MENUBAR_DATA(f) =
1243             Fcons(XWINDOW(FRAME_LAST_NONMINIBUF_WINDOW(f))->buffer, Qt);
1244
1245         return (menubar_visible);
1246 }
1247
1248 /* Called from gtk_create_widgets() to create the inital menubar of a frame
1249    before it is mapped, so that the window is mapped with the menubar already
1250    there instead of us tacking it on later and thrashing the window after it
1251    is visible. */
1252 int gtk_initialize_frame_menubar(struct frame *f)
1253 {
1254         create_menubar_widget(f);
1255         return set_frame_menubar(f, 1);
1256 }
1257 \f
1258 static void gtk_update_frame_menubar_internal(struct frame *f)
1259 {
1260         /* We assume the menubar contents has changed if the global flag is set,
1261            or if the current buffer has changed, or if the menubar has never
1262            been updated before.
1263          */
1264         int menubar_contents_changed =
1265             (f->menubar_changed || NILP(FRAME_MENUBAR_DATA(f))
1266              || (!EQ(XFRAME_MENUBAR_DATA_LASTBUFF(f),
1267                      XWINDOW(FRAME_LAST_NONMINIBUF_WINDOW(f))->buffer)));
1268
1269         gboolean menubar_was_visible =
1270             GTK_WIDGET_VISIBLE(FRAME_GTK_MENUBAR_WIDGET(f));
1271         gboolean menubar_will_be_visible = menubar_was_visible;
1272         gboolean menubar_visibility_changed;
1273
1274         if (menubar_contents_changed) {
1275                 menubar_will_be_visible = set_frame_menubar(f, 0);
1276         }
1277
1278         menubar_visibility_changed =
1279             menubar_was_visible != menubar_will_be_visible;
1280
1281         if (!menubar_visibility_changed) {
1282                 return;
1283         }
1284
1285         /* We hide and show the menubar's parent (which is actually the
1286            GtkHandleBox)... this is to simplify the code that destroys old
1287            menu items, etc.  There is no easy way to get the child out of a
1288            handle box, and I didn't want to add yet another stupid widget
1289            slot to struct gtk_frame. */
1290         if (menubar_will_be_visible) {
1291                 gtk_widget_show_all(FRAME_GTK_MENUBAR_WIDGET(f)->parent);
1292         } else {
1293                 gtk_widget_hide_all(FRAME_GTK_MENUBAR_WIDGET(f)->parent);
1294         }
1295
1296         MARK_FRAME_SIZE_SLIPPED(f);
1297 }
1298
1299 static void gtk_update_frame_menubars(struct frame *f)
1300 {
1301         GtkWidget *menubar = NULL;
1302
1303         assert(FRAME_GTK_P(f));
1304
1305         menubar = FRAME_GTK_MENUBAR_WIDGET(f);
1306
1307         if ((GTK_MENU_SHELL(menubar)->active) ||
1308             (GTK_MENU_SHELL(menubar)->have_grab) ||
1309             (GTK_MENU_SHELL(menubar)->have_xgrab)) {
1310                 return;
1311         }
1312
1313         gtk_update_frame_menubar_internal(f);
1314 }
1315
1316 static void gtk_free_frame_menubars(struct frame *f)
1317 {
1318         GtkWidget *menubar_widget;
1319
1320         assert(FRAME_GTK_P(f));
1321
1322         menubar_widget = FRAME_GTK_MENUBAR_WIDGET(f);
1323         if (menubar_widget) {
1324                 gtk_widget_destroy(menubar_widget);
1325         }
1326 }
1327
1328 static void popdown_menu_cb(GtkMenuShell * menu, gpointer user_data)
1329 {
1330         popup_up_p--;
1331 }
1332
1333 static void gtk_popup_menu(Lisp_Object menu_desc, Lisp_Object event)
1334 {
1335         struct Lisp_Event *eev = NULL;
1336         GtkWidget *widget = NULL;
1337         GtkWidget *menu = NULL;
1338         gpointer id = NULL;
1339
1340         /* Do basic error checking first... */
1341         if (SYMBOLP(menu_desc))
1342                 menu_desc = Fsymbol_value(menu_desc);
1343         CHECK_CONS(menu_desc);
1344         CHECK_STRING(XCAR(menu_desc));
1345
1346         /* Now lets get down to business... */
1347         widget = menu_descriptor_to_widget(menu_desc, NULL);
1348         menu = GTK_MENU_ITEM(widget)->submenu;
1349         gtk_widget_set_name(widget, "XEmacsPopupMenu");
1350         id = gtk_object_get_data(GTK_OBJECT(widget), XEMACS_MENU_GUIID_TAG);
1351
1352         __activate_menu(GTK_MENU_ITEM(widget), id);
1353
1354         if (!NILP(event)) {
1355                 CHECK_LIVE_EVENT(event);
1356                 eev = XEVENT(event);
1357
1358                 if ((eev->event_type != button_press_event) &&
1359                     (eev->event_type != button_release_event))
1360                         wrong_type_argument(Qmouse_event_p, event);
1361         } else if (!NILP(Vthis_command_keys)) {
1362                 /* If an event wasn't passed, use the last event of the event
1363                    sequence currently being executed, if that event is a mouse
1364                    event. */
1365                 eev = XEVENT(Vthis_command_keys);
1366                 if ((eev->event_type != button_press_event) &&
1367                     (eev->event_type != button_release_event))
1368                         eev = NULL;
1369         }
1370
1371         gtk_widget_show(menu);
1372
1373         popup_up_p++;
1374         gtk_signal_connect(GTK_OBJECT(menu), "deactivate",
1375                            GTK_SIGNAL_FUNC(popdown_menu_cb), NULL);
1376
1377         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
1378                        eev ? eev->event.button.button : 0,
1379                        eev ? eev->timestamp : GDK_CURRENT_TIME);
1380 }
1381
1382 DEFUN("gtk-build-xemacs-menu", Fgtk_build_xemacs_menu, 1, 1, 0, /*
1383 Returns a GTK menu item from MENU, a standard XEmacs menu description.
1384 See the definition of `popup-menu' for more information on the format of MENU.
1385 */
1386       (menu))
1387 {
1388         GtkWidget *w = menu_descriptor_to_widget(menu, NULL);
1389
1390         return (w ? build_gtk_object(GTK_OBJECT(w)) : Qnil);
1391 }
1392 \f
1393 void syms_of_menubar_gtk(void)
1394 {
1395         DEFSUBR(Fgtk_build_xemacs_menu);
1396 }
1397
1398 void console_type_create_menubar_gtk(void)
1399 {
1400         CONSOLE_HAS_METHOD(gtk, update_frame_menubars);
1401         CONSOLE_HAS_METHOD(gtk, free_frame_menubars);
1402         CONSOLE_HAS_METHOD(gtk, popup_menu);
1403 }
1404
1405 void reinit_vars_of_menubar_gtk(void)
1406 {
1407         dockable_menubar = 1;
1408 #ifdef TEAR_OFF_MENUS
1409         tear_off_menus = 1;
1410 #endif
1411 }
1412
1413 void vars_of_menubar_gtk(void)
1414 {
1415         Fprovide(intern("gtk-menubars"));
1416         DEFVAR_BOOL("menubar-dockable-p", &dockable_menubar     /*
1417 If non-nil, the frame menubar can be detached into its own top-level window.
1418                                                                  */ );
1419 #ifdef TEAR_OFF_MENUS
1420         DEFVAR_BOOL("menubar-tearable-p", &tear_off_menus       /*
1421 If non-nil, menus can be torn off into their own top-level windows.
1422                                                                  */ );
1423 #endif
1424         reinit_vars_of_menubar_gtk();
1425 }