Mercurial > hg > xemacs-beta
comparison src/menubar-x.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | 080151679be2 |
comparison
equal
deleted
inserted
replaced
427:0a0253eac470 | 428:3ecd8885ac67 |
---|---|
1 /* Implements an elisp-programmable menubar -- X interface. | |
2 Copyright (C) 1993, 1994 Free Software Foundation, Inc. | |
3 Copyright (C) 1995 Tinker Systems and INS Engineering Corp. | |
4 | |
5 This file is part of XEmacs. | |
6 | |
7 XEmacs is free software; you can redistribute it and/or modify it | |
8 under the terms of the GNU General Public License as published by the | |
9 Free Software Foundation; either version 2, or (at your option) any | |
10 later version. | |
11 | |
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 for more details. | |
16 | |
17 You should have received a copy of the GNU General Public License | |
18 along with XEmacs; see the file COPYING. If not, write to | |
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
20 Boston, MA 02111-1307, USA. */ | |
21 | |
22 /* Synched up with: Not in FSF. */ | |
23 | |
24 /* created 16-dec-91 by jwz */ | |
25 | |
26 #include <config.h> | |
27 #include "lisp.h" | |
28 | |
29 #include "console-x.h" | |
30 #include "EmacsFrame.h" | |
31 #include "gui-x.h" | |
32 | |
33 #include "buffer.h" | |
34 #include "commands.h" /* zmacs_regions */ | |
35 #include "gui.h" | |
36 #include "events.h" | |
37 #include "frame.h" | |
38 #include "opaque.h" | |
39 #include "window.h" | |
40 | |
41 static int set_frame_menubar (struct frame *f, | |
42 int deep_p, | |
43 int first_time_p); | |
44 | |
45 #define FRAME_MENUBAR_DATA(frame) ((frame)->menubar_data) | |
46 #define XFRAME_MENUBAR_DATA(frame) XPOPUP_DATA ((frame)->menubar_data) | |
47 | |
48 #define MENUBAR_TYPE 0 | |
49 #define SUBMENU_TYPE 1 | |
50 #define POPUP_TYPE 2 | |
51 | |
52 | |
53 /* Converting Lisp menu tree descriptions to lwlib's `widget_value' form. | |
54 | |
55 menu_item_descriptor_to_widget_value() converts a lisp description of a | |
56 menubar into a tree of widget_value structures. It allocates widget_values | |
57 with malloc_widget_value() and allocates other storage only for the `key' | |
58 slot. All other slots are filled with pointers to Lisp_String data. We | |
59 allocate a widget_value description of the menu or menubar, and hand it to | |
60 lwlib, which then makes a copy of it, which it manages internally. We then | |
61 immediately free our widget_value tree; it will not be referenced again. | |
62 | |
63 Incremental menu construction callbacks operate just a bit differently. | |
64 They allocate widget_values and call replace_widget_value_tree() to tell | |
65 lwlib to destructively modify the incremental stub (subtree) of its | |
66 separate widget_value tree. | |
67 | |
68 This function is highly recursive (it follows the menu trees) and may call | |
69 eval. The reason we keep pointers to lisp string data instead of copying | |
70 it and freeing it later is to avoid the speed penalty that would entail | |
71 (since this needs to be fast, in the simple cases at least). (The reason | |
72 we malloc/free the keys slot is because there's not a lisp string around | |
73 for us to use in that case.) | |
74 | |
75 Since we keep pointers to lisp strings, and we call eval, we could lose if | |
76 GC relocates (or frees) those strings. It's not easy to gc protect the | |
77 strings because of the recursive nature of this function, and the fact that | |
78 it returns a data structure that gets freed later. So... we do the | |
79 sleaziest thing possible and inhibit GC for the duration. This is probably | |
80 not a big deal... | |
81 | |
82 We do not have to worry about the pointers to Lisp_String data after | |
83 this function successfully finishes. lwlib copies all such data with | |
84 strdup(). */ | |
85 | |
86 static widget_value * | |
87 menu_item_descriptor_to_widget_value_1 (Lisp_Object desc, | |
88 int menu_type, int deep_p, | |
89 int filter_p, | |
90 int depth) | |
91 { | |
92 /* This function cannot GC. | |
93 It is only called from menu_item_descriptor_to_widget_value, which | |
94 prohibits GC. */ | |
95 /* !!#### This function has not been Mule-ized */ | |
96 int menubar_root_p = (menu_type == MENUBAR_TYPE && depth == 0); | |
97 widget_value *wv; | |
98 Lisp_Object wv_closure; | |
99 int count = specpdl_depth (); | |
100 int partition_seen = 0; | |
101 | |
102 wv = xmalloc_widget_value (); | |
103 | |
104 wv_closure = make_opaque_ptr (wv); | |
105 record_unwind_protect (widget_value_unwind, wv_closure); | |
106 | |
107 if (STRINGP (desc)) | |
108 { | |
109 char *string_chars = (char *) XSTRING_DATA (desc); | |
110 wv->type = (separator_string_p (string_chars) ? SEPARATOR_TYPE : | |
111 TEXT_TYPE); | |
112 #if 1 | |
113 /* #### - should internationalize with X resources instead. | |
114 Not so! --ben */ | |
115 string_chars = GETTEXT (string_chars); | |
116 #endif | |
117 if (wv->type == SEPARATOR_TYPE) | |
118 { | |
119 wv->value = menu_separator_style (string_chars); | |
120 } | |
121 else | |
122 { | |
123 wv->name = string_chars; | |
124 wv->enabled = 1; | |
125 /* dverna Dec. 98: command_builder_operate_menu_accelerator will | |
126 manipulate the accel as a Lisp_Object if the widget has a name. | |
127 Since simple labels have a name, but no accel, we *must* set it | |
128 to nil */ | |
129 wv->accel = LISP_TO_VOID (Qnil); | |
130 } | |
131 } | |
132 else if (VECTORP (desc)) | |
133 { | |
134 Lisp_Object gui_item = gui_parse_item_keywords (desc); | |
135 if (!button_item_to_widget_value (gui_item, wv, 1, | |
136 (menu_type == MENUBAR_TYPE | |
137 && depth <= 1))) | |
138 { | |
139 /* :included form was nil */ | |
140 wv = NULL; | |
141 goto menu_item_done; | |
142 } | |
143 } | |
144 else if (CONSP (desc)) | |
145 { | |
146 Lisp_Object incremental_data = desc; | |
147 widget_value *prev = 0; | |
148 | |
149 if (STRINGP (XCAR (desc))) | |
150 { | |
151 Lisp_Object key, val; | |
152 Lisp_Object include_p = Qnil, hook_fn = Qnil, config_tag = Qnil; | |
153 Lisp_Object active_p = Qt; | |
154 Lisp_Object accel; | |
155 int included_spec = 0; | |
156 int active_spec = 0; | |
157 wv->type = CASCADE_TYPE; | |
158 wv->enabled = 1; | |
159 wv->name = (char *) XSTRING_DATA (LISP_GETTEXT (XCAR (desc))); | |
160 | |
161 accel = gui_name_accelerator (LISP_GETTEXT (XCAR (desc))); | |
162 wv->accel = LISP_TO_VOID (accel); | |
163 | |
164 desc = Fcdr (desc); | |
165 | |
166 while (key = Fcar (desc), KEYWORDP (key)) | |
167 { | |
168 Lisp_Object cascade = desc; | |
169 desc = Fcdr (desc); | |
170 if (NILP (desc)) | |
171 signal_simple_error ("Keyword in menu lacks a value", | |
172 cascade); | |
173 val = Fcar (desc); | |
174 desc = Fcdr (desc); | |
175 if (EQ (key, Q_included)) | |
176 include_p = val, included_spec = 1; | |
177 else if (EQ (key, Q_config)) | |
178 config_tag = val; | |
179 else if (EQ (key, Q_filter)) | |
180 hook_fn = val; | |
181 else if (EQ (key, Q_active)) | |
182 active_p = val, active_spec = 1; | |
183 else if (EQ (key, Q_accelerator)) | |
184 { | |
185 if ( SYMBOLP (val) | |
186 || CHARP (val)) | |
187 wv->accel = LISP_TO_VOID (val); | |
188 else | |
189 signal_simple_error ("bad keyboard accelerator", val); | |
190 } | |
191 else if (EQ (key, Q_label)) | |
192 { | |
193 /* implement in 21.2 */ | |
194 } | |
195 else | |
196 signal_simple_error ("Unknown menu cascade keyword", cascade); | |
197 } | |
198 | |
199 if ((!NILP (config_tag) | |
200 && NILP (Fmemq (config_tag, Vmenubar_configuration))) | |
201 || (included_spec && NILP (Feval (include_p)))) | |
202 { | |
203 wv = NULL; | |
204 goto menu_item_done; | |
205 } | |
206 | |
207 if (active_spec) | |
208 active_p = Feval (active_p); | |
209 | |
210 if (!NILP (hook_fn) && !NILP (active_p)) | |
211 { | |
212 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF | |
213 if (filter_p || depth == 0) | |
214 { | |
215 #endif | |
216 desc = call1_trapping_errors ("Error in menubar filter", | |
217 hook_fn, desc); | |
218 if (UNBOUNDP (desc)) | |
219 desc = Qnil; | |
220 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF | |
221 } | |
222 else | |
223 { | |
224 widget_value *incr_wv = xmalloc_widget_value (); | |
225 wv->contents = incr_wv; | |
226 incr_wv->type = INCREMENTAL_TYPE; | |
227 incr_wv->enabled = 1; | |
228 incr_wv->name = wv->name; | |
229 /* This is automatically GC protected through | |
230 the call to lw_map_widget_values(); no need | |
231 to worry. */ | |
232 incr_wv->call_data = LISP_TO_VOID (incremental_data); | |
233 goto menu_item_done; | |
234 } | |
235 #endif /* LWLIB_MENUBARS_LUCID || LWLIB_MENUBARS_MOTIF */ | |
236 } | |
237 if (menu_type == POPUP_TYPE && popup_menu_titles && depth == 0) | |
238 { | |
239 /* Simply prepend three more widget values to the contents of | |
240 the menu: a label, and two separators (to get a double | |
241 line). */ | |
242 widget_value *title_wv = xmalloc_widget_value (); | |
243 widget_value *sep_wv = xmalloc_widget_value (); | |
244 title_wv->type = TEXT_TYPE; | |
245 title_wv->name = wv->name; | |
246 title_wv->enabled = 1; | |
247 title_wv->next = sep_wv; | |
248 sep_wv->type = SEPARATOR_TYPE; | |
249 sep_wv->value = menu_separator_style ("=="); | |
250 sep_wv->next = 0; | |
251 | |
252 wv->contents = title_wv; | |
253 prev = sep_wv; | |
254 } | |
255 wv->enabled = ! NILP (active_p); | |
256 if (deep_p && !wv->enabled && !NILP (desc)) | |
257 { | |
258 widget_value *dummy; | |
259 /* Add a fake entry so the menus show up */ | |
260 wv->contents = dummy = xmalloc_widget_value (); | |
261 dummy->name = "(inactive)"; | |
262 dummy->accel = LISP_TO_VOID (Qnil); | |
263 dummy->enabled = 0; | |
264 dummy->selected = 0; | |
265 dummy->value = NULL; | |
266 dummy->type = BUTTON_TYPE; | |
267 dummy->call_data = NULL; | |
268 dummy->next = NULL; | |
269 | |
270 goto menu_item_done; | |
271 } | |
272 | |
273 } | |
274 else if (menubar_root_p) | |
275 { | |
276 wv->name = (char *) "menubar"; | |
277 wv->type = CASCADE_TYPE; /* Well, nothing else seems to fit and | |
278 this is ignored anyway... */ | |
279 } | |
280 else | |
281 { | |
282 signal_simple_error ("Menu name (first element) must be a string", | |
283 desc); | |
284 } | |
285 | |
286 if (deep_p || menubar_root_p) | |
287 { | |
288 widget_value *next; | |
289 for (; !NILP (desc); desc = Fcdr (desc)) | |
290 { | |
291 Lisp_Object child = Fcar (desc); | |
292 if (menubar_root_p && NILP (child)) /* the partition */ | |
293 { | |
294 if (partition_seen) | |
295 error ( | |
296 "More than one partition (nil) in menubar description"); | |
297 partition_seen = 1; | |
298 next = xmalloc_widget_value (); | |
299 next->type = PUSHRIGHT_TYPE; | |
300 } | |
301 else | |
302 { | |
303 next = menu_item_descriptor_to_widget_value_1 | |
304 (child, menu_type, deep_p, filter_p, depth + 1); | |
305 } | |
306 if (! next) | |
307 continue; | |
308 else if (prev) | |
309 prev->next = next; | |
310 else | |
311 wv->contents = next; | |
312 prev = next; | |
313 } | |
314 } | |
315 if (deep_p && !wv->contents) | |
316 wv = NULL; | |
317 } | |
318 else if (NILP (desc)) | |
319 error ("nil may not appear in menu descriptions"); | |
320 else | |
321 signal_simple_error ("Unrecognized menu descriptor", desc); | |
322 | |
323 menu_item_done: | |
324 | |
325 if (wv) | |
326 { | |
327 /* Completed normally. Clear out the object that widget_value_unwind() | |
328 will be called with to tell it not to free the wv (as we are | |
329 returning it.) */ | |
330 set_opaque_ptr (wv_closure, 0); | |
331 } | |
332 | |
333 unbind_to (count, Qnil); | |
334 return wv; | |
335 } | |
336 | |
337 static widget_value * | |
338 menu_item_descriptor_to_widget_value (Lisp_Object desc, | |
339 int menu_type, /* if this is a menubar, | |
340 popup or sub menu */ | |
341 int deep_p, /* */ | |
342 int filter_p) /* if :filter forms | |
343 should run now */ | |
344 { | |
345 widget_value *wv; | |
346 int count = specpdl_depth (); | |
347 record_unwind_protect (restore_gc_inhibit, | |
348 make_int (gc_currently_forbidden)); | |
349 gc_currently_forbidden = 1; | |
350 /* Can't GC! */ | |
351 wv = menu_item_descriptor_to_widget_value_1 (desc, menu_type, deep_p, | |
352 filter_p, 0); | |
353 unbind_to (count, Qnil); | |
354 return wv; | |
355 } | |
356 | |
357 | |
358 #if defined LWLIB_MENUBARS_LUCID || defined LWLIB_MENUBARS_MOTIF | |
359 int in_menu_callback; | |
360 | |
361 static Lisp_Object | |
362 restore_in_menu_callback (Lisp_Object val) | |
363 { | |
364 in_menu_callback = XINT(val); | |
365 return Qnil; | |
366 } | |
367 #endif /* LWLIB_MENUBARS_LUCID || LWLIB_MENUBARS_MOTIF */ | |
368 | |
369 #if 0 | |
370 /* #### Sort of a hack needed to process Vactivate_menubar_hook | |
371 correctly wrt buffer-local values. A correct solution would | |
372 involve adding a callback mechanism to run_hook(). This function | |
373 is currently unused. */ | |
374 static int | |
375 my_run_hook (Lisp_Object hooksym, int allow_global_p) | |
376 { | |
377 /* This function can GC */ | |
378 Lisp_Object tail; | |
379 Lisp_Object value = Fsymbol_value (hooksym); | |
380 int changes = 0; | |
381 | |
382 if (!NILP (value) && (!CONSP (value) || EQ (XCAR (value), Qlambda))) | |
383 return !EQ (call0 (value), Qt); | |
384 | |
385 EXTERNAL_LIST_LOOP (tail, value) | |
386 { | |
387 if (allow_global_p && EQ (XCAR (tail), Qt)) | |
388 changes |= my_run_hook (Fdefault_value (hooksym), 0); | |
389 if (!EQ (call0 (XCAR (tail)), Qt)) | |
390 changes = 1; | |
391 } | |
392 return changes; | |
393 } | |
394 #endif | |
395 | |
396 | |
397 /* The order in which callbacks are run is funny to say the least. | |
398 It's sometimes tricky to avoid running a callback twice, and to | |
399 avoid returning prematurely. So, this function returns true | |
400 if the menu's callbacks are no longer gc protected. So long | |
401 as we unprotect them before allowing other callbacks to run, | |
402 everything should be ok. | |
403 | |
404 The pre_activate_callback() *IS* intentionally called multiple times. | |
405 If client_data == NULL, then it's being called before the menu is posted. | |
406 If client_data != NULL, then client_data is a (widget_value *) and | |
407 client_data->data is a Lisp_Object pointing to a lisp submenu description | |
408 that must be converted into widget_values. *client_data is destructively | |
409 modified. | |
410 | |
411 #### Stig thinks that there may be a GC problem here due to the | |
412 fact that pre_activate_callback() is called multiple times, but I | |
413 think he's wrong. | |
414 | |
415 */ | |
416 | |
417 static void | |
418 pre_activate_callback (Widget widget, LWLIB_ID id, XtPointer client_data) | |
419 { | |
420 /* This function can GC */ | |
421 struct device *d = get_device_from_display (XtDisplay (widget)); | |
422 struct frame *f = x_any_window_to_frame (d, XtWindow (widget)); | |
423 Lisp_Object frame; | |
424 int count; | |
425 | |
426 /* set in lwlib to the time stamp associated with the most recent menu | |
427 operation */ | |
428 extern Time x_focus_timestamp_really_sucks_fix_me_better; | |
429 | |
430 if (!f) | |
431 f = x_any_window_to_frame (d, XtWindow (XtParent (widget))); | |
432 if (!f) | |
433 return; | |
434 | |
435 /* make sure f is the selected frame */ | |
436 XSETFRAME (frame, f); | |
437 Fselect_frame (frame); | |
438 | |
439 if (client_data) | |
440 { | |
441 /* this is an incremental menu construction callback */ | |
442 widget_value *hack_wv = (widget_value *) client_data; | |
443 Lisp_Object submenu_desc; | |
444 widget_value *wv; | |
445 | |
446 assert (hack_wv->type == INCREMENTAL_TYPE); | |
447 VOID_TO_LISP (submenu_desc, hack_wv->call_data); | |
448 | |
449 /* | |
450 * #### Fix the menu code so this isn't necessary. | |
451 * | |
452 * Protect against reentering the menu code otherwise we will | |
453 * crash later when the code gets confused at the state | |
454 * changes. | |
455 */ | |
456 count = specpdl_depth (); | |
457 record_unwind_protect (restore_in_menu_callback, | |
458 make_int (in_menu_callback)); | |
459 in_menu_callback = 1; | |
460 wv = menu_item_descriptor_to_widget_value (submenu_desc, SUBMENU_TYPE, | |
461 1, 0); | |
462 unbind_to (count, Qnil); | |
463 | |
464 if (!wv) | |
465 { | |
466 wv = xmalloc_widget_value (); | |
467 wv->type = CASCADE_TYPE; | |
468 wv->next = NULL; | |
469 wv->accel = LISP_TO_VOID (Qnil); | |
470 wv->contents = xmalloc_widget_value (); | |
471 wv->contents->type = TEXT_TYPE; | |
472 wv->contents->name = (char *) "No menu"; | |
473 wv->contents->next = NULL; | |
474 wv->contents->accel = LISP_TO_VOID (Qnil); | |
475 } | |
476 assert (wv && wv->type == CASCADE_TYPE && wv->contents); | |
477 replace_widget_value_tree (hack_wv, wv->contents); | |
478 free_popup_widget_value_tree (wv); | |
479 } | |
480 else if (!POPUP_DATAP (FRAME_MENUBAR_DATA (f))) | |
481 return; | |
482 else | |
483 { | |
484 #if 0 /* Unused, see comment below. */ | |
485 int any_changes; | |
486 | |
487 /* #### - this menubar update mechanism is expensively anti-social and | |
488 the activate-menubar-hook is now mostly obsolete. */ | |
489 any_changes = my_run_hook (Qactivate_menubar_hook, 1); | |
490 | |
491 /* #### - It is necessary to *ALWAYS* call set_frame_menubar() now that | |
492 incremental menus are implemented. If a subtree of a menu has been | |
493 updated incrementally (a destructive operation), then that subtree | |
494 must somehow be wiped. | |
495 | |
496 It is difficult to undo the destructive operation in lwlib because | |
497 a pointer back to lisp data needs to be hidden away somewhere. So | |
498 that an INCREMENTAL_TYPE widget_value can be recreated... Hmmmmm. */ | |
499 if (any_changes || | |
500 !XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date) | |
501 set_frame_menubar (f, 1, 0); | |
502 #else | |
503 run_hook (Qactivate_menubar_hook); | |
504 set_frame_menubar (f, 1, 0); | |
505 #endif | |
506 DEVICE_X_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) = | |
507 DEVICE_X_GLOBAL_MOUSE_TIMESTAMP (XDEVICE (FRAME_DEVICE (f))) = | |
508 x_focus_timestamp_really_sucks_fix_me_better; | |
509 } | |
510 } | |
511 | |
512 static widget_value * | |
513 compute_menubar_data (struct frame *f, Lisp_Object menubar, int deep_p) | |
514 { | |
515 widget_value *data; | |
516 | |
517 if (NILP (menubar)) | |
518 data = 0; | |
519 else | |
520 { | |
521 Lisp_Object old_buffer; | |
522 int count = specpdl_depth (); | |
523 | |
524 old_buffer = Fcurrent_buffer (); | |
525 record_unwind_protect (Fset_buffer, old_buffer); | |
526 Fset_buffer ( XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer); | |
527 data = menu_item_descriptor_to_widget_value (menubar, MENUBAR_TYPE, | |
528 deep_p, 0); | |
529 Fset_buffer (old_buffer); | |
530 unbind_to (count, Qnil); | |
531 } | |
532 return data; | |
533 } | |
534 | |
535 static int | |
536 set_frame_menubar (struct frame *f, int deep_p, int first_time_p) | |
537 { | |
538 widget_value *data; | |
539 Lisp_Object menubar; | |
540 int menubar_visible; | |
541 long id; | |
542 /* As for the toolbar, the minibuffer does not have its own menubar. */ | |
543 struct window *w = XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f)); | |
544 | |
545 if (! FRAME_X_P (f)) | |
546 return 0; | |
547 | |
548 /***** first compute the contents of the menubar *****/ | |
549 | |
550 if (! first_time_p) | |
551 { | |
552 /* evaluate `current-menubar' in the buffer of the selected window | |
553 of the frame in question. */ | |
554 menubar = symbol_value_in_buffer (Qcurrent_menubar, w->buffer); | |
555 } | |
556 else | |
557 { | |
558 /* That's a little tricky the first time since the frame isn't | |
559 fully initialized yet. */ | |
560 menubar = Fsymbol_value (Qcurrent_menubar); | |
561 } | |
562 | |
563 if (NILP (menubar)) | |
564 { | |
565 menubar = Vblank_menubar; | |
566 menubar_visible = 0; | |
567 } | |
568 else | |
569 menubar_visible = !NILP (w->menubar_visible_p); | |
570 | |
571 data = compute_menubar_data (f, menubar, deep_p); | |
572 if (!data || (!data->next && !data->contents)) | |
573 abort (); | |
574 | |
575 if (NILP (FRAME_MENUBAR_DATA (f))) | |
576 { | |
577 struct popup_data *mdata = | |
578 alloc_lcrecord_type (struct popup_data, &lrecord_popup_data); | |
579 | |
580 mdata->id = new_lwlib_id (); | |
581 mdata->last_menubar_buffer = Qnil; | |
582 mdata->menubar_contents_up_to_date = 0; | |
583 XSETPOPUP_DATA (FRAME_MENUBAR_DATA (f), mdata); | |
584 } | |
585 | |
586 /***** now store into the menubar widget, creating it if necessary *****/ | |
587 | |
588 id = XFRAME_MENUBAR_DATA (f)->id; | |
589 if (!FRAME_X_MENUBAR_WIDGET (f)) | |
590 { | |
591 Widget parent = FRAME_X_CONTAINER_WIDGET (f); | |
592 | |
593 assert (first_time_p); | |
594 | |
595 /* It's the first time we've mapped the menubar so compute its | |
596 contents completely once. This makes sure that the menubar | |
597 components are created with the right type. */ | |
598 if (!deep_p) | |
599 { | |
600 free_popup_widget_value_tree (data); | |
601 data = compute_menubar_data (f, menubar, 1); | |
602 } | |
603 | |
604 | |
605 FRAME_X_MENUBAR_WIDGET (f) = | |
606 lw_create_widget ("menubar", "menubar", id, data, parent, | |
607 0, pre_activate_callback, | |
608 popup_selection_callback, 0); | |
609 | |
610 } | |
611 else | |
612 { | |
613 lw_modify_all_widgets (id, data, deep_p ? True : False); | |
614 } | |
615 free_popup_widget_value_tree (data); | |
616 | |
617 XFRAME_MENUBAR_DATA (f)->menubar_contents_up_to_date = deep_p; | |
618 XFRAME_MENUBAR_DATA (f)->last_menubar_buffer = | |
619 XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer; | |
620 return menubar_visible; | |
621 } | |
622 | |
623 | |
624 /* Called from x_create_widgets() to create the initial menubar of a frame | |
625 before it is mapped, so that the window is mapped with the menubar already | |
626 there instead of us tacking it on later and thrashing the window after it | |
627 is visible. */ | |
628 int | |
629 x_initialize_frame_menubar (struct frame *f) | |
630 { | |
631 return set_frame_menubar (f, 1, 1); | |
632 } | |
633 | |
634 | |
635 static LWLIB_ID last_popup_menu_selection_callback_id; | |
636 | |
637 static void | |
638 popup_menu_selection_callback (Widget widget, LWLIB_ID id, | |
639 XtPointer client_data) | |
640 { | |
641 last_popup_menu_selection_callback_id = id; | |
642 popup_selection_callback (widget, id, client_data); | |
643 /* lw_destroy_all_widgets() will be called from popup_down_callback() */ | |
644 } | |
645 | |
646 static void | |
647 popup_menu_down_callback (Widget widget, LWLIB_ID id, XtPointer client_data) | |
648 { | |
649 if (popup_handled_p (id)) | |
650 return; | |
651 assert (popup_up_p != 0); | |
652 ungcpro_popup_callbacks (id); | |
653 popup_up_p--; | |
654 /* if this isn't called immediately after the selection callback, then | |
655 there wasn't a menu selection. */ | |
656 if (id != last_popup_menu_selection_callback_id) | |
657 popup_selection_callback (widget, id, (XtPointer) -1); | |
658 lw_destroy_all_widgets (id); | |
659 } | |
660 | |
661 | |
662 static void | |
663 make_dummy_xbutton_event (XEvent *dummy, | |
664 Widget daddy, | |
665 struct Lisp_Event *eev) | |
666 /* NULL for eev means query pointer */ | |
667 { | |
668 XButtonPressedEvent *btn = (XButtonPressedEvent *) dummy; | |
669 | |
670 btn->type = ButtonPress; | |
671 btn->serial = 0; | |
672 btn->send_event = 0; | |
673 btn->display = XtDisplay (daddy); | |
674 btn->window = XtWindow (daddy); | |
675 if (eev) | |
676 { | |
677 Position shellx, shelly, framex, framey; | |
678 Arg al [2]; | |
679 btn->time = eev->timestamp; | |
680 btn->button = eev->event.button.button; | |
681 btn->root = RootWindowOfScreen (XtScreen (daddy)); | |
682 btn->subwindow = (Window) NULL; | |
683 btn->x = eev->event.button.x; | |
684 btn->y = eev->event.button.y; | |
685 shellx = shelly = 0; | |
686 #ifndef HAVE_WMCOMMAND | |
687 { | |
688 Widget shell = XtParent (daddy); | |
689 | |
690 XtSetArg (al [0], XtNx, &shellx); | |
691 XtSetArg (al [1], XtNy, &shelly); | |
692 XtGetValues (shell, al, 2); | |
693 } | |
694 #endif | |
695 XtSetArg (al [0], XtNx, &framex); | |
696 XtSetArg (al [1], XtNy, &framey); | |
697 XtGetValues (daddy, al, 2); | |
698 btn->x_root = shellx + framex + btn->x; | |
699 btn->y_root = shelly + framey + btn->y; | |
700 btn->state = ButtonPressMask; /* all buttons pressed */ | |
701 } | |
702 else | |
703 { | |
704 /* CurrentTime is just ZERO, so it's worthless for | |
705 determining relative click times. */ | |
706 struct device *d = get_device_from_display (XtDisplay (daddy)); | |
707 btn->time = DEVICE_X_MOUSE_TIMESTAMP (d); /* event-Xt maintains this */ | |
708 btn->button = 0; | |
709 XQueryPointer (btn->display, btn->window, &btn->root, | |
710 &btn->subwindow, &btn->x_root, &btn->y_root, | |
711 &btn->x, &btn->y, &btn->state); | |
712 } | |
713 } | |
714 | |
715 | |
716 | |
717 static void | |
718 x_update_frame_menubar_internal (struct frame *f) | |
719 { | |
720 /* We assume the menubar contents has changed if the global flag is set, | |
721 or if the current buffer has changed, or if the menubar has never | |
722 been updated before. | |
723 */ | |
724 int menubar_contents_changed = | |
725 (f->menubar_changed | |
726 || NILP (FRAME_MENUBAR_DATA (f)) | |
727 || (!EQ (XFRAME_MENUBAR_DATA (f)->last_menubar_buffer, | |
728 XWINDOW (FRAME_LAST_NONMINIBUF_WINDOW (f))->buffer))); | |
729 | |
730 Boolean menubar_was_visible = XtIsManaged (FRAME_X_MENUBAR_WIDGET (f)); | |
731 Boolean menubar_will_be_visible = menubar_was_visible; | |
732 Boolean menubar_visibility_changed; | |
733 | |
734 if (menubar_contents_changed) | |
735 menubar_will_be_visible = set_frame_menubar (f, 0, 0); | |
736 | |
737 menubar_visibility_changed = menubar_was_visible != menubar_will_be_visible; | |
738 | |
739 if (!menubar_visibility_changed) | |
740 return; | |
741 | |
742 /* Set menubar visibility */ | |
743 (menubar_will_be_visible ? XtManageChild : XtUnmanageChild) | |
744 (FRAME_X_MENUBAR_WIDGET (f)); | |
745 | |
746 MARK_FRAME_SIZE_SLIPPED (f); | |
747 } | |
748 | |
749 static void | |
750 x_update_frame_menubars (struct frame *f) | |
751 { | |
752 assert (FRAME_X_P (f)); | |
753 | |
754 x_update_frame_menubar_internal (f); | |
755 | |
756 /* #### This isn't going to work right now that this function works on | |
757 a per-frame, not per-device basis. Guess what? I don't care. */ | |
758 } | |
759 | |
760 static void | |
761 x_free_frame_menubars (struct frame *f) | |
762 { | |
763 Widget menubar_widget; | |
764 | |
765 assert (FRAME_X_P (f)); | |
766 | |
767 menubar_widget = FRAME_X_MENUBAR_WIDGET (f); | |
768 if (menubar_widget) | |
769 { | |
770 LWLIB_ID id = XFRAME_MENUBAR_DATA (f)->id; | |
771 lw_destroy_all_widgets (id); | |
772 XFRAME_MENUBAR_DATA (f)->id = 0; | |
773 } | |
774 } | |
775 | |
776 static void | |
777 x_popup_menu (Lisp_Object menu_desc, Lisp_Object event) | |
778 { | |
779 int menu_id; | |
780 struct frame *f = selected_frame (); | |
781 widget_value *data; | |
782 Widget parent; | |
783 Widget menu; | |
784 struct Lisp_Event *eev = NULL; | |
785 XEvent xev; | |
786 Lisp_Object frame; | |
787 | |
788 XSETFRAME (frame, f); | |
789 CHECK_X_FRAME (frame); | |
790 parent = FRAME_X_SHELL_WIDGET (f); | |
791 | |
792 if (!NILP (event)) | |
793 { | |
794 CHECK_LIVE_EVENT (event); | |
795 eev= XEVENT (event); | |
796 if (eev->event_type != button_press_event | |
797 && eev->event_type != button_release_event) | |
798 wrong_type_argument (Qmouse_event_p, event); | |
799 } | |
800 else if (!NILP (Vthis_command_keys)) | |
801 { | |
802 /* if an event wasn't passed, use the last event of the event sequence | |
803 currently being executed, if that event is a mouse event */ | |
804 eev = XEVENT (Vthis_command_keys); /* last event first */ | |
805 if (eev->event_type != button_press_event | |
806 && eev->event_type != button_release_event) | |
807 eev = NULL; | |
808 } | |
809 make_dummy_xbutton_event (&xev, parent, eev); | |
810 | |
811 if (SYMBOLP (menu_desc)) | |
812 menu_desc = Fsymbol_value (menu_desc); | |
813 CHECK_CONS (menu_desc); | |
814 CHECK_STRING (XCAR (menu_desc)); | |
815 data = menu_item_descriptor_to_widget_value (menu_desc, POPUP_TYPE, 1, 1); | |
816 | |
817 if (! data) error ("no menu"); | |
818 | |
819 menu_id = new_lwlib_id (); | |
820 menu = lw_create_widget ("popup", "popup" /* data->name */, menu_id, data, | |
821 parent, 1, 0, | |
822 popup_menu_selection_callback, | |
823 popup_menu_down_callback); | |
824 free_popup_widget_value_tree (data); | |
825 | |
826 gcpro_popup_callbacks (menu_id); | |
827 | |
828 /* Setting zmacs-region-stays is necessary here because executing a command | |
829 from a menu is really a two-command process: the first command (bound to | |
830 the button-click) simply pops up the menu, and returns. This causes a | |
831 sequence of magic-events (destined for the popup-menu widget) to begin. | |
832 Eventually, a menu item is selected, and a menu-event blip is pushed onto | |
833 the end of the input stream, which is then executed by the event loop. | |
834 | |
835 So there are two command-events, with a bunch of magic-events between | |
836 them. We don't want the *first* command event to alter the state of the | |
837 region, so that the region can be available as an argument for the second | |
838 command. | |
839 */ | |
840 if (zmacs_regions) | |
841 zmacs_region_stays = 1; | |
842 | |
843 popup_up_p++; | |
844 lw_popup_menu (menu, &xev); | |
845 /* this speeds up display of pop-up menus */ | |
846 XFlush (XtDisplay (parent)); | |
847 } | |
848 | |
849 | |
850 void | |
851 syms_of_menubar_x (void) | |
852 { | |
853 } | |
854 | |
855 void | |
856 console_type_create_menubar_x (void) | |
857 { | |
858 CONSOLE_HAS_METHOD (x, update_frame_menubars); | |
859 CONSOLE_HAS_METHOD (x, free_frame_menubars); | |
860 CONSOLE_HAS_METHOD (x, popup_menu); | |
861 } | |
862 | |
863 void | |
864 reinit_vars_of_menubar_x (void) | |
865 { | |
866 last_popup_menu_selection_callback_id = (LWLIB_ID) -1; | |
867 } | |
868 | |
869 void | |
870 vars_of_menubar_x (void) | |
871 { | |
872 reinit_vars_of_menubar_x (); | |
873 | |
874 #if defined (LWLIB_MENUBARS_LUCID) | |
875 Fprovide (intern ("lucid-menubars")); | |
876 #elif defined (LWLIB_MENUBARS_MOTIF) | |
877 Fprovide (intern ("motif-menubars")); | |
878 #elif defined (LWLIB_MENUBARS_ATHENA) | |
879 Fprovide (intern ("athena-menubars")); | |
880 #endif | |
881 } |