SECURE_CODING: Use snprintf/write_fmt_str instead of sprintf
[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                         int sz, maxsz;
841
842                         if (STRINGP(suffix) && XSTRING_LENGTH(suffix)) {
843                                 maxsz = XSTRING_LENGTH(name) + 15 +
844                                         XSTRING_LENGTH(suffix);
845                                 label_buffer = alloca(maxsz);
846                                 sz = snprintf(label_buffer, maxsz, "%s %s ",
847                                               XSTRING_DATA(name),
848                                               XSTRING_DATA(suffix));
849                                 assert(sz>=0 && sz<maxsz);
850                         } else {
851                                 maxsz = XSTRING_LENGTH(name) + 15;
852                                 label_buffer = alloca(maxsz);
853                                 sz = sprintf(label_buffer, maxsz, "%s ",
854                                              XSTRING_DATA(name));
855                                 assert(sz>=0 && sz<maxsz);
856                         }
857
858                         temp_label = convert_underscores(label_buffer);
859                         main_label = gtk_xemacs_accel_label_new(NULL);
860                         accel_key =
861                             gtk_label_parse_uline(GTK_LABEL(main_label),
862                                                   temp_label);
863                         free(temp_label);
864                 }
865
866                 /* Evaluate the selected and active items now */
867                 if (selected_spec) {
868                         if (NILP(selected_p) || EQ(selected_p, Qt)) {
869                                 /* Do nothing */
870                         } else {
871                                 selected_p = Feval(selected_p);
872                         }
873                 }
874
875                 if (NILP(active_p) || EQ(active_p, Qt)) {
876                         /* Do Nothing */
877                 } else {
878                         active_p = Feval(active_p);
879                 }
880
881                 if (0 ||
882 #ifdef HAVE_MENUBARS
883                     menubar_show_keybindings
884 #endif
885                     ) {
886                         /* Need to get keybindings */
887                         if (!NILP(keys)) {
888                                 /* User-specified string to generate key bindings with */
889                                 CHECK_STRING(keys);
890
891                                 keys = Fsubstitute_command_keys(keys);
892                         } else if (SYMBOLP(callback)) {
893                                 char buf[1024];
894
895                                 /* #### Warning, dependency here on current_buffer and point */
896                                 where_is_to_char(callback, buf);
897
898                                 keys = build_string(buf);
899                         }
900                 }
901
902                 /* Now we get down to the dirty business of creating the widgets */
903                 if (NILP(style) || EQ(style, Qtext) || EQ(style, Qbutton)) {
904                         /* A normal menu item */
905                         widget = gtk_menu_item_new();
906                 } else if (EQ(style, Qtoggle) || EQ(style, Qradio)) {
907                         /* They are radio or toggle buttons.
908
909                            XEmacs' menu descriptions are fairly lame in that they do
910                            not have the idea of a 'group' of radio buttons.  They
911                            are exactly like toggle buttons except that they get
912                            drawn differently.
913
914                            GTK rips us a new one again.  If you have a radio button
915                            in a group by itself, it always draws it as highlighted.
916                            So we dummy up and create a second radio button that does
917                            not get added to the menu, but gets invisibly set/unset
918                            when the other gets unset/set.  *sigh*
919
920                          */
921                         if (EQ(style, Qradio)) {
922                                 GtkWidget *dummy_sibling = NULL;
923                                 GSList *group = NULL;
924
925                                 dummy_sibling = gtk_radio_menu_item_new(group);
926                                 group =
927                                     gtk_radio_menu_item_group
928                                     (GTK_RADIO_MENU_ITEM(dummy_sibling));
929                                 widget = gtk_radio_menu_item_new(group);
930
931                                 /* We need to notice when the 'real' one gets destroyed
932                                    so we can clean up the dummy as well. */
933                                 gtk_object_weakref(GTK_OBJECT(widget),
934                                                    (GtkDestroyNotify)
935                                                    gtk_widget_destroy,
936                                                    dummy_sibling);
937                         } else {
938                                 widget = gtk_check_menu_item_new();
939                         }
940
941                         /* What horrible defaults you have GTK dear!  The default
942                            for a toggle menu item is to not show the toggle unless it
943                            is turned on or actively highlighted.  How absolutely
944                            hideous. */
945                         gtk_check_menu_item_set_show_toggle(GTK_CHECK_MENU_ITEM
946                                                             (widget), TRUE);
947                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
948                                                        (widget),
949                                                        NILP(selected_p) ? FALSE
950                                                        : TRUE);
951                 } else {
952                         signal_simple_error_2("unknown style", style, descr);
953                 }
954
955                 gtk_widget_set_sensitive(widget, !NILP(active_p));
956
957                 gtk_signal_connect(GTK_OBJECT(widget), "activate-item",
958                                    GTK_SIGNAL_FUNC(__generic_button_callback),
959                                    LISP_TO_VOID(callback));
960
961                 gtk_signal_connect(GTK_OBJECT(widget), "activate",
962                                    GTK_SIGNAL_FUNC(__generic_button_callback),
963                                    LISP_TO_VOID(callback));
964
965                 /* Now that all the information about the menu item is know, set the
966                    remaining properties.
967                  */
968
969                 if (main_label) {
970                         gtk_container_add(GTK_CONTAINER(widget), main_label);
971
972                         gtk_misc_set_alignment(GTK_MISC(main_label), 0.0, 0.5);
973                         gtk_xemacs_set_accel_keys(GTK_XEMACS_ACCEL_LABEL
974                                                   (main_label), keys);
975
976                         if (accel_group)
977                                 gtk_widget_add_accelerator(widget,
978                                                            "activate_item",
979                                                            accel_group,
980                                                            accel_key, 0,
981                                                            GTK_ACCEL_LOCKED);
982                 }
983
984                 return (widget);
985         } else {
986                 return (NULL);
987                 /* abort (); ???? */
988         }
989 }
990
991 static GtkWidget *menu_descriptor_to_widget(Lisp_Object descr,
992                                             GtkAccelGroup * accel_group)
993 {
994         int count = specpdl_depth();
995         GtkWidget *rval = NULL;
996
997         record_unwind_protect(restore_gc_inhibit,
998                               make_int(gc_currently_forbidden));
999
1000         gc_currently_forbidden = 1;
1001
1002         /* Cannot GC from here on out... */
1003         rval = menu_descriptor_to_widget_1(descr, accel_group);
1004         unbind_to(count, Qnil);
1005         return (rval);
1006
1007 }
1008
1009 static gboolean menu_can_reuse_widget(GtkWidget * child, const char *label)
1010 {
1011         /* Everything up at the top level was done using
1012          ** gtk_xemacs_accel_label_new(), but we still double check to make
1013          ** sure we don't seriously foobar ourselves.
1014          */
1015         gpointer possible_child =
1016             g_list_nth_data(gtk_container_children(GTK_CONTAINER(child)), 0);
1017         gboolean ret_val = FALSE;
1018
1019         if (possible_child && GTK_IS_LABEL(possible_child)) {
1020                 char *temp_label = remove_underscores(label);
1021
1022                 if (!strcmp(GTK_LABEL(possible_child)->label, temp_label))
1023                         ret_val = TRUE;
1024
1025                 free(temp_label);
1026         }
1027
1028         return ret_val;
1029 }
1030
1031 /* Converts a menubar description into a GtkMenuBar... a menubar is a
1032    list of menus or buttons 
1033 */
1034 static void menu_create_menubar(struct frame *f, Lisp_Object descr)
1035 {
1036         gboolean right_justify = FALSE;
1037         Lisp_Object tail = Qnil;
1038         Lisp_Object value = descr;
1039         Lisp_Object item_descr = Qnil;
1040         GtkWidget *menubar = FRAME_GTK_MENUBAR_WIDGET(f);
1041         GUI_ID id =
1042             (GUI_ID) gtk_object_get_data(GTK_OBJECT(menubar),
1043                                          XEMACS_MENU_GUIID_TAG);
1044         guint menu_position = 0;
1045         GtkAccelGroup *menubar_accel_group;
1046
1047         /* Remove any existing protection for old menu items */
1048         ungcpro_popup_callbacks(id);
1049
1050         /* GCPRO the whole damn thing */
1051         gcpro_popup_callbacks(id, descr);
1052
1053         menubar_accel_group = gtk_accel_group_new();
1054
1055         EXTERNAL_LIST_LOOP(tail, value) {
1056                 gpointer current_child =
1057                     g_list_nth_data(GTK_MENU_SHELL(menubar)->children,
1058                                     menu_position);
1059
1060                 item_descr = XCAR(tail);
1061
1062                 if (NILP(item_descr)) {
1063                         /* Need to start right-justifying menus */
1064                         right_justify = TRUE;
1065                         menu_position--;
1066                 } else if (VECTORP(item_descr)) {
1067                         /* It is a button description */
1068                         GtkWidget *item;
1069
1070                         item =
1071                             menu_descriptor_to_widget(item_descr,
1072                                                       menubar_accel_group);
1073                         gtk_widget_set_name(item, "XEmacsMenuButton");
1074
1075                         if (!item) {
1076                                 item =
1077                                     gtk_menu_item_new_with_label
1078                                     ("ITEM CREATION ERROR");
1079                         }
1080
1081                         gtk_widget_show_all(item);
1082                         if (current_child)
1083                                 gtk_widget_destroy(GTK_WIDGET(current_child));
1084                         gtk_menu_bar_insert(GTK_MENU_BAR(menubar), item,
1085                                             menu_position);
1086                 } else if (LISTP(item_descr)) {
1087                         /* Need to actually convert it into a menu and slap it in */
1088                         GtkWidget *widget;
1089                         gboolean reused_p = FALSE;
1090
1091                         /* We may be able to reuse the widget, let's at least check. */
1092                         if (current_child
1093                             && menu_can_reuse_widget(GTK_WIDGET(current_child),
1094                                                      XSTRING_DATA(XCAR
1095                                                                   (item_descr))))
1096                         {
1097                                 widget =
1098                                     menu_convert(item_descr,
1099                                                  GTK_WIDGET(current_child),
1100                                                  menubar_accel_group);
1101                                 reused_p = TRUE;
1102                         } else {
1103                                 widget =
1104                                     menu_convert(item_descr, NULL,
1105                                                  menubar_accel_group);
1106                                 if (current_child)
1107                                         gtk_widget_destroy(GTK_WIDGET
1108                                                            (current_child));
1109                                 gtk_menu_bar_insert(GTK_MENU_BAR(menubar),
1110                                                     widget, menu_position);
1111                         }
1112
1113                         if (widget) {
1114                                 if (right_justify)
1115                                         gtk_menu_item_right_justify
1116                                             (GTK_MENU_ITEM(widget));
1117                         } else {
1118                                 widget = gtk_menu_item_new_with_label("ERROR");
1119                                 /* abort() */
1120                         }
1121                         gtk_widget_show_all(widget);
1122                 } else if (STRINGP(item_descr)) {
1123                         /* Do I really want to be this careful?  Anything else in a
1124                            menubar description is illegal */
1125                 }
1126                 menu_position++;
1127         }
1128
1129         /* Need to delete any menu items that were past the bounds of the new one */
1130         {
1131                 GList *l = NULL;
1132
1133                 while ((l =
1134                         g_list_nth(GTK_MENU_SHELL(menubar)->children,
1135                                    menu_position))) {
1136                         gpointer data = l->data;
1137                         g_list_remove_link(GTK_MENU_SHELL(menubar)->children,
1138                                            l);
1139
1140                         if (data) {
1141                                 gtk_widget_destroy(GTK_WIDGET(data));
1142                         }
1143                 }
1144         }
1145
1146         /* Attach the new accelerator group to the frame. */
1147         gtk_window_add_accel_group(GTK_WINDOW(FRAME_GTK_SHELL_WIDGET(f)),
1148                                    menubar_accel_group);
1149 }
1150 \f
1151 /* Deal with getting/setting the menubar */
1152 #ifndef GNOME_IS_APP
1153 #define GNOME_IS_APP(x) 0
1154 #define gnome_app_set_menus(x,y)
1155 #endif
1156
1157 static gboolean
1158 run_menubar_hook(GtkWidget * widget, GdkEventButton * event, gpointer user_data)
1159 {
1160         if (!GTK_MENU_SHELL(widget)->active) {
1161                 run_hook(Qactivate_menubar_hook);
1162         }
1163         return (FALSE);
1164 }
1165
1166 static void create_menubar_widget(struct frame *f)
1167 {
1168         GUI_ID id = new_gui_id();
1169         GtkWidget *handlebox = NULL;
1170         GtkWidget *menubar = gtk_xemacs_menubar_new(f);
1171
1172         if (GNOME_IS_APP(FRAME_GTK_SHELL_WIDGET(f))) {
1173                 gnome_app_set_menus(GNOME_APP(FRAME_GTK_SHELL_WIDGET(f)),
1174                                     GTK_MENU_BAR(menubar));
1175         } else if (dockable_menubar) {
1176                 handlebox = gtk_handle_box_new();
1177                 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(handlebox),
1178                                                    GTK_POS_LEFT);
1179                 gtk_container_add(GTK_CONTAINER(handlebox), menubar);
1180                 gtk_box_pack_start(GTK_BOX(FRAME_GTK_CONTAINER_WIDGET(f)),
1181                                    handlebox, FALSE, FALSE, 0);
1182         } else {
1183                 gtk_box_pack_start(GTK_BOX(FRAME_GTK_CONTAINER_WIDGET(f)),
1184                                    menubar, FALSE, FALSE, 0);
1185         }
1186
1187         gtk_signal_connect(GTK_OBJECT(menubar), "button-press-event",
1188                            GTK_SIGNAL_FUNC(run_menubar_hook), NULL);
1189
1190         FRAME_GTK_MENUBAR_WIDGET(f) = menubar;
1191         gtk_object_set_data(GTK_OBJECT(menubar), XEMACS_MENU_GUIID_TAG,
1192                             (gpointer) id);
1193         gtk_object_weakref(GTK_OBJECT(menubar), __remove_gcpro_by_id,
1194                            (gpointer) id);
1195 }
1196
1197 static int set_frame_menubar(struct frame *f, int first_time_p)
1198 {
1199         Lisp_Object menubar;
1200         int menubar_visible;
1201         /* As for the toolbar, the minibuffer does not have its own menubar. */
1202         struct window *w = XWINDOW(FRAME_LAST_NONMINIBUF_WINDOW(f));
1203
1204         if (!FRAME_GTK_P(f))
1205                 return 0;
1206
1207   /***** first compute the contents of the menubar *****/
1208
1209         if (!first_time_p) {
1210                 /* evaluate `current-menubar' in the buffer of the selected window
1211                    of the frame in question. */
1212                 menubar = symbol_value_in_buffer(Qcurrent_menubar, w->buffer);
1213         } else {
1214                 /* That's a little tricky the first time since the frame isn't
1215                    fully initialized yet. */
1216                 menubar = Fsymbol_value(Qcurrent_menubar);
1217         }
1218
1219         if (NILP(menubar)) {
1220                 menubar = Vblank_menubar;
1221                 menubar_visible = 0;
1222         } else {
1223                 menubar_visible = !NILP(w->menubar_visible_p);
1224         }
1225
1226         if (!FRAME_GTK_MENUBAR_WIDGET(f)) {
1227                 create_menubar_widget(f);
1228         }
1229
1230         /* Populate the menubar, but nothing is shown yet */
1231         {
1232                 Lisp_Object old_buffer;
1233                 int count = specpdl_depth();
1234
1235                 old_buffer = Fcurrent_buffer();
1236                 record_unwind_protect(Fset_buffer, old_buffer);
1237                 Fset_buffer(XWINDOW(FRAME_SELECTED_WINDOW(f))->buffer);
1238
1239                 menu_create_menubar(f, menubar);
1240
1241                 Fset_buffer(old_buffer);
1242                 unbind_to(count, Qnil);
1243         }
1244
1245         FRAME_MENUBAR_DATA(f) =
1246             Fcons(XWINDOW(FRAME_LAST_NONMINIBUF_WINDOW(f))->buffer, Qt);
1247
1248         return (menubar_visible);
1249 }
1250
1251 /* Called from gtk_create_widgets() to create the inital menubar of a frame
1252    before it is mapped, so that the window is mapped with the menubar already
1253    there instead of us tacking it on later and thrashing the window after it
1254    is visible. */
1255 int gtk_initialize_frame_menubar(struct frame *f)
1256 {
1257         create_menubar_widget(f);
1258         return set_frame_menubar(f, 1);
1259 }
1260 \f
1261 static void gtk_update_frame_menubar_internal(struct frame *f)
1262 {
1263         /* We assume the menubar contents has changed if the global flag is set,
1264            or if the current buffer has changed, or if the menubar has never
1265            been updated before.
1266          */
1267         int menubar_contents_changed =
1268             (f->menubar_changed || NILP(FRAME_MENUBAR_DATA(f))
1269              || (!EQ(XFRAME_MENUBAR_DATA_LASTBUFF(f),
1270                      XWINDOW(FRAME_LAST_NONMINIBUF_WINDOW(f))->buffer)));
1271
1272         gboolean menubar_was_visible =
1273             GTK_WIDGET_VISIBLE(FRAME_GTK_MENUBAR_WIDGET(f));
1274         gboolean menubar_will_be_visible = menubar_was_visible;
1275         gboolean menubar_visibility_changed;
1276
1277         if (menubar_contents_changed) {
1278                 menubar_will_be_visible = set_frame_menubar(f, 0);
1279         }
1280
1281         menubar_visibility_changed =
1282             menubar_was_visible != menubar_will_be_visible;
1283
1284         if (!menubar_visibility_changed) {
1285                 return;
1286         }
1287
1288         /* We hide and show the menubar's parent (which is actually the
1289            GtkHandleBox)... this is to simplify the code that destroys old
1290            menu items, etc.  There is no easy way to get the child out of a
1291            handle box, and I didn't want to add yet another stupid widget
1292            slot to struct gtk_frame. */
1293         if (menubar_will_be_visible) {
1294                 gtk_widget_show_all(FRAME_GTK_MENUBAR_WIDGET(f)->parent);
1295         } else {
1296                 gtk_widget_hide_all(FRAME_GTK_MENUBAR_WIDGET(f)->parent);
1297         }
1298
1299         MARK_FRAME_SIZE_SLIPPED(f);
1300 }
1301
1302 static void gtk_update_frame_menubars(struct frame *f)
1303 {
1304         GtkWidget *menubar = NULL;
1305
1306         assert(FRAME_GTK_P(f));
1307
1308         menubar = FRAME_GTK_MENUBAR_WIDGET(f);
1309
1310         if ((GTK_MENU_SHELL(menubar)->active) ||
1311             (GTK_MENU_SHELL(menubar)->have_grab) ||
1312             (GTK_MENU_SHELL(menubar)->have_xgrab)) {
1313                 return;
1314         }
1315
1316         gtk_update_frame_menubar_internal(f);
1317 }
1318
1319 static void gtk_free_frame_menubars(struct frame *f)
1320 {
1321         GtkWidget *menubar_widget;
1322
1323         assert(FRAME_GTK_P(f));
1324
1325         menubar_widget = FRAME_GTK_MENUBAR_WIDGET(f);
1326         if (menubar_widget) {
1327                 gtk_widget_destroy(menubar_widget);
1328         }
1329 }
1330
1331 static void popdown_menu_cb(GtkMenuShell * menu, gpointer user_data)
1332 {
1333         popup_up_p--;
1334 }
1335
1336 static void gtk_popup_menu(Lisp_Object menu_desc, Lisp_Object event)
1337 {
1338         struct Lisp_Event *eev = NULL;
1339         GtkWidget *widget = NULL;
1340         GtkWidget *menu = NULL;
1341         gpointer id = NULL;
1342
1343         /* Do basic error checking first... */
1344         if (SYMBOLP(menu_desc))
1345                 menu_desc = Fsymbol_value(menu_desc);
1346         CHECK_CONS(menu_desc);
1347         CHECK_STRING(XCAR(menu_desc));
1348
1349         /* Now lets get down to business... */
1350         widget = menu_descriptor_to_widget(menu_desc, NULL);
1351         menu = GTK_MENU_ITEM(widget)->submenu;
1352         gtk_widget_set_name(widget, "XEmacsPopupMenu");
1353         id = gtk_object_get_data(GTK_OBJECT(widget), XEMACS_MENU_GUIID_TAG);
1354
1355         __activate_menu(GTK_MENU_ITEM(widget), id);
1356
1357         if (!NILP(event)) {
1358                 CHECK_LIVE_EVENT(event);
1359                 eev = XEVENT(event);
1360
1361                 if ((eev->event_type != button_press_event) &&
1362                     (eev->event_type != button_release_event))
1363                         wrong_type_argument(Qmouse_event_p, event);
1364         } else if (!NILP(Vthis_command_keys)) {
1365                 /* If an event wasn't passed, use the last event of the event
1366                    sequence currently being executed, if that event is a mouse
1367                    event. */
1368                 eev = XEVENT(Vthis_command_keys);
1369                 if ((eev->event_type != button_press_event) &&
1370                     (eev->event_type != button_release_event))
1371                         eev = NULL;
1372         }
1373
1374         gtk_widget_show(menu);
1375
1376         popup_up_p++;
1377         gtk_signal_connect(GTK_OBJECT(menu), "deactivate",
1378                            GTK_SIGNAL_FUNC(popdown_menu_cb), NULL);
1379
1380         gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
1381                        eev ? eev->event.button.button : 0,
1382                        eev ? eev->timestamp : GDK_CURRENT_TIME);
1383 }
1384
1385 DEFUN("gtk-build-xemacs-menu", Fgtk_build_xemacs_menu, 1, 1, 0, /*
1386 Returns a GTK menu item from MENU, a standard XEmacs menu description.
1387 See the definition of `popup-menu' for more information on the format of MENU.
1388 */
1389       (menu))
1390 {
1391         GtkWidget *w = menu_descriptor_to_widget(menu, NULL);
1392
1393         return (w ? build_gtk_object(GTK_OBJECT(w)) : Qnil);
1394 }
1395 \f
1396 void syms_of_menubar_gtk(void)
1397 {
1398         DEFSUBR(Fgtk_build_xemacs_menu);
1399 }
1400
1401 void console_type_create_menubar_gtk(void)
1402 {
1403         CONSOLE_HAS_METHOD(gtk, update_frame_menubars);
1404         CONSOLE_HAS_METHOD(gtk, free_frame_menubars);
1405         CONSOLE_HAS_METHOD(gtk, popup_menu);
1406 }
1407
1408 void reinit_vars_of_menubar_gtk(void)
1409 {
1410         dockable_menubar = 1;
1411 #ifdef TEAR_OFF_MENUS
1412         tear_off_menus = 1;
1413 #endif
1414 }
1415
1416 void vars_of_menubar_gtk(void)
1417 {
1418         Fprovide(intern("gtk-menubars"));
1419         DEFVAR_BOOL("menubar-dockable-p", &dockable_menubar     /*
1420 If non-nil, the frame menubar can be detached into its own top-level window.
1421                                                                  */ );
1422 #ifdef TEAR_OFF_MENUS
1423         DEFVAR_BOOL("menubar-tearable-p", &tear_off_menus       /*
1424 If non-nil, menus can be torn off into their own top-level windows.
1425                                                                  */ );
1426 #endif
1427         reinit_vars_of_menubar_gtk();
1428 }