Mercurial > hg > xemacs-beta
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 } |