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