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