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