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