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