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