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