Mercurial > hg > xemacs-beta
annotate src/menubar-gtk.c @ 5602:c9e5612f5424
Support the MP library on recent FreeBSD, have it pass relevant tests.
src/ChangeLog addition:
2011-11-26 Aidan Kehoe <kehoea@parhasard.net>
* number-mp.c (bignum_to_string):
Don't overwrite the accumulator we've just set up for this
function.
* number-mp.c (BIGNUM_TO_TYPE):
mp_itom() doesn't necessarily do what this code used to think with
negative numbers, it can treat them as unsigned ints. Subtract
numbers from bignum_zero instead of multiplying them by -1 to
convert them to their negative equivalents.
* number-mp.c (bignum_to_int):
* number-mp.c (bignum_to_uint):
* number-mp.c (bignum_to_long):
* number-mp.c (bignum_to_ulong):
* number-mp.c (bignum_to_double):
Use the changed BIGNUM_TO_TYPE() in these functions.
* number-mp.c (bignum_ceil):
* number-mp.c (bignum_floor):
In these functions, be more careful about rounding to positive and
negative infinity, respectively. Don't use the sign of QUOTIENT
when working out out whether to add or subtract one, rather use
the sign QUOTIENT would have if arbitrary-precision division were
done.
* number-mp.h:
* number-mp.h (MP_GCD):
Wrap #include <mp.h> in BEGIN_C_DECLS/END_C_DECLS.
* number.c (Fbigfloat_get_precision):
* number.c (Fbigfloat_set_precision):
Don't attempt to call XBIGFLOAT_GET_PREC if this build doesn't
support big floats.
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Sat, 26 Nov 2011 17:59:14 +0000 |
parents | 4dee0387b9de |
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 |