comparison src/menubar-gtk.c @ 462:0784d089fdc9 r21-2-46

Import from CVS: tag r21-2-46
author cvs
date Mon, 13 Aug 2007 11:44:37 +0200
parents
children 183866b06e0b
comparison
equal deleted inserted replaced
461:120ed4009e51 462:0784d089fdc9
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
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
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
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
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
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
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 }