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