(xmenu_show): Enable asynchronous events.
[bpt/emacs.git] / src / xmenu.c
1 /* X Communication module for terminals which understand the X protocol.
2 Copyright (C) 1986, 1988, 1993, 1994 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20 /* X pop-up deck-of-cards menu facility for gnuemacs.
21 *
22 * Written by Jon Arnold and Roman Budzianowski
23 * Mods and rewrite by Robert Krawitz
24 *
25 */
26
27 /* Modified by Fred Pierresteguy on December 93
28 to make the popup menus and menubar use the Xt. */
29
30 /* Rewritten for clarity and GC protection by rms in Feb 94. */
31
32 #include <stdio.h>
33
34 /* On 4.3 this loses if it comes after xterm.h. */
35 #include <signal.h>
36 #include <config.h>
37 #include "lisp.h"
38 #include "termhooks.h"
39 #include "frame.h"
40 #include "window.h"
41 #include "keyboard.h"
42 #include "blockinput.h"
43
44 /* This may include sys/types.h, and that somehow loses
45 if this is not done before the other system files. */
46 #include "xterm.h"
47
48 /* Load sys/types.h if not already loaded.
49 In some systems loading it twice is suicidal. */
50 #ifndef makedev
51 #include <sys/types.h>
52 #endif
53
54 #include "dispextern.h"
55
56 #ifdef HAVE_X11
57 #include "../oldXMenu/XMenu.h"
58 #else
59 #include <X/XMenu.h>
60 #endif
61
62 #ifdef USE_X_TOOLKIT
63 #include <X11/Xlib.h>
64 #include <X11/IntrinsicP.h>
65 #include <X11/CoreP.h>
66 #include <X11/StringDefs.h>
67 #include <X11/Xaw/Paned.h>
68 #include "../lwlib/lwlib.h"
69 #include "../lwlib/xlwmenuP.h"
70 #endif /* USE_X_TOOLKIT */
71
72 #define min(x,y) (((x) < (y)) ? (x) : (y))
73 #define max(x,y) (((x) > (y)) ? (x) : (y))
74
75 #ifndef TRUE
76 #define TRUE 1
77 #define FALSE 0
78 #endif /* no TRUE */
79
80 #ifdef HAVE_X11
81 extern Display *x_current_display;
82 #else
83 #define ButtonReleaseMask ButtonReleased
84 #endif /* not HAVE_X11 */
85
86 /* We need a unique id for each popup menu and dialog box. */
87 static unsigned int popup_id_tick;
88
89 extern Lisp_Object Qmenu_enable;
90 extern Lisp_Object Qmenu_bar;
91
92 #ifdef USE_X_TOOLKIT
93 extern void process_expose_from_menu ();
94 extern XtAppContext Xt_app_con;
95
96 static int string_width ();
97 static Lisp_Object xdialog_show ();
98 #endif
99
100 static Lisp_Object xmenu_show ();
101 static void keymap_panes ();
102 static void single_keymap_panes ();
103 static void list_of_panes ();
104 static void list_of_items ();
105 \f
106 /* This holds a Lisp vector that holds the results of decoding
107 the keymaps or alist-of-alists that specify a menu.
108
109 It describes the panes and items within the panes.
110
111 Each pane is described by 3 elements in the vector:
112 t, the pane name, the pane's prefix key.
113 Then follow the pane's items, with 4 elements per item:
114 the item string, the enable flag, the item's value,
115 and the equivalent keyboard key's description string.
116
117 In some cases, multiple levels of menus may be described.
118 A single vector slot containing nil indicates the start of a submenu.
119 A single vector slot containing lambda indicates the end of a submenu.
120 The submenu follows a menu item which is the way to reach the submenu.
121
122 Using a Lisp vector to hold this information while we decode it
123 takes care of protecting all the data from GC. */
124
125 #define MENU_ITEMS_PANE_NAME 1
126 #define MENU_ITEMS_PANE_PREFIX 2
127 #define MENU_ITEMS_PANE_LENGTH 3
128
129 #define MENU_ITEMS_ITEM_NAME 0
130 #define MENU_ITEMS_ITEM_ENABLE 1
131 #define MENU_ITEMS_ITEM_VALUE 2
132 #define MENU_ITEMS_ITEM_EQUIV_KEY 3
133 #define MENU_ITEMS_ITEM_LENGTH 4
134
135 static Lisp_Object menu_items;
136
137 /* Number of slots currently allocated in menu_items. */
138 static int menu_items_allocated;
139
140 /* This is the index in menu_items of the first empty slot. */
141 static int menu_items_used;
142
143 /* The number of panes currently recorded in menu_items,
144 excluding those within submenus. */
145 static int menu_items_n_panes;
146
147 /* Current depth within submenus. */
148 static int menu_items_submenu_depth;
149
150 /* Initialize the menu_items structure if we haven't already done so.
151 Also mark it as currently empty. */
152
153 static void
154 init_menu_items ()
155 {
156 if (NILP (menu_items))
157 {
158 menu_items_allocated = 60;
159 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
160 }
161
162 menu_items_used = 0;
163 menu_items_n_panes = 0;
164 menu_items_submenu_depth = 0;
165 }
166
167 /* Call at the end of generating the data in menu_items.
168 This fills in the number of items in the last pane. */
169
170 static void
171 finish_menu_items ()
172 {
173 }
174
175 /* Call when finished using the data for the current menu
176 in menu_items. */
177
178 static void
179 discard_menu_items ()
180 {
181 /* Free the structure if it is especially large.
182 Otherwise, hold on to it, to save time. */
183 if (menu_items_allocated > 200)
184 {
185 menu_items = Qnil;
186 menu_items_allocated = 0;
187 }
188 }
189
190 /* Make the menu_items vector twice as large. */
191
192 static void
193 grow_menu_items ()
194 {
195 Lisp_Object old;
196 int old_size = menu_items_allocated;
197 old = menu_items;
198
199 menu_items_allocated *= 2;
200 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
201 bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
202 old_size * sizeof (Lisp_Object));
203 }
204
205 /* Begin a submenu. */
206
207 static void
208 push_submenu_start ()
209 {
210 if (menu_items_used + 1 > menu_items_allocated)
211 grow_menu_items ();
212
213 XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
214 menu_items_submenu_depth++;
215 }
216
217 /* End a submenu. */
218
219 static void
220 push_submenu_end ()
221 {
222 if (menu_items_used + 1 > menu_items_allocated)
223 grow_menu_items ();
224
225 XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
226 menu_items_submenu_depth--;
227 }
228
229 /* Start a new menu pane in menu_items..
230 NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
231
232 static void
233 push_menu_pane (name, prefix_vec)
234 Lisp_Object name, prefix_vec;
235 {
236 if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
237 grow_menu_items ();
238
239 if (menu_items_submenu_depth == 0)
240 menu_items_n_panes++;
241 XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
242 XVECTOR (menu_items)->contents[menu_items_used++] = name;
243 XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
244 }
245
246 /* Push one menu item into the current pane.
247 NAME is the string to display. ENABLE if non-nil means
248 this item can be selected. KEY is the key generated by
249 choosing this item. EQUIV is the textual description
250 of the keyboard equivalent for this item (or nil if none). */
251
252 static void
253 push_menu_item (name, enable, key, equiv)
254 Lisp_Object name, enable, key, equiv;
255 {
256 if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
257 grow_menu_items ();
258
259 XVECTOR (menu_items)->contents[menu_items_used++] = name;
260 XVECTOR (menu_items)->contents[menu_items_used++] = enable;
261 XVECTOR (menu_items)->contents[menu_items_used++] = key;
262 XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
263 }
264 \f
265 /* Figure out the current keyboard equivalent of a menu item ITEM1.
266 The item string for menu display should be ITEM_STRING.
267 Store the equivalent keyboard key sequence's
268 textual description into *DESCRIP_PTR.
269 Also cache them in the item itself.
270 Return the real definition to execute. */
271
272 static Lisp_Object
273 menu_item_equiv_key (item_string, item1, descrip_ptr)
274 Lisp_Object item_string;
275 Lisp_Object item1;
276 Lisp_Object *descrip_ptr;
277 {
278 /* This is the real definition--the function to run. */
279 Lisp_Object def;
280 /* This is the sublist that records cached equiv key data
281 so we can save time. */
282 Lisp_Object cachelist;
283 /* These are the saved equivalent keyboard key sequence
284 and its key-description. */
285 Lisp_Object savedkey, descrip;
286 Lisp_Object def1;
287 int changed = 0;
288
289 /* If a help string follows the item string, skip it. */
290 if (CONSP (XCONS (item1)->cdr)
291 && STRINGP (XCONS (XCONS (item1)->cdr)->car))
292 item1 = XCONS (item1)->cdr;
293
294 def = Fcdr (item1);
295
296 /* Get out the saved equivalent-keyboard-key info. */
297 cachelist = savedkey = descrip = Qnil;
298 if (CONSP (def) && CONSP (XCONS (def)->car)
299 && (NILP (XCONS (XCONS (def)->car)->car)
300 || VECTORP (XCONS (XCONS (def)->car)->car)))
301 {
302 cachelist = XCONS (def)->car;
303 def = XCONS (def)->cdr;
304 savedkey = XCONS (cachelist)->car;
305 descrip = XCONS (cachelist)->cdr;
306 }
307
308 /* Is it still valid? */
309 def1 = Qnil;
310 if (!NILP (savedkey))
311 def1 = Fkey_binding (savedkey, Qnil);
312 /* If not, update it. */
313 if (! EQ (def1, def)
314 /* If something had no key binding before, don't recheck it--
315 doing that takes too much time and makes menus too slow. */
316 && !(!NILP (cachelist) && NILP (savedkey)))
317 {
318 changed = 1;
319 descrip = Qnil;
320 savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
321 if (VECTORP (savedkey)
322 && EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar))
323 savedkey = Qnil;
324 if (!NILP (savedkey))
325 {
326 descrip = Fkey_description (savedkey);
327 descrip = concat2 (make_string (" (", 3), descrip);
328 descrip = concat2 (descrip, make_string (")", 1));
329 }
330 }
331
332 /* Cache the data we just got in a sublist of the menu binding. */
333 if (NILP (cachelist))
334 XCONS (item1)->cdr = Fcons (Fcons (savedkey, descrip), def);
335 else if (changed)
336 {
337 XCONS (cachelist)->car = savedkey;
338 XCONS (cachelist)->cdr = descrip;
339 }
340
341 *descrip_ptr = descrip;
342 return def;
343 }
344
345 /* This is used as the handler when calling internal_condition_case_1. */
346
347 static Lisp_Object
348 menu_item_enabled_p_1 (arg)
349 Lisp_Object arg;
350 {
351 return Qnil;
352 }
353
354 /* Return non-nil if the command DEF is enabled when used as a menu item.
355 This is based on looking for a menu-enable property.
356 If NOTREAL is set, don't bother really computing this. */
357
358 static Lisp_Object
359 menu_item_enabled_p (def, notreal)
360 Lisp_Object def;
361 {
362 Lisp_Object enabled, tem;
363
364 enabled = Qt;
365 if (notreal)
366 return enabled;
367 if (XTYPE (def) == Lisp_Symbol)
368 {
369 /* No property, or nil, means enable.
370 Otherwise, enable if value is not nil. */
371 tem = Fget (def, Qmenu_enable);
372 if (!NILP (tem))
373 /* (condition-case nil (eval tem)
374 (error nil)) */
375 enabled = internal_condition_case_1 (Feval, tem, Qerror,
376 menu_item_enabled_p_1);
377 }
378 return enabled;
379 }
380 \f
381 /* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
382 and generate menu panes for them in menu_items.
383 If NOTREAL is nonzero,
384 don't bother really computing whether an item is enabled. */
385
386 static void
387 keymap_panes (keymaps, nmaps, notreal)
388 Lisp_Object *keymaps;
389 int nmaps;
390 int notreal;
391 {
392 int mapno;
393
394 init_menu_items ();
395
396 /* Loop over the given keymaps, making a pane for each map.
397 But don't make a pane that is empty--ignore that map instead.
398 P is the number of panes we have made so far. */
399 for (mapno = 0; mapno < nmaps; mapno++)
400 single_keymap_panes (keymaps[mapno], Qnil, Qnil, notreal);
401
402 finish_menu_items ();
403 }
404
405 /* This is a recursive subroutine of keymap_panes.
406 It handles one keymap, KEYMAP.
407 The other arguments are passed along
408 or point to local variables of the previous function.
409 If NOTREAL is nonzero,
410 don't bother really computing whether an item is enabled. */
411
412 static void
413 single_keymap_panes (keymap, pane_name, prefix, notreal)
414 Lisp_Object keymap;
415 Lisp_Object pane_name;
416 Lisp_Object prefix;
417 int notreal;
418 {
419 Lisp_Object pending_maps;
420 Lisp_Object tail, item, item1, item_string, table;
421 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
422
423 pending_maps = Qnil;
424
425 push_menu_pane (pane_name, prefix);
426
427 for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr)
428 {
429 /* Look at each key binding, and if it has a menu string,
430 make a menu item from it. */
431 item = XCONS (tail)->car;
432 if (XTYPE (item) == Lisp_Cons)
433 {
434 item1 = XCONS (item)->cdr;
435 if (XTYPE (item1) == Lisp_Cons)
436 {
437 item_string = XCONS (item1)->car;
438 if (XTYPE (item_string) == Lisp_String)
439 {
440 /* This is the real definition--the function to run. */
441 Lisp_Object def;
442 /* These are the saved equivalent keyboard key sequence
443 and its key-description. */
444 Lisp_Object descrip;
445 Lisp_Object tem, enabled;
446
447 def = menu_item_equiv_key (item_string, item1, &descrip);
448
449 /* GCPRO because we will call eval.
450 Protecting KEYMAP preserves everything we use;
451 aside from that, must protect whatever might be
452 a string. Since there's no GCPRO5, we refetch
453 item_string instead of protecting it. */
454 GCPRO4 (keymap, pending_maps, def, descrip);
455 enabled = menu_item_enabled_p (def, notreal);
456
457 UNGCPRO;
458
459 item_string = XCONS (item1)->car;
460
461 tem = Fkeymapp (def);
462 if (XSTRING (item_string)->data[0] == '@' && !NILP (tem))
463 pending_maps = Fcons (Fcons (def, Fcons (item_string, XCONS (item)->car)),
464 pending_maps);
465 else
466 {
467 Lisp_Object submap;
468 submap = get_keymap_1 (def, 0, 1);
469 #ifndef USE_X_TOOLKIT
470 /* Indicate visually that this is a submenu. */
471 if (!NILP (submap))
472 item_string = concat2 (item_string,
473 build_string (" >"));
474 #endif
475 push_menu_item (item_string, enabled, XCONS (item)->car,
476 descrip);
477 #ifdef USE_X_TOOLKIT
478 /* Display a submenu using the toolkit. */
479 if (! NILP (submap))
480 {
481 push_submenu_start ();
482 single_keymap_panes (submap, Qnil,
483 XCONS (item)->car, notreal);
484 push_submenu_end ();
485 }
486 #endif
487 }
488 }
489 }
490 }
491 else if (XTYPE (item) == Lisp_Vector)
492 {
493 /* Loop over the char values represented in the vector. */
494 int len = XVECTOR (item)->size;
495 int c;
496 for (c = 0; c < len; c++)
497 {
498 Lisp_Object character;
499 XFASTINT (character) = c;
500 item1 = XVECTOR (item)->contents[c];
501 if (XTYPE (item1) == Lisp_Cons)
502 {
503 item_string = XCONS (item1)->car;
504 if (XTYPE (item_string) == Lisp_String)
505 {
506 Lisp_Object def;
507
508 /* These are the saved equivalent keyboard key sequence
509 and its key-description. */
510 Lisp_Object descrip;
511 Lisp_Object tem, enabled;
512
513 def = menu_item_equiv_key (item_string, item1, &descrip);
514
515 /* GCPRO because we will call eval.
516 Protecting KEYMAP preserves everything we use;
517 aside from that, must protect whatever might be
518 a string. Since there's no GCPRO5, we refetch
519 item_string instead of protecting it. */
520 GCPRO4 (keymap, pending_maps, def, descrip);
521 enabled = menu_item_enabled_p (def, notreal);
522 UNGCPRO;
523
524 item_string = XCONS (item1)->car;
525
526 tem = Fkeymapp (def);
527 if (XSTRING (item_string)->data[0] == '@' && !NILP (tem))
528 pending_maps = Fcons (Fcons (def, Fcons (item_string, character)),
529 pending_maps);
530 else
531 {
532 Lisp_Object submap;
533 submap = get_keymap_1 (def, 0, 1);
534 #ifndef USE_X_TOOLKIT
535 if (!NILP (submap))
536 item_string = concat2 (item_string,
537 build_string (" >"));
538 #endif
539 push_menu_item (item_string, enabled, character,
540 descrip);
541 #ifdef USE_X_TOOLKIT
542 if (! NILP (submap))
543 {
544 push_submenu_start ();
545 single_keymap_panes (submap, Qnil,
546 character, notreal);
547 push_submenu_end ();
548 }
549 #endif
550 }
551 }
552 }
553 }
554 }
555 }
556
557 /* Process now any submenus which want to be panes at this level. */
558 while (!NILP (pending_maps))
559 {
560 Lisp_Object elt, eltcdr, string;
561 elt = Fcar (pending_maps);
562 eltcdr = XCONS (elt)->cdr;
563 string = XCONS (eltcdr)->car;
564 /* We no longer discard the @ from the beginning of the string here.
565 Instead, we do this in xmenu_show. */
566 single_keymap_panes (Fcar (elt), string,
567 XCONS (eltcdr)->cdr, notreal);
568 pending_maps = Fcdr (pending_maps);
569 }
570 }
571 \f
572 /* Push all the panes and items of a menu decsribed by the
573 alist-of-alists MENU.
574 This handles old-fashioned calls to x-popup-menu. */
575
576 static void
577 list_of_panes (menu)
578 Lisp_Object menu;
579 {
580 Lisp_Object tail;
581
582 init_menu_items ();
583
584 for (tail = menu; !NILP (tail); tail = Fcdr (tail))
585 {
586 Lisp_Object elt, pane_name, pane_data;
587 elt = Fcar (tail);
588 pane_name = Fcar (elt);
589 CHECK_STRING (pane_name, 0);
590 push_menu_pane (pane_name, Qnil);
591 pane_data = Fcdr (elt);
592 CHECK_CONS (pane_data, 0);
593 list_of_items (pane_data);
594 }
595
596 finish_menu_items ();
597 }
598
599 /* Push the items in a single pane defined by the alist PANE. */
600
601 static void
602 list_of_items (pane)
603 Lisp_Object pane;
604 {
605 Lisp_Object tail, item, item1;
606
607 for (tail = pane; !NILP (tail); tail = Fcdr (tail))
608 {
609 item = Fcar (tail);
610 if (STRINGP (item))
611 push_menu_item (item, Qnil, Qnil, Qnil);
612 else
613 {
614 CHECK_CONS (item, 0);
615 item1 = Fcar (item);
616 CHECK_STRING (item1, 1);
617 push_menu_item (item1, Qt, Fcdr (item), Qnil);
618 }
619 }
620 }
621 \f
622 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
623 "Pop up a deck-of-cards menu and return user's selection.\n\
624 POSITION is a position specification. This is either a mouse button event\n\
625 or a list ((XOFFSET YOFFSET) WINDOW)\n\
626 where XOFFSET and YOFFSET are positions in characters from the top left\n\
627 corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.)\n\
628 This controls the position of the center of the first line\n\
629 in the first pane of the menu, not the top left of the menu as a whole.\n\
630 If POSITION is t, it means to use the current mouse position.\n\
631 \n\
632 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.\n\
633 The menu items come from key bindings that have a menu string as well as\n\
634 a definition; actually, the \"definition\" in such a key binding looks like\n\
635 \(STRING . REAL-DEFINITION). To give the menu a title, put a string into\n\
636 the keymap as a top-level element.\n\n\
637 You can also use a list of keymaps as MENU.\n\
638 Then each keymap makes a separate pane.\n\
639 When MENU is a keymap or a list of keymaps, the return value\n\
640 is a list of events.\n\n\
641 Alternatively, you can specify a menu of multiple panes\n\
642 with a list of the form (TITLE PANE1 PANE2...),\n\
643 where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
644 Each ITEM is normally a cons cell (STRING . VALUE);\n\
645 but a string can appear as an item--that makes a nonselectable line\n\
646 in the menu.\n\
647 With this form of menu, the return value is VALUE from the chosen item.\n\
648 \n\
649 If POSITION is nil, don't display the menu at all, just precalculate the\n\
650 cached information about equivalent key sequences.")
651 (position, menu)
652 Lisp_Object position, menu;
653 {
654 int number_of_panes, panes;
655 Lisp_Object keymap, tem;
656 int xpos, ypos;
657 Lisp_Object title;
658 char *error_name;
659 Lisp_Object selection;
660 int i, j;
661 FRAME_PTR f;
662 Lisp_Object x, y, window;
663 int keymaps = 0;
664 int menubarp = 0;
665 struct gcpro gcpro1;
666
667 if (! NILP (position))
668 {
669 check_x ();
670
671 /* Decode the first argument: find the window and the coordinates. */
672 if (EQ (position, Qt))
673 {
674 /* Use the mouse's current position. */
675 FRAME_PTR new_f = 0;
676 Lisp_Object bar_window;
677 int part;
678 unsigned long time;
679
680 if (new_f != 0)
681 XSET (window, Lisp_Frame, new_f);
682 else
683 {
684 window = selected_window;
685 XFASTINT (x) = 0;
686 XFASTINT (y) = 0;
687 }
688 }
689 else
690 {
691 tem = Fcar (position);
692 if (XTYPE (tem) == Lisp_Cons)
693 {
694 window = Fcar (Fcdr (position));
695 x = Fcar (tem);
696 y = Fcar (Fcdr (tem));
697 }
698 else
699 {
700 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
701 window = Fcar (tem); /* POSN_WINDOW (tem) */
702 tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
703 x = Fcar (tem);
704 y = Fcdr (tem);
705
706 /* Determine whether this menu is handling a menu bar click. */
707 tem = Fcar (Fcdr (Fcar (Fcdr (position))));
708 if (XTYPE (Fcar (position)) != Lisp_Cons
709 && CONSP (tem)
710 && EQ (Fcar (tem), Qmenu_bar))
711 menubarp = 1;
712 }
713 }
714
715 CHECK_NUMBER (x, 0);
716 CHECK_NUMBER (y, 0);
717
718 /* Decode where to put the menu. */
719
720 if (XTYPE (window) == Lisp_Frame)
721 {
722 f = XFRAME (window);
723
724 xpos = 0;
725 ypos = 0;
726 }
727 else if (XTYPE (window) == Lisp_Window)
728 {
729 CHECK_LIVE_WINDOW (window, 0);
730 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
731
732 xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
733 ypos = (FONT_HEIGHT (f->display.x->font) * XWINDOW (window)->top);
734 }
735 else
736 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
737 but I don't want to make one now. */
738 CHECK_WINDOW (window, 0);
739
740 xpos += XINT (x);
741 ypos += XINT (y);
742 }
743
744 title = Qnil;
745 GCPRO1 (title);
746
747 /* Decode the menu items from what was specified. */
748
749 keymap = Fkeymapp (menu);
750 tem = Qnil;
751 if (XTYPE (menu) == Lisp_Cons)
752 tem = Fkeymapp (Fcar (menu));
753 if (!NILP (keymap))
754 {
755 /* We were given a keymap. Extract menu info from the keymap. */
756 Lisp_Object prompt;
757 keymap = get_keymap (menu);
758
759 /* Extract the detailed info to make one pane. */
760 keymap_panes (&menu, 1, NILP (position));
761
762 /* Search for a string appearing directly as an element of the keymap.
763 That string is the title of the menu. */
764 prompt = map_prompt (keymap);
765
766 /* Make that be the pane title of the first pane. */
767 if (!NILP (prompt) && menu_items_n_panes >= 0)
768 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
769
770 keymaps = 1;
771 }
772 else if (!NILP (tem))
773 {
774 /* We were given a list of keymaps. */
775 int nmaps = XFASTINT (Flength (menu));
776 Lisp_Object *maps
777 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
778 int i;
779
780 title = Qnil;
781
782 /* The first keymap that has a prompt string
783 supplies the menu title. */
784 for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem))
785 {
786 Lisp_Object prompt;
787
788 maps[i++] = keymap = get_keymap (Fcar (tem));
789
790 prompt = map_prompt (keymap);
791 if (NILP (title) && !NILP (prompt))
792 title = prompt;
793 }
794
795 /* Extract the detailed info to make one pane. */
796 keymap_panes (maps, nmaps, NILP (position));
797
798 /* Make the title be the pane title of the first pane. */
799 if (!NILP (title) && menu_items_n_panes >= 0)
800 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
801
802 keymaps = 1;
803 }
804 else
805 {
806 /* We were given an old-fashioned menu. */
807 title = Fcar (menu);
808 CHECK_STRING (title, 1);
809
810 list_of_panes (Fcdr (menu));
811
812 keymaps = 0;
813 }
814
815 if (NILP (position))
816 {
817 discard_menu_items ();
818 UNGCPRO;
819 return Qnil;
820 }
821
822 /* Display them in a menu. */
823 BLOCK_INPUT;
824
825 selection = xmenu_show (f, xpos, ypos, menubarp,
826 keymaps, title, &error_name);
827 UNBLOCK_INPUT;
828
829 discard_menu_items ();
830
831 UNGCPRO;
832
833 if (error_name) error (error_name);
834 return selection;
835 }
836
837 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0,
838 "Pop up a dialog box and return user's selection.\n\
839 POSITION specifies which frame to use.\n\
840 This is normally a mouse button event or a window or frame.\n\
841 If POSITION is t, it means to use the frame the mouse is on.\n\
842 The dialog box appears in the middle of the specified frame.\n\
843 \n\
844 CONTENTS specifies the alternatives to display in the dialog box.\n\
845 It is a list of the form (TITLE ITEM1 ITEM2...).\n\
846 Each ITEM is a cons cell (STRING . VALUE).\n\
847 The return value is VALUE from the chosen item.")
848 (position, contents)
849 Lisp_Object position, contents;
850 {
851 FRAME_PTR f;
852 Lisp_Object window;
853
854 check_x ();
855
856 /* Decode the first argument: find the window or frame to use. */
857 if (EQ (position, Qt))
858 {
859 /* Use the mouse's current position. */
860 FRAME_PTR new_f = 0;
861 Lisp_Object bar_window;
862 int part;
863 unsigned long time;
864 Lisp_Object x, y;
865
866 (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
867
868 if (new_f != 0)
869 XSET (window, Lisp_Frame, new_f);
870 else
871 window = selected_window;
872 }
873 else if (CONSP (position))
874 {
875 Lisp_Object tem;
876 tem = Fcar (position);
877 if (XTYPE (tem) == Lisp_Cons)
878 window = Fcar (Fcdr (position));
879 else
880 {
881 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
882 window = Fcar (tem); /* POSN_WINDOW (tem) */
883 }
884 }
885 else if (WINDOWP (position) || FRAMEP (position))
886 window = position;
887
888 /* Decode where to put the menu. */
889
890 if (XTYPE (window) == Lisp_Frame)
891 f = XFRAME (window);
892 else if (XTYPE (window) == Lisp_Window)
893 {
894 CHECK_LIVE_WINDOW (window, 0);
895 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
896 }
897 else
898 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
899 but I don't want to make one now. */
900 CHECK_WINDOW (window, 0);
901
902 #ifndef USE_X_TOOLKIT
903 /* Display a menu with these alternatives
904 in the middle of frame F. */
905 {
906 Lisp_Object x, y, frame, newpos;
907 XSET (frame, Lisp_Frame, f);
908 XSET (x, Lisp_Int, x_pixel_width (f) / 2);
909 XSET (y, Lisp_Int, x_pixel_height (f) / 2);
910 newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
911
912 return Fx_popup_menu (newpos,
913 Fcons (Fcar (contents), Fcons (contents, Qnil)));
914 }
915 #else
916 {
917 Lisp_Object title;
918 char *error_name;
919 Lisp_Object selection;
920
921 /* Decode the dialog items from what was specified. */
922 title = Fcar (contents);
923 CHECK_STRING (title, 1);
924
925 list_of_panes (Fcons (contents, Qnil));
926
927 /* Display them in a dialog box. */
928 BLOCK_INPUT;
929 selection = xdialog_show (f, 0, 0, title, &error_name);
930 UNBLOCK_INPUT;
931
932 discard_menu_items ();
933
934 if (error_name) error (error_name);
935 return selection;
936 }
937 #endif
938 }
939 \f
940 #ifdef USE_X_TOOLKIT
941
942 static void
943 dispatch_dummy_expose (w, x, y)
944 Widget w;
945 int x;
946 int y;
947 {
948 XExposeEvent dummy;
949
950 dummy.type = Expose;
951 dummy.window = XtWindow (w);
952 dummy.count = 0;
953 dummy.serial = 0;
954 dummy.send_event = 0;
955 dummy.display = XtDisplay (w);
956 dummy.x = x;
957 dummy.y = y;
958
959 XtDispatchEvent (&dummy);
960 }
961
962 static int
963 string_width (mw, s)
964 XlwMenuWidget mw;
965 char* s;
966 {
967 XCharStruct xcs;
968 int drop;
969
970 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
971 return xcs.width;
972 }
973
974 static int
975 event_is_in_menu_item (mw, event, name, string_w)
976 XlwMenuWidget mw;
977 struct input_event *event;
978 char *name;
979 int *string_w;
980 {
981 *string_w += (string_width (mw, name)
982 + 2 * (mw->menu.horizontal_spacing
983 + mw->menu.shadow_thickness));
984 return XINT (event->x) < *string_w;
985 }
986
987
988 /* Return the menu bar key which corresponds to event EVENT in frame F. */
989
990 Lisp_Object
991 map_event_to_object (event, f)
992 struct input_event *event;
993 FRAME_PTR f;
994 {
995 int i,j, string_w;
996 window_state* ws;
997 XlwMenuWidget mw = (XlwMenuWidget) f->display.x->menubar_widget;
998 widget_value *val;
999
1000
1001 string_w = 0;
1002 /* Find the window */
1003 for (val = mw->menu.old_stack [0]->contents; val; val = val->next)
1004 {
1005 ws = &mw->menu.windows [0];
1006 if (ws && event_is_in_menu_item (mw, event, val->name, &string_w))
1007 {
1008 Lisp_Object items;
1009 int i;
1010
1011 items = FRAME_MENU_BAR_ITEMS (f);
1012
1013 for (i = 0; i < XVECTOR (items)->size; i += 3)
1014 {
1015 Lisp_Object pos, string, item;
1016 item = XVECTOR (items)->contents[i];
1017 string = XVECTOR (items)->contents[i + 1];
1018 pos = XVECTOR (items)->contents[i + 2];
1019 if (NILP (string))
1020 break;
1021
1022 if (!strcmp (val->name, XSTRING (string)->data))
1023 return item;
1024 }
1025 }
1026 }
1027 return Qnil;
1028 }
1029
1030 static Lisp_Object *menu_item_selection;
1031
1032 static void
1033 popup_selection_callback (widget, id, client_data)
1034 Widget widget;
1035 LWLIB_ID id;
1036 XtPointer client_data;
1037 {
1038 menu_item_selection = (Lisp_Object *) client_data;
1039 }
1040
1041 static void
1042 popup_down_callback (widget, id, client_data)
1043 Widget widget;
1044 LWLIB_ID id;
1045 XtPointer client_data;
1046 {
1047 BLOCK_INPUT;
1048 lw_destroy_all_widgets (id);
1049 UNBLOCK_INPUT;
1050 }
1051
1052 static void
1053 dialog_selection_callback (widget, id, client_data)
1054 Widget widget;
1055 LWLIB_ID id;
1056 XtPointer client_data;
1057 {
1058 if ((int)client_data != -1)
1059 menu_item_selection = (Lisp_Object *) client_data;
1060 BLOCK_INPUT;
1061 lw_destroy_all_widgets (id);
1062 UNBLOCK_INPUT;
1063 }
1064
1065 /* This recursively calls free_widget_value() on the tree of widgets.
1066 It must free all data that was malloc'ed for these widget_values.
1067 In Emacs, many slots are pointers into the data of Lisp_Strings, and
1068 must be left alone. */
1069
1070 void
1071 free_menubar_widget_value_tree (wv)
1072 widget_value *wv;
1073 {
1074 if (! wv) return;
1075
1076 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
1077
1078 if (wv->contents && (wv->contents != (widget_value*)1))
1079 {
1080 free_menubar_widget_value_tree (wv->contents);
1081 wv->contents = (widget_value *) 0xDEADBEEF;
1082 }
1083 if (wv->next)
1084 {
1085 free_menubar_widget_value_tree (wv->next);
1086 wv->next = (widget_value *) 0xDEADBEEF;
1087 }
1088 BLOCK_INPUT;
1089 free_widget_value (wv);
1090 UNBLOCK_INPUT;
1091 }
1092
1093 extern void EmacsFrameSetCharSize ();
1094
1095 static void
1096 update_frame_menubar (f)
1097 FRAME_PTR f;
1098 {
1099 struct x_display *x = f->display.x;
1100 int columns, rows;
1101 int menubar_changed;
1102
1103 menubar_changed = (x->menubar_widget
1104 && !XtIsManaged (x->menubar_widget));
1105
1106 if (! (menubar_changed))
1107 return;
1108
1109 BLOCK_INPUT;
1110 /* Save the size of the frame because the pane widget doesn't accept to
1111 resize itself. So force it. */
1112 columns = f->width;
1113 rows = f->height;
1114
1115
1116 XawPanedSetRefigureMode (x->column_widget, 0);
1117
1118 /* the order in which children are managed is the top to
1119 bottom order in which they are displayed in the paned window.
1120 First, remove the text-area widget.
1121 */
1122 XtUnmanageChild (x->edit_widget);
1123
1124 /* remove the menubar that is there now, and put up the menubar that
1125 should be there.
1126 */
1127 if (menubar_changed)
1128 {
1129 XtManageChild (x->menubar_widget);
1130 XtMapWidget (x->menubar_widget);
1131 XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0);
1132 }
1133
1134
1135 /* Re-manage the text-area widget */
1136 XtManageChild (x->edit_widget);
1137
1138 /* and now thrash the sizes */
1139 XawPanedSetRefigureMode (x->column_widget, 1);
1140
1141 /* Force the pane widget to resize itself with the right values. */
1142 EmacsFrameSetCharSize (x->edit_widget, columns, rows);
1143
1144 UNBLOCK_INPUT;
1145 }
1146
1147 void
1148 set_frame_menubar (f)
1149 FRAME_PTR f;
1150 {
1151 Widget menubar_widget = f->display.x->menubar_widget;
1152 int id = (int) f;
1153 Lisp_Object tail, items;
1154 widget_value *wv, *save_wv, *first_wv, *prev_wv = 0;
1155 int i;
1156
1157 BLOCK_INPUT;
1158
1159 wv = malloc_widget_value ();
1160 wv->name = "menubar";
1161 wv->value = 0;
1162 wv->enabled = 1;
1163 save_wv = first_wv = wv;
1164
1165 if (NILP (items = FRAME_MENU_BAR_ITEMS (f)))
1166 items = FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
1167
1168 for (i = 0; i < XVECTOR (items)->size; i += 3)
1169 {
1170 Lisp_Object string;
1171
1172 string = XVECTOR (items)->contents[i + 1];
1173 if (NILP (string))
1174 break;
1175
1176 wv = malloc_widget_value ();
1177 if (prev_wv)
1178 prev_wv->next = wv;
1179 else
1180 save_wv->contents = wv;
1181 wv->name = XSTRING (string)->data;
1182 wv->value = 0;
1183 wv->enabled = 1;
1184 prev_wv = wv;
1185 }
1186
1187 if (menubar_widget)
1188 lw_modify_all_widgets (id, first_wv, False);
1189 else
1190 {
1191 menubar_widget = lw_create_widget ("menubar", "menubar",
1192 id, first_wv,
1193 f->display.x->column_widget,
1194 0, 0,
1195 0, 0);
1196 f->display.x->menubar_widget = menubar_widget;
1197 XtVaSetValues (menubar_widget,
1198 XtNshowGrip, 0,
1199 XtNresizeToPreferred, 1,
1200 XtNallowResize, 1,
1201 0);
1202 }
1203
1204 free_menubar_widget_value_tree (first_wv);
1205
1206 update_frame_menubar (f);
1207
1208 UNBLOCK_INPUT;
1209 }
1210
1211 void
1212 free_frame_menubar (f)
1213 FRAME_PTR f;
1214 {
1215 Widget menubar_widget;
1216 int id;
1217
1218 menubar_widget = f->display.x->menubar_widget;
1219 id = (int) f;
1220
1221 if (menubar_widget)
1222 {
1223 BLOCK_INPUT;
1224 lw_destroy_all_widgets (id);
1225 UNBLOCK_INPUT;
1226 }
1227 }
1228 /* Called from Fx_create_frame to create the inital menubar of a frame
1229 before it is mapped, so that the window is mapped with the menubar already
1230 there instead of us tacking it on later and thrashing the window after it
1231 is visible. */
1232 void
1233 initialize_frame_menubar (f)
1234 FRAME_PTR f;
1235 {
1236 set_frame_menubar (f);
1237 }
1238 \f
1239 /* Nonzero if position X, Y relative to inside of frame F
1240 is in some other menu bar item. */
1241
1242 static int this_menu_bar_item_beg;
1243 static int this_menu_bar_item_end;
1244
1245 static int
1246 other_menu_bar_item_p (f, x, y)
1247 FRAME_PTR f;
1248 int x, y;
1249 {
1250 return (y >= 0
1251 && y < f->display.x->menubar_widget->core.height
1252 && x >= 0
1253 && x < f->display.x->menubar_widget->core.width
1254 && (x >= this_menu_bar_item_end
1255 || x < this_menu_bar_item_beg));
1256 }
1257
1258 /* Unread a button-press event in the menu bar of frame F
1259 at x position XPOS relative to the inside of the frame. */
1260
1261 static void
1262 unread_menu_bar_button (f, xpos)
1263 FRAME_PTR f;
1264 int xpos;
1265 {
1266 XEvent event;
1267
1268 event.type = ButtonPress;
1269 event.xbutton.display = x_current_display;
1270 event.xbutton.serial = 0;
1271 event.xbutton.send_event = 0;
1272 event.xbutton.time = CurrentTime;
1273 event.xbutton.button = Button1;
1274 event.xbutton.window = XtWindow (f->display.x->menubar_widget);
1275 event.xbutton.x = xpos;
1276 XPutBackEvent (XDISPLAY &event);
1277 }
1278
1279 /* If the mouse has moved to another menu bar item,
1280 return 1 and unread a button press event for that item.
1281 Otherwise return 0. */
1282
1283 static int
1284 check_mouse_other_menu_bar (f)
1285 FRAME_PTR f;
1286 {
1287 FRAME_PTR new_f;
1288 Lisp_Object bar_window;
1289 int part;
1290 Lisp_Object x, y;
1291 unsigned long time;
1292
1293 (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
1294
1295 if (f == new_f && other_menu_bar_item_p (f, x, y))
1296 {
1297 unread_menu_bar_button (f, x);
1298 return 1;
1299 }
1300
1301 return 0;
1302 }
1303 #endif /* USE_X_TOOLKIT */
1304 \f
1305 /* xmenu_show actually displays a menu using the panes and items in menu_items
1306 and returns the value selected from it.
1307 There are two versions of xmenu_show, one for Xt and one for Xlib.
1308 Both assume input is blocked by the caller. */
1309
1310 /* F is the frame the menu is for.
1311 X and Y are the frame-relative specified position,
1312 relative to the inside upper left corner of the frame F.
1313 MENUBARP is 1 if the click that asked for this menu came from the menu bar.
1314 KEYMAPS is 1 if this menu was specified with keymaps;
1315 in that case, we return a list containing the chosen item's value
1316 and perhaps also the pane's prefix.
1317 TITLE is the specified menu title.
1318 ERROR is a place to store an error message string in case of failure.
1319 (We return nil on failure, but the value doesn't actually matter.) */
1320
1321 #ifdef USE_X_TOOLKIT
1322
1323 extern unsigned int x_mouse_grabbed;
1324 extern Lisp_Object Vmouse_depressed;
1325
1326 static Lisp_Object
1327 xmenu_show (f, x, y, menubarp, keymaps, title, error)
1328 FRAME_PTR f;
1329 int x;
1330 int y;
1331 int menubarp;
1332 int keymaps;
1333 Lisp_Object title;
1334 char **error;
1335 {
1336 int i;
1337 int menu_id;
1338 Widget menu;
1339 XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
1340
1341 /* This is the menu bar item (if any) that led to this menu. */
1342 widget_value *menubar_item = 0;
1343
1344 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
1345 widget_value **submenu_stack
1346 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
1347 Lisp_Object *subprefix_stack
1348 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
1349 int submenu_depth = 0;
1350
1351 /* Define a queue to save up for later unreading
1352 all X events that don't pertain to the menu. */
1353 struct event_queue
1354 {
1355 XEvent event;
1356 struct event_queue *next;
1357 };
1358
1359 struct event_queue *queue = NULL;
1360 struct event_queue *queue_tmp;
1361
1362 *error = NULL;
1363
1364 this_menu_bar_item_beg = -1;
1365 this_menu_bar_item_end = -1;
1366
1367 /* Figure out which menu bar item, if any, this menu is for. */
1368 if (menubarp)
1369 {
1370 int xbeg;
1371 int xend = 0;
1372
1373 for (menubar_item = menubar->menu.old_stack[0]->contents;
1374 menubar_item;
1375 menubar_item = menubar_item->next)
1376 {
1377 xbeg = xend;
1378 xend += (string_width (menubar, menubar_item->name)
1379 + 2 * (menubar->menu.horizontal_spacing
1380 + menubar->menu.shadow_thickness));
1381 if (x < xend)
1382 {
1383 x = xbeg + 4;
1384 y = 0;
1385 /* Arrange to show a different menu if we move in the menu bar
1386 to a different item. */
1387 this_menu_bar_item_beg = xbeg;
1388 this_menu_bar_item_end = xend;
1389 break;
1390 }
1391 }
1392 }
1393 if (menubar_item == 0)
1394 menubarp = 0;
1395
1396 /* Offset the coordinates to root-relative. */
1397 x += (f->display.x->widget->core.x
1398 + f->display.x->widget->core.border_width);
1399 y += (f->display.x->widget->core.y
1400 + f->display.x->widget->core.border_width
1401 + f->display.x->menubar_widget->core.height);
1402
1403 /* Create a tree of widget_value objects
1404 representing the panes and their items. */
1405 wv = malloc_widget_value ();
1406 wv->name = "menu";
1407 wv->value = 0;
1408 wv->enabled = 1;
1409 first_wv = wv;
1410
1411 /* Loop over all panes and items, filling in the tree. */
1412 i = 0;
1413 while (i < menu_items_used)
1414 {
1415 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1416 {
1417 submenu_stack[submenu_depth++] = save_wv;
1418 save_wv = prev_wv;
1419 prev_wv = 0;
1420 i++;
1421 }
1422 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1423 {
1424 prev_wv = save_wv;
1425 save_wv = submenu_stack[--submenu_depth];
1426 i++;
1427 }
1428 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1429 && submenu_depth != 0)
1430 i += MENU_ITEMS_PANE_LENGTH;
1431 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1432 {
1433 /* Create a new pane. */
1434 Lisp_Object pane_name, prefix;
1435 char *pane_string;
1436 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1437 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1438 pane_string = (NILP (pane_name)
1439 ? "" : (char *) XSTRING (pane_name)->data);
1440 /* If there is just one top-level pane, put all its items directly
1441 under the top-level menu. */
1442 if (menu_items_n_panes == 1)
1443 pane_string = "";
1444
1445 /* If the pane has a meaningful name,
1446 make the pane a top-level menu item
1447 with its items as a submenu beneath it. */
1448 if (strcmp (pane_string, ""))
1449 {
1450 wv = malloc_widget_value ();
1451 if (save_wv)
1452 save_wv->next = wv;
1453 else
1454 first_wv->contents = wv;
1455 wv->name = pane_string;
1456 if (keymaps && !NILP (prefix))
1457 wv->name++;
1458 wv->value = 0;
1459 wv->enabled = 1;
1460 }
1461 save_wv = wv;
1462 prev_wv = 0;
1463 i += MENU_ITEMS_PANE_LENGTH;
1464 }
1465 else
1466 {
1467 /* Create a new item within current pane. */
1468 Lisp_Object item_name, enable, descrip;
1469 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
1470 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
1471 descrip
1472 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
1473
1474 wv = malloc_widget_value ();
1475 if (prev_wv)
1476 prev_wv->next = wv;
1477 else
1478 save_wv->contents = wv;
1479 wv->name = XSTRING (item_name)->data;
1480 if (!NILP (descrip))
1481 wv->key = XSTRING (descrip)->data;
1482 wv->value = 0;
1483 wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
1484 wv->enabled = !NILP (enable);
1485 prev_wv = wv;
1486
1487 i += MENU_ITEMS_ITEM_LENGTH;
1488 }
1489 }
1490
1491 /* Actually create the menu. */
1492 menu_id = ++popup_id_tick;
1493 menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
1494 f->display.x->widget, 1, 0,
1495 popup_selection_callback, popup_down_callback);
1496 /* Free the widget_value objects we used to specify the contents. */
1497 free_menubar_widget_value_tree (first_wv);
1498
1499 /* No selection has been chosen yet. */
1500 menu_item_selection = 0;
1501
1502 /* If the mouse moves out of the menu before we show the menu,
1503 don't show it at all. */
1504 if (check_mouse_other_menu_bar (f))
1505 {
1506 lw_destroy_all_widgets (menu_id);
1507 return Qnil;
1508 }
1509
1510
1511 /* Highlight the menu bar item (if any) that led to this menu. */
1512 if (menubarp)
1513 {
1514 menubar_item->call_data = (XtPointer) 1;
1515 dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
1516 }
1517
1518 /* Display the menu. */
1519 {
1520 XButtonPressedEvent dummy;
1521 XlwMenuWidget mw;
1522
1523 mw = (XlwMenuWidget) ((CompositeWidget)menu)->composite.children[0];
1524
1525 dummy.type = ButtonPress;
1526 dummy.serial = 0;
1527 dummy.send_event = 0;
1528 dummy.display = XtDisplay (menu);
1529 dummy.window = XtWindow (XtParent (menu));
1530 dummy.time = CurrentTime;
1531 dummy.button = 0;
1532 dummy.x_root = x;
1533 dummy.y_root = y;
1534
1535 /* We activate directly the lucid implementation. */
1536 pop_up_menu (mw, &dummy);
1537 }
1538
1539 /* No need to check a second time since this is done in the XEvent loop.
1540 This slows done the execution. */
1541 #if 0
1542 /* Check again whether the mouse has moved to another menu bar item. */
1543 if (check_mouse_other_menu_bar (f))
1544 {
1545 /* The mouse moved into a different menu bar item.
1546 We should bring up that item's menu instead.
1547 First pop down this menu. */
1548 XtUngrabPointer ((Widget)
1549 ((XlwMenuWidget)
1550 ((CompositeWidget)menu)->composite.children[0]),
1551 CurrentTime);
1552 lw_destroy_all_widgets (menu_id);
1553 goto pop_down;
1554 }
1555 #endif
1556
1557 /* Process events that apply to the menu. */
1558 while (1)
1559 {
1560 XEvent event;
1561
1562 XtAppNextEvent (Xt_app_con, &event);
1563 if (event.type == ButtonRelease)
1564 {
1565 XtDispatchEvent (&event);
1566 if (! menubarp)
1567 {
1568 /* Do the work of construct_mouse_click since it can't
1569 be called. Initially, the popup menu has been called
1570 from a ButtonPress in the edit_widget. Then the mouse
1571 has been set to grabbed. Reset it now. */
1572 x_mouse_grabbed &= ~(1 << event.xbutton.button);
1573 if (!x_mouse_grabbed)
1574 Vmouse_depressed = Qnil;
1575 }
1576 break;
1577 }
1578 else if (event.type == Expose)
1579 process_expose_from_menu (event);
1580 else if (event.type == MotionNotify)
1581 {
1582 int event_x = (event.xmotion.x_root
1583 - (f->display.x->widget->core.x
1584 + f->display.x->widget->core.border_width));
1585 int event_y = (event.xmotion.y_root
1586 - (f->display.x->widget->core.y
1587 + f->display.x->widget->core.border_width));
1588
1589 if (other_menu_bar_item_p (f, event_x, event_y))
1590 {
1591 /* The mouse moved into a different menu bar item.
1592 We should bring up that item's menu instead.
1593 First pop down this menu. */
1594 XtUngrabPointer ((Widget)
1595 ((XlwMenuWidget)
1596 ((CompositeWidget)menu)->composite.children[0]),
1597 event.xbutton.time);
1598 lw_destroy_all_widgets (menu_id);
1599
1600 /* Put back an event that will bring up the other item's menu. */
1601 unread_menu_bar_button (f, event_x);
1602 /* Don't let us select anything in this case. */
1603 menu_item_selection = 0;
1604 break;
1605 }
1606 }
1607
1608 XtDispatchEvent (&event);
1609 if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
1610 {
1611 queue_tmp
1612 = (struct event_queue *) malloc (sizeof (struct event_queue));
1613
1614 if (queue_tmp != NULL)
1615 {
1616 queue_tmp->event = event;
1617 queue_tmp->next = queue;
1618 queue = queue_tmp;
1619 }
1620 }
1621 }
1622
1623 pop_down:
1624 /* Unhighlight the menu bar item (if any) that led to this menu. */
1625 if (menubarp)
1626 {
1627 menubar_item->call_data = (XtPointer) 0;
1628 dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
1629 }
1630
1631 #if 0 /* No need to do that. The menu has disappeared. */
1632 /* Make sure the menu disappears. */
1633 lw_destroy_all_widgets (menu_id);
1634 #endif
1635
1636 /* Unread any events that we got but did not handle. */
1637 while (queue != NULL)
1638 {
1639 queue_tmp = queue;
1640 XPutBackEvent (XDISPLAY &queue_tmp->event);
1641 queue = queue_tmp->next;
1642 free ((char *)queue_tmp);
1643 }
1644
1645 /* Find the selected item, and its pane, to return
1646 the proper value. */
1647 if (menu_item_selection != 0)
1648 {
1649 Lisp_Object prefix;
1650
1651 prefix = Qnil;
1652 i = 0;
1653 while (i < menu_items_used)
1654 {
1655 Lisp_Object entry;
1656
1657 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1658 {
1659 subprefix_stack[submenu_depth++] = prefix;
1660 prefix = entry;
1661 i++;
1662 }
1663 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1664 {
1665 prefix = subprefix_stack[--submenu_depth];
1666 i++;
1667 }
1668 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1669 {
1670 prefix
1671 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1672 i += MENU_ITEMS_PANE_LENGTH;
1673 }
1674 else
1675 {
1676 entry
1677 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
1678 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
1679 {
1680 if (keymaps != 0)
1681 {
1682 int j;
1683
1684 entry = Fcons (entry, Qnil);
1685 if (!NILP (prefix))
1686 entry = Fcons (prefix, entry);
1687 for (j = submenu_depth - 1; j >= 0; j--)
1688 entry = Fcons (subprefix_stack[j], entry);
1689 }
1690 return entry;
1691 }
1692 i += MENU_ITEMS_ITEM_LENGTH;
1693 }
1694 }
1695 }
1696
1697 return Qnil;
1698 }
1699
1700 static char * button_names [] = {
1701 "button1", "button2", "button3", "button4", "button5",
1702 "button6", "button7", "button8", "button9", "button10" };
1703
1704 static Lisp_Object
1705 xdialog_show (f, menubarp, keymaps, title, error)
1706 FRAME_PTR f;
1707 int menubarp;
1708 int keymaps;
1709 Lisp_Object title;
1710 char **error;
1711 {
1712 int i, nb_buttons=0;
1713 int dialog_id;
1714 Widget menu;
1715 XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
1716 char dialog_name[6];
1717
1718 /* This is the menu bar item (if any) that led to this menu. */
1719 widget_value *menubar_item = 0;
1720
1721 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
1722
1723 /* Define a queue to save up for later unreading
1724 all X events that don't pertain to the menu. */
1725 struct event_queue
1726 {
1727 XEvent event;
1728 struct event_queue *next;
1729 };
1730
1731 struct event_queue *queue = NULL;
1732 struct event_queue *queue_tmp;
1733
1734 *error = NULL;
1735
1736 if (menu_items_n_panes > 1)
1737 {
1738 *error = "Multiple panes in dialog box";
1739 return Qnil;
1740 }
1741
1742 /* Create a tree of widget_value objects
1743 representing the text label and buttons. */
1744 {
1745 Lisp_Object pane_name, prefix;
1746 char *pane_string;
1747 pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
1748 prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
1749 pane_string = (NILP (pane_name)
1750 ? "" : (char *) XSTRING (pane_name)->data);
1751 prev_wv = malloc_widget_value ();
1752 prev_wv->value = pane_string;
1753 if (keymaps && !NILP (prefix))
1754 prev_wv->name++;
1755 prev_wv->enabled = 1;
1756 prev_wv->name = "message";
1757 first_wv = prev_wv;
1758
1759 /* Loop over all panes and items, filling in the tree. */
1760 i = MENU_ITEMS_PANE_LENGTH;
1761 while (i < menu_items_used)
1762 {
1763
1764 /* Create a new item within current pane. */
1765 Lisp_Object item_name, enable, descrip;
1766 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
1767 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
1768 descrip
1769 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
1770
1771 if (NILP (item_name))
1772 {
1773 free_menubar_widget_value_tree (first_wv);
1774 *error = "Submenu in dialog items";
1775 return Qnil;
1776 }
1777 if (nb_buttons >= 10)
1778 {
1779 free_menubar_widget_value_tree (first_wv);
1780 *error = "Too many dialog items";
1781 return Qnil;
1782 }
1783
1784 wv = malloc_widget_value ();
1785 prev_wv->next = wv;
1786 wv->name = (char *) button_names[nb_buttons];
1787 if (!NILP (descrip))
1788 wv->key = XSTRING (descrip)->data;
1789 wv->value = XSTRING (item_name)->data;
1790 wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
1791 wv->enabled = !NILP (enable);
1792 prev_wv = wv;
1793
1794 nb_buttons++;
1795 i += MENU_ITEMS_ITEM_LENGTH;
1796 }
1797
1798 wv = malloc_widget_value ();
1799 wv->name = dialog_name;
1800
1801 /* Dialog boxes use a really stupid name encoding
1802 which specifies how many buttons to use
1803 and how many buttons are on the right.
1804 The Q means something also. */
1805 dialog_name[0] = 'Q';
1806 dialog_name[1] = '0' + nb_buttons;
1807 dialog_name[2] = 'B';
1808 dialog_name[3] = 'R';
1809 dialog_name[4] = '0' + nb_buttons / 2;
1810 dialog_name[5] = 0;
1811 wv->contents = first_wv;
1812 first_wv = wv;
1813
1814 }
1815
1816 /* Actually create the dialog. */
1817 dialog_id = ++popup_id_tick;
1818 menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
1819 f->display.x->widget, 1, 0,
1820 dialog_selection_callback, 0);
1821 #if 0 /* This causes crashes, and seems to be redundant -- rms. */
1822 lw_modify_all_widgets (dialog_id, first_wv, True);
1823 #endif
1824 lw_modify_all_widgets (dialog_id, first_wv->contents->next, True);
1825 /* Free the widget_value objects we used to specify the contents. */
1826 free_menubar_widget_value_tree (first_wv);
1827
1828 /* No selection has been chosen yet. */
1829 menu_item_selection = 0;
1830
1831 /* Display the menu. */
1832 lw_pop_up_all_widgets (dialog_id);
1833
1834 /* Process events that apply to the menu. */
1835 while (1)
1836 {
1837 XEvent event;
1838
1839 XtAppNextEvent (Xt_app_con, &event);
1840 if (event.type == ButtonRelease)
1841 {
1842 XtDispatchEvent (&event);
1843 break;
1844 }
1845 else if (event.type == Expose)
1846 process_expose_from_menu (event);
1847 XtDispatchEvent (&event);
1848 if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
1849 {
1850 queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
1851
1852 if (queue_tmp != NULL)
1853 {
1854 queue_tmp->event = event;
1855 queue_tmp->next = queue;
1856 queue = queue_tmp;
1857 }
1858 }
1859 }
1860 pop_down:
1861
1862 /* Unread any events that we got but did not handle. */
1863 while (queue != NULL)
1864 {
1865 queue_tmp = queue;
1866 XPutBackEvent (XDISPLAY &queue_tmp->event);
1867 queue = queue_tmp->next;
1868 free ((char *)queue_tmp);
1869 }
1870
1871 /* Find the selected item, and its pane, to return
1872 the proper value. */
1873 if (menu_item_selection != 0)
1874 {
1875 Lisp_Object prefix;
1876
1877 prefix = Qnil;
1878 i = 0;
1879 while (i < menu_items_used)
1880 {
1881 Lisp_Object entry;
1882
1883 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1884 {
1885 prefix
1886 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1887 i += MENU_ITEMS_PANE_LENGTH;
1888 }
1889 else
1890 {
1891 entry
1892 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
1893 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
1894 {
1895 if (keymaps != 0)
1896 {
1897 entry = Fcons (entry, Qnil);
1898 if (!NILP (prefix))
1899 entry = Fcons (prefix, entry);
1900 }
1901 return entry;
1902 }
1903 i += MENU_ITEMS_ITEM_LENGTH;
1904 }
1905 }
1906 }
1907
1908 return Qnil;
1909 }
1910 #else /* not USE_X_TOOLKIT */
1911
1912 static Lisp_Object
1913 xmenu_show (f, x, y, menubarp, keymaps, title, error)
1914 FRAME_PTR f;
1915 int x, y;
1916 int keymaps;
1917 int menubarp;
1918 Lisp_Object title;
1919 char **error;
1920 {
1921 Window root;
1922 XMenu *menu;
1923 int pane, selidx, lpane, status;
1924 Lisp_Object entry, pane_prefix;
1925 char *datap;
1926 int ulx, uly, width, height;
1927 int dispwidth, dispheight;
1928 int i;
1929 int dummy_int;
1930 unsigned int dummy_uint;
1931
1932 *error = 0;
1933 if (menu_items_n_panes == 0)
1934 return Qnil;
1935
1936 /* Figure out which root window F is on. */
1937 XGetGeometry (x_current_display, FRAME_X_WINDOW (f), &root,
1938 &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
1939 &dummy_uint, &dummy_uint);
1940
1941 /* Make the menu on that window. */
1942 menu = XMenuCreate (XDISPLAY root, "emacs");
1943 if (menu == NULL)
1944 {
1945 *error = "Can't create menu";
1946 return Qnil;
1947 }
1948
1949 /* Adjust coordinates to relative to the outer (window manager) window. */
1950 #ifdef HAVE_X11
1951 {
1952 Window child;
1953 int win_x = 0, win_y = 0;
1954
1955 /* Find the position of the outside upper-left corner of
1956 the inner window, with respect to the outer window. */
1957 if (f->display.x->parent_desc != ROOT_WINDOW)
1958 {
1959 BLOCK_INPUT;
1960 XTranslateCoordinates (x_current_display,
1961
1962 /* From-window, to-window. */
1963 f->display.x->window_desc,
1964 f->display.x->parent_desc,
1965
1966 /* From-position, to-position. */
1967 0, 0, &win_x, &win_y,
1968
1969 /* Child of window. */
1970 &child);
1971 UNBLOCK_INPUT;
1972 x += win_x;
1973 y += win_y;
1974 }
1975 }
1976 #endif /* HAVE_X11 */
1977
1978 /* Adjust coordinates to be root-window-relative. */
1979 x += f->display.x->left_pos;
1980 y += f->display.x->top_pos;
1981
1982 /* Create all the necessary panes and their items. */
1983 i = 0;
1984 while (i < menu_items_used)
1985 {
1986 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1987 {
1988 /* Create a new pane. */
1989 Lisp_Object pane_name, prefix;
1990 char *pane_string;
1991
1992 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1993 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1994 pane_string = (NILP (pane_name)
1995 ? "" : (char *) XSTRING (pane_name)->data);
1996 if (keymaps && !NILP (prefix))
1997 pane_string++;
1998
1999 lpane = XMenuAddPane (XDISPLAY menu, pane_string, TRUE);
2000 if (lpane == XM_FAILURE)
2001 {
2002 XMenuDestroy (XDISPLAY menu);
2003 *error = "Can't create pane";
2004 return Qnil;
2005 }
2006 i += MENU_ITEMS_PANE_LENGTH;
2007 }
2008 else
2009 {
2010 /* Create a new item within current pane. */
2011 Lisp_Object item_name, enable, descrip;
2012
2013 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2014 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2015 descrip
2016 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
2017 if (!NILP (descrip))
2018 item_name = concat2 (item_name, descrip);
2019
2020 if (XMenuAddSelection (XDISPLAY menu, lpane, 0,
2021 XSTRING (item_name)->data,
2022 !NILP (enable))
2023 == XM_FAILURE)
2024 {
2025 XMenuDestroy (XDISPLAY menu);
2026 *error = "Can't add selection to menu";
2027 return Qnil;
2028 }
2029 i += MENU_ITEMS_ITEM_LENGTH;
2030 }
2031 }
2032
2033 /* All set and ready to fly. */
2034 XMenuRecompute (XDISPLAY menu);
2035 dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
2036 dispheight = DisplayHeight (x_current_display, XDefaultScreen (x_current_display));
2037 x = min (x, dispwidth);
2038 y = min (y, dispheight);
2039 x = max (x, 1);
2040 y = max (y, 1);
2041 XMenuLocate (XDISPLAY menu, 0, 0, x, y,
2042 &ulx, &uly, &width, &height);
2043 if (ulx+width > dispwidth)
2044 {
2045 x -= (ulx + width) - dispwidth;
2046 ulx = dispwidth - width;
2047 }
2048 if (uly+height > dispheight)
2049 {
2050 y -= (uly + height) - dispheight;
2051 uly = dispheight - height;
2052 }
2053 if (ulx < 0) x -= ulx;
2054 if (uly < 0) y -= uly;
2055
2056 XMenuSetAEQ (menu, TRUE);
2057 XMenuSetFreeze (menu, TRUE);
2058 pane = selidx = 0;
2059
2060 status = XMenuActivate (XDISPLAY menu, &pane, &selidx,
2061 x, y, ButtonReleaseMask, &datap);
2062 switch (status)
2063 {
2064 case XM_SUCCESS:
2065 #ifdef XDEBUG
2066 fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
2067 #endif
2068
2069 /* Find the item number SELIDX in pane number PANE. */
2070 i = 0;
2071 while (i < menu_items_used)
2072 {
2073 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2074 {
2075 if (pane == 0)
2076 pane_prefix
2077 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2078 pane--;
2079 i += MENU_ITEMS_PANE_LENGTH;
2080 }
2081 else
2082 {
2083 if (pane == -1)
2084 {
2085 if (selidx == 0)
2086 {
2087 entry
2088 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2089 if (keymaps != 0)
2090 {
2091 entry = Fcons (entry, Qnil);
2092 if (!NILP (pane_prefix))
2093 entry = Fcons (pane_prefix, entry);
2094 }
2095 break;
2096 }
2097 selidx--;
2098 }
2099 i += MENU_ITEMS_ITEM_LENGTH;
2100 }
2101 }
2102 break;
2103
2104 case XM_FAILURE:
2105 XMenuDestroy (XDISPLAY menu);
2106 *error = "Can't activate menu";
2107 case XM_IA_SELECT:
2108 case XM_NO_SELECT:
2109 entry = Qnil;
2110 break;
2111 }
2112 XMenuDestroy (XDISPLAY menu);
2113 return entry;
2114 }
2115 #endif /* not USE_X_TOOLKIT */
2116 \f
2117 syms_of_xmenu ()
2118 {
2119 staticpro (&menu_items);
2120 menu_items = Qnil;
2121
2122 popup_id_tick = (1<<16);
2123 defsubr (&Sx_popup_menu);
2124 defsubr (&Sx_popup_dialog);
2125 }