(install-arch-indep): Add missing backslash after a `then'.
[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))
611 push_menu_item (item, Qnil, Qnil);
612 else
613 {
614 CHECK_CONS (item, 0);
615 item1 = Fcar (item);
616 CHECK_STRING (item1, 1);
617 push_menu_item (item1, Qt, Fcdr (item), Qnil);
618 }
619 }
620}
621\f
622DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 1, 2, 0,
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. */
675 FRAME_PTR new_f;
676 Lisp_Object bar_window;
677 int part;
678 unsigned long time;
679
680 (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
681 XSET (window, Lisp_Frame, new_f);
682 }
683 else
684 {
685 tem = Fcar (position);
686 if (XTYPE (tem) == Lisp_Cons)
687 {
688 window = Fcar (Fcdr (position));
689 x = Fcar (tem);
690 y = Fcar (Fcdr (tem));
691 }
692 else
693 {
694 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
695 window = Fcar (tem); /* POSN_WINDOW (tem) */
696 tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
697 x = Fcar (tem);
698 y = Fcdr (tem);
699
700 /* Determine whether this menu is handling a menu bar click. */
701 tem = Fcar (Fcdr (Fcar (Fcdr (position))));
702 if (XTYPE (Fcar (position)) != Lisp_Cons
703 && CONSP (tem)
704 && EQ (Fcar (tem), Qmenu_bar))
705 menubarp = 1;
706 }
707 }
708
709 CHECK_NUMBER (x, 0);
710 CHECK_NUMBER (y, 0);
711
712 /* Decode where to put the menu. */
713
714 if (XTYPE (window) == Lisp_Frame)
715 {
716 f = XFRAME (window);
717
718 xpos = 0;
719 ypos = 0;
720 }
721 else if (XTYPE (window) == Lisp_Window)
722 {
723 CHECK_LIVE_WINDOW (window, 0);
724 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
725
726 xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
727 ypos = (FONT_HEIGHT (f->display.x->font) * XWINDOW (window)->top);
728 }
729 else
730 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
731 but I don't want to make one now. */
732 CHECK_WINDOW (window, 0);
733
734 xpos += XINT (x);
735 ypos += XINT (y);
736 }
737
738 title = Qnil;
739 GCPRO1 (title);
740
741 /* Decode the menu items from what was specified. */
18686d47
RS
742
743 keymap = Fkeymapp (menu);
744 tem = Qnil;
18686d47
RS
745 if (XTYPE (menu) == Lisp_Cons)
746 tem = Fkeymapp (Fcar (menu));
747 if (!NILP (keymap))
748 {
749 /* We were given a keymap. Extract menu info from the keymap. */
750 Lisp_Object prompt;
751 keymap = get_keymap (menu);
752
78589e07 753 /* Extract the detailed info to make one pane. */
101bb4a5 754 keymap_panes (&menu, 1, NILP (position));
78589e07 755
18686d47
RS
756 /* Search for a string appearing directly as an element of the keymap.
757 That string is the title of the menu. */
758 prompt = map_prompt (keymap);
18686d47 759
78589e07
RS
760 /* Make that be the pane title of the first pane. */
761 if (!NILP (prompt) && menu_items_n_panes >= 0)
762 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
763
764 keymaps = 1;
18686d47
RS
765 }
766 else if (!NILP (tem))
767 {
768 /* We were given a list of keymaps. */
18686d47
RS
769 int nmaps = XFASTINT (Flength (menu));
770 Lisp_Object *maps
771 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
772 int i;
78589e07
RS
773
774 title = Qnil;
18686d47
RS
775
776 /* The first keymap that has a prompt string
777 supplies the menu title. */
778 for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem))
779 {
78589e07
RS
780 Lisp_Object prompt;
781
18686d47
RS
782 maps[i++] = keymap = get_keymap (Fcar (tem));
783
784 prompt = map_prompt (keymap);
78589e07
RS
785 if (NILP (title) && !NILP (prompt))
786 title = prompt;
18686d47
RS
787 }
788
789 /* Extract the detailed info to make one pane. */
101bb4a5 790 keymap_panes (maps, nmaps, NILP (position));
78589e07
RS
791
792 /* Make the title be the pane title of the first pane. */
793 if (!NILP (title) && menu_items_n_panes >= 0)
794 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
795
796 keymaps = 1;
18686d47
RS
797 }
798 else
799 {
800 /* We were given an old-fashioned menu. */
78589e07
RS
801 title = Fcar (menu);
802 CHECK_STRING (title, 1);
18686d47 803
78589e07 804 list_of_panes (Fcdr (menu));
18686d47 805
78589e07
RS
806 keymaps = 0;
807 }
18686d47 808
78589e07 809 if (NILP (position))
18686d47 810 {
78589e07
RS
811 discard_menu_items ();
812 UNGCPRO;
813 return Qnil;
18686d47
RS
814 }
815
78589e07
RS
816 /* Display them in a menu. */
817 BLOCK_INPUT;
18686d47 818
78589e07
RS
819 selection = xmenu_show (f, xpos, ypos, menubarp,
820 keymaps, title, &error_name);
821 UNBLOCK_INPUT;
18686d47 822
78589e07 823 discard_menu_items ();
18686d47 824
78589e07 825 UNGCPRO;
18686d47 826
78589e07
RS
827 if (error_name) error (error_name);
828 return selection;
18686d47 829}
165e1749
FP
830
831DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 1, 2, 0,
832 "Pop up a dialog box and return user's selection.\n\
833POSITION is a position specification. This is either a mouse button event\n\
834or a list ((XOFFSET YOFFSET) WINDOW)\n\
835where XOFFSET and YOFFSET are positions in characters from the top left\n\
836corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.)\n\
837This controls the position of the center of the first line\n\
838in the first pane of the menu, not the top left of the menu as a whole.\n\
839If POSITION is t, it means to use the current mouse position.\n\
840\n\
841MENU is a specifier for a menu. For the simplest case, MENU is a keymap.\n\
842The menu items come from key bindings that have a menu string as well as\n\
843a definition; actually, the \"definition\" in such a key binding looks like\n\
844\(STRING . REAL-DEFINITION). To give the menu a title, put a string into\n\
845the keymap as a top-level element.\n\n\
846You can also use a list of keymaps as MENU.\n\
847 Then each keymap makes a separate pane.\n\
848When MENU is a keymap or a list of keymaps, the return value\n\
849is a list of events.\n\n\
850Alternatively, you can specify a menu of multiple panes\n\
851 with a list of the form (TITLE PANE1 PANE2...),\n\
852where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
853Each ITEM is normally a cons cell (STRING . VALUE);\n\
854but a string can appear as an item--that makes a nonselectable line\n\
855in the menu.\n\
856With this form of menu, the return value is VALUE from the chosen item.\n\
857\n\
858If POSITION is nil, don't display the menu at all, just precalculate the\n\
859cached information about equivalent key sequences.")
860 (position, menu)
861 Lisp_Object position, menu;
862{
392d3f4b
RS
863#ifndef USE_X_TOOLKIT
864 return Fx_popup_menu (position, menu);
865#else
165e1749
FP
866 int number_of_panes, panes;
867 Lisp_Object keymap, tem;
868 int xpos, ypos;
869 Lisp_Object title;
870 char *error_name;
871 Lisp_Object selection;
872 int i, j;
873 FRAME_PTR f;
874 Lisp_Object x, y, window;
875 int keymaps = 0;
876 int menubarp = 0;
877 struct gcpro gcpro1;
878
879 check_x ();
880
881 if (! NILP (position))
882 {
883 /* Decode the first argument: find the window and the coordinates. */
884 if (EQ (position, Qt))
885 {
886 /* Use the mouse's current position. */
887 FRAME_PTR new_f;
888 Lisp_Object bar_window;
889 int part;
890 unsigned long time;
891
892 (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
893 XSET (window, Lisp_Frame, new_f);
894 }
895
896 CHECK_NUMBER (x, 0);
897 CHECK_NUMBER (y, 0);
898
899 /* Decode where to put the menu. */
900
901 if (XTYPE (window) == Lisp_Frame)
902 {
903 f = XFRAME (window);
904
905 xpos = 0;
906 ypos = 0;
907 }
908 else if (XTYPE (window) == Lisp_Window)
909 {
910 CHECK_LIVE_WINDOW (window, 0);
911 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
912
913 xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
914 ypos = (FONT_HEIGHT (f->display.x->font) * XWINDOW (window)->top);
915 }
916 else
917 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
918 but I don't want to make one now. */
919 CHECK_WINDOW (window, 0);
920
921 xpos += XINT (x);
922 ypos += XINT (y);
923 }
924
925 title = Qnil;
926 GCPRO1 (title);
927
928 /* Decode the dialog items from what was specified. */
929 {
930 /* We were given an old-fashioned menu. */
931 title = Fcar (menu);
932 CHECK_STRING (title, 1);
933
934 list_of_panes (Fcdr (menu));
935
936 keymaps = 0;
937 }
938
939 if (NILP (position))
940 {
941 discard_menu_items ();
942 UNGCPRO;
943 return Qnil;
944 }
945
946 /* Display them in a dialog box. */
947 BLOCK_INPUT;
948
949 selection = xdialog_show (f, xpos, ypos, menubarp,
950 keymaps, title, &error_name);
951 UNBLOCK_INPUT;
952
953 discard_menu_items ();
954
955 UNGCPRO;
956
957 if (error_name) error (error_name);
958 return selection;
7464b131 959#endif
392d3f4b 960}
78589e07
RS
961\f
962#ifdef USE_X_TOOLKIT
18686d47
RS
963
964static void
78589e07
RS
965dispatch_dummy_expose (w, x, y)
966 Widget w;
967 int x;
968 int y;
969{
970 XExposeEvent dummy;
971
972 dummy.type = Expose;
973 dummy.window = XtWindow (w);
974 dummy.count = 0;
975 dummy.serial = 0;
976 dummy.send_event = 0;
977 dummy.display = XtDisplay (w);
978 dummy.x = x;
979 dummy.y = y;
980
981 XtDispatchEvent (&dummy);
982}
983
984static int
985string_width (mw, s)
986 XlwMenuWidget mw;
987 char* s;
988{
989 XCharStruct xcs;
990 int drop;
991
992 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
993 return xcs.width;
994}
995
996static int
997event_is_in_menu_item (mw, event, name, string_w)
998 XlwMenuWidget mw;
999 struct input_event *event;
1000 char *name;
1001 int *string_w;
1002{
1003 *string_w += (string_width (mw, name)
1004 + 2 * (mw->menu.horizontal_spacing
1005 + mw->menu.shadow_thickness));
1006 return XINT (event->x) < *string_w;
1007}
1008
1009
5b3557df
RS
1010/* Return the menu bar key which corresponds to event EVENT in frame F. */
1011
78589e07
RS
1012Lisp_Object
1013map_event_to_object (event, f)
1014 struct input_event *event;
1015 FRAME_PTR f;
1016{
1017 int i,j, string_w;
1018 window_state* ws;
1019 XlwMenuWidget mw = (XlwMenuWidget) f->display.x->menubar_widget;
1020 widget_value *val;
1021
1022
1023 string_w = 0;
1024 /* Find the window */
1025 for (val = mw->menu.old_stack [0]->contents; val; val = val->next)
1026 {
1027 ws = &mw->menu.windows [0];
1028 if (ws && event_is_in_menu_item (mw, event, val->name, &string_w))
1029 {
1030 Lisp_Object items;
5b3557df
RS
1031 int i;
1032
78589e07 1033 items = FRAME_MENU_BAR_ITEMS (f);
5b3557df
RS
1034
1035 for (i = 0; i < XVECTOR (items)->size; i += 3)
1036 {
1037 Lisp_Object pos, string, item;
1038 item = XVECTOR (items)->contents[i];
1039 string = XVECTOR (items)->contents[i + 1];
1040 pos = XVECTOR (items)->contents[i + 2];
1041 if (NILP (string))
1042 break;
1043
1044 if (!strcmp (val->name, XSTRING (string)->data))
1045 return item;
1046 }
78589e07
RS
1047 }
1048 }
1049 return Qnil;
1050}
1051
1052static Lisp_Object *menu_item_selection;
1053
1054static void
1055popup_selection_callback (widget, id, client_data)
1056 Widget widget;
1057 LWLIB_ID id;
1058 XtPointer client_data;
1059{
1060 menu_item_selection = (Lisp_Object *) client_data;
1061}
1062
1063static void
1064popup_down_callback (widget, id, client_data)
1065 Widget widget;
1066 LWLIB_ID id;
1067 XtPointer client_data;
1068{
18686d47
RS
1069 BLOCK_INPUT;
1070 lw_destroy_all_widgets (id);
1071 UNBLOCK_INPUT;
18686d47
RS
1072}
1073
165e1749
FP
1074static void
1075dialog_selection_callback (widget, id, client_data)
1076 Widget widget;
1077 LWLIB_ID id;
1078 XtPointer client_data;
1079{
1080 if ((int)client_data != -1)
1081 menu_item_selection = (Lisp_Object *) client_data;
1082 BLOCK_INPUT;
1083 lw_destroy_all_widgets (id);
1084 UNBLOCK_INPUT;
1085}
1086
18686d47
RS
1087/* This recursively calls free_widget_value() on the tree of widgets.
1088 It must free all data that was malloc'ed for these widget_values.
78589e07
RS
1089 In Emacs, many slots are pointers into the data of Lisp_Strings, and
1090 must be left alone. */
1091
18686d47
RS
1092void
1093free_menubar_widget_value_tree (wv)
1094 widget_value *wv;
1095{
1096 if (! wv) return;
18686d47
RS
1097
1098 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
1099
1100 if (wv->contents && (wv->contents != (widget_value*)1))
1101 {
1102 free_menubar_widget_value_tree (wv->contents);
1103 wv->contents = (widget_value *) 0xDEADBEEF;
1104 }
1105 if (wv->next)
1106 {
1107 free_menubar_widget_value_tree (wv->next);
1108 wv->next = (widget_value *) 0xDEADBEEF;
1109 }
1110 BLOCK_INPUT;
1111 free_widget_value (wv);
1112 UNBLOCK_INPUT;
1113}
1114
cffa74ea
FP
1115extern void EmacsFrameSetCharSize ();
1116
18686d47
RS
1117static void
1118update_one_frame_psheets (f)
1119 FRAME_PTR f;
1120{
1121 struct x_display *x = f->display.x;
cffa74ea 1122 int columns, rows;
18686d47
RS
1123 int menubar_changed;
1124
1125 menubar_changed = (x->menubar_widget
1126 && !XtIsManaged (x->menubar_widget));
1127
1128 if (! (menubar_changed))
1129 return;
1130
1131 BLOCK_INPUT;
cffa74ea
FP
1132 /* Save the size of the frame because the pane widget doesn't accept to
1133 resize itself. So force it. */
1134 columns = f->width;
1135 rows = f->height;
1136
1137
18686d47
RS
1138 XawPanedSetRefigureMode (x->column_widget, 0);
1139
1140 /* the order in which children are managed is the top to
1141 bottom order in which they are displayed in the paned window.
1142 First, remove the text-area widget.
1143 */
1144 XtUnmanageChild (x->edit_widget);
1145
1146 /* remove the menubar that is there now, and put up the menubar that
1147 should be there.
1148 */
1149 if (menubar_changed)
1150 {
1151 XtManageChild (x->menubar_widget);
1152 XtMapWidget (x->menubar_widget);
1153 XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0);
1154 }
1155
1156
1157 /* Re-manage the text-area widget */
1158 XtManageChild (x->edit_widget);
1159
1160 /* and now thrash the sizes */
1161 XawPanedSetRefigureMode (x->column_widget, 1);
cffa74ea
FP
1162
1163 /* Force the pane widget to resize itself with the right values. */
1164 EmacsFrameSetCharSize (x->edit_widget, columns, rows);
1165
18686d47
RS
1166 UNBLOCK_INPUT;
1167}
1168
1169void
1170set_frame_menubar (f)
1171 FRAME_PTR f;
1172{
1173 Widget menubar_widget = f->display.x->menubar_widget;
1174 int id = (int) f;
5b3557df 1175 Lisp_Object tail, items;
18686d47 1176 widget_value *wv, *save_wv, *first_wv, *prev_wv = 0;
5b3557df 1177 int i;
18686d47
RS
1178
1179 BLOCK_INPUT;
1180
1181 wv = malloc_widget_value ();
1182 wv->name = "menubar";
1183 wv->value = 0;
1184 wv->enabled = 1;
1185 save_wv = first_wv = wv;
1186
5b3557df 1187 items = FRAME_MENU_BAR_ITEMS (f);
18686d47 1188
5b3557df 1189 for (i = 0; i < XVECTOR (items)->size; i += 3)
18686d47
RS
1190 {
1191 Lisp_Object string;
1192
5b3557df
RS
1193 string = XVECTOR (items)->contents[i + 1];
1194 if (NILP (string))
1195 break;
18686d47
RS
1196
1197 wv = malloc_widget_value ();
1198 if (prev_wv)
1199 prev_wv->next = wv;
1200 else
1201 save_wv->contents = wv;
1202 wv->name = XSTRING (string)->data;
1203 wv->value = 0;
1204 wv->enabled = 1;
1205 prev_wv = wv;
1206 }
1207
1208 if (menubar_widget)
1209 lw_modify_all_widgets (id, first_wv, False);
1210 else
1211 {
1212 menubar_widget = lw_create_widget ("menubar", "menubar",
1213 id, first_wv,
1214 f->display.x->column_widget,
1215 0, 0,
1216 0, 0);
1217 f->display.x->menubar_widget = menubar_widget;
1218 XtVaSetValues (menubar_widget,
1219 XtNshowGrip, 0,
1220 XtNresizeToPreferred, 1,
1221 XtNallowResize, 1,
1222 0);
1223 }
1224
1225 free_menubar_widget_value_tree (first_wv);
1226
1227 update_one_frame_psheets (f);
1228
1229 UNBLOCK_INPUT;
1230}
85f487d1
FP
1231
1232void
1233free_frame_menubar (f)
1234 FRAME_PTR f;
1235{
1236 Widget menubar_widget;
1237 int id;
1238
1239 menubar_widget = f->display.x->menubar_widget;
1240 id = (int) f;
1241
1242 if (menubar_widget)
1243 {
1244 BLOCK_INPUT;
1245 lw_destroy_all_widgets (id);
1246 UNBLOCK_INPUT;
1247 }
1248}
18686d47 1249\f
78589e07
RS
1250/* Nonzero if position X, Y relative to inside of frame F
1251 is in some other menu bar item. */
dcfdbac7 1252
78589e07
RS
1253static int this_menu_bar_item_beg;
1254static int this_menu_bar_item_end;
1255
1256static int
1257other_menu_bar_item_p (f, x, y)
1258 FRAME_PTR f;
1259 int x, y;
1260{
1261 return (y >= 0
1262 && y < f->display.x->menubar_widget->core.height
1263 && x >= 0
1264 && x < f->display.x->menubar_widget->core.width
1265 && (x >= this_menu_bar_item_end
1266 || x < this_menu_bar_item_beg));
1267}
1268
1269/* Unread a button-press event in the menu bar of frame F
1270 at x position XPOS relative to the inside of the frame. */
1271
1272static void
1273unread_menu_bar_button (f, xpos)
1274 FRAME_PTR f;
1275 int xpos;
1276{
1277 XEvent event;
1278
1279 event.type = ButtonPress;
1280 event.xbutton.display = x_current_display;
1281 event.xbutton.serial = 0;
1282 event.xbutton.send_event = 0;
1283 event.xbutton.time = CurrentTime;
1284 event.xbutton.button = Button1;
1285 event.xbutton.window = XtWindow (f->display.x->menubar_widget);
1286 event.xbutton.x = xpos;
1287 XPutBackEvent (XDISPLAY &event);
1288}
1289
1290/* If the mouse has moved to another menu bar item,
1291 return 1 and unread a button press event for that item.
1292 Otherwise return 0. */
1293
1294static int
1295check_mouse_other_menu_bar (f)
1296 FRAME_PTR f;
1297{
1298 FRAME_PTR new_f;
1299 Lisp_Object bar_window;
1300 int part;
1301 Lisp_Object x, y;
1302 unsigned long time;
1303
1304 (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
1305
1306 if (f == new_f && other_menu_bar_item_p (f, x, y))
1307 {
1308 unread_menu_bar_button (f, x);
1309 return 1;
1310 }
1311
1312 return 0;
1313}
1314#endif /* USE_X_TOOLKIT */
1315\f
1316/* xmenu_show actually displays a menu using the panes and items in menu_items
1317 and returns the value selected from it.
1318 There are two versions of xmenu_show, one for Xt and one for Xlib.
1319 Both assume input is blocked by the caller. */
1320
1321/* F is the frame the menu is for.
1322 X and Y are the frame-relative specified position,
1323 relative to the inside upper left corner of the frame F.
1324 MENUBARP is 1 if the click that asked for this menu came from the menu bar.
1325 KEYMAPS is 1 if this menu was specified with keymaps;
1326 in that case, we return a list containing the chosen item's value
1327 and perhaps also the pane's prefix.
1328 TITLE is the specified menu title.
1329 ERROR is a place to store an error message string in case of failure.
1330 (We return nil on failure, but the value doesn't actually matter.) */
18686d47
RS
1331
1332#ifdef USE_X_TOOLKIT
18686d47 1333
165e1749
FP
1334extern unsigned int x_mouse_grabbed;
1335extern Lisp_Object Vmouse_depressed;
1336
78589e07
RS
1337static Lisp_Object
1338xmenu_show (f, x, y, menubarp, keymaps, title, error)
18686d47 1339 FRAME_PTR f;
18686d47
RS
1340 int x;
1341 int y;
1342 int menubarp;
78589e07
RS
1343 int keymaps;
1344 Lisp_Object title;
1345 char **error;
18686d47 1346{
78589e07
RS
1347 int i;
1348 int menu_id;
18686d47 1349 Widget menu;
78589e07 1350 XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
18686d47 1351
78589e07
RS
1352 /* This is the menu bar item (if any) that led to this menu. */
1353 widget_value *menubar_item = 0;
1354
1355 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
101bb4a5
RS
1356 widget_value **submenu_stack
1357 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
1358 Lisp_Object *subprefix_stack
1359 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
1360 int submenu_depth = 0;
78589e07
RS
1361
1362 /* Define a queue to save up for later unreading
1363 all X events that don't pertain to the menu. */
1364 struct event_queue
18686d47
RS
1365 {
1366 XEvent event;
78589e07
RS
1367 struct event_queue *next;
1368 };
18686d47 1369
78589e07
RS
1370 struct event_queue *queue = NULL;
1371 struct event_queue *queue_tmp;
1372
1373 *error = NULL;
1374
1375 this_menu_bar_item_beg = -1;
1376 this_menu_bar_item_end = -1;
1377
1378 /* Figure out which menu bar item, if any, this menu is for. */
1379 if (menubarp)
1380 {
1381 int xbeg;
1382 int xend = 0;
1383
1384 for (menubar_item = menubar->menu.old_stack[0]->contents;
1385 menubar_item;
1386 menubar_item = menubar_item->next)
1387 {
1388 xbeg = xend;
1389 xend += (string_width (menubar, menubar_item->name)
1390 + 2 * (menubar->menu.horizontal_spacing
1391 + menubar->menu.shadow_thickness));
1392 if (x < xend)
1393 {
1394 x = xbeg + 4;
1395 y = 0;
1396 /* Arrange to show a different menu if we move in the menu bar
1397 to a different item. */
1398 this_menu_bar_item_beg = xbeg;
1399 this_menu_bar_item_end = xend;
1400 break;
1401 }
1402 }
1403 }
1404 if (menubar_item == 0)
1405 menubarp = 0;
1406
1407 /* Offset the coordinates to root-relative. */
1408 x += (f->display.x->widget->core.x
1409 + f->display.x->widget->core.border_width);
1410 y += (f->display.x->widget->core.y
1411 + f->display.x->widget->core.border_width
1412 + f->display.x->menubar_widget->core.height);
63c414df 1413
78589e07
RS
1414 /* Create a tree of widget_value objects
1415 representing the panes and their items. */
1416 wv = malloc_widget_value ();
1417 wv->name = "menu";
1418 wv->value = 0;
1419 wv->enabled = 1;
1420 first_wv = wv;
1421
1422 /* Loop over all panes and items, filling in the tree. */
1423 i = 0;
1424 while (i < menu_items_used)
1425 {
101bb4a5
RS
1426 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1427 {
1428 submenu_stack[submenu_depth++] = save_wv;
1429 save_wv = prev_wv;
1430 prev_wv = 0;
1431 i++;
1432 }
1433 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1434 {
1435 prev_wv = save_wv;
1436 save_wv = submenu_stack[--submenu_depth];
1437 i++;
1438 }
1439 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1440 && submenu_depth != 0)
1441 i += MENU_ITEMS_PANE_LENGTH;
1442 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
78589e07
RS
1443 {
1444 /* Create a new pane. */
1445 Lisp_Object pane_name, prefix;
1446 char *pane_string;
1447 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1448 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1449 pane_string = (NILP (pane_name)
1450 ? "" : (char *) XSTRING (pane_name)->data);
101bb4a5 1451 /* If there is just one top-level pane, put all its items directly
78589e07
RS
1452 under the top-level menu. */
1453 if (menu_items_n_panes == 1)
1454 pane_string = "";
1455
1456 /* If the pane has a meaningful name,
1457 make the pane a top-level menu item
1458 with its items as a submenu beneath it. */
1459 if (strcmp (pane_string, ""))
1460 {
1461 wv = malloc_widget_value ();
1462 if (save_wv)
1463 save_wv->next = wv;
1464 else
1465 first_wv->contents = wv;
1466 wv->name = pane_string;
1467 if (keymaps && !NILP (prefix))
1468 wv->name++;
1469 wv->value = 0;
1470 wv->enabled = 1;
1471 }
1472 save_wv = wv;
1473 prev_wv = 0;
1474 i += MENU_ITEMS_PANE_LENGTH;
1475 }
1476 else
1477 {
1478 /* Create a new item within current pane. */
1479 Lisp_Object item_name, enable, descrip;
1480 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
1481 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
1482 descrip
1483 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
1484
1485 wv = malloc_widget_value ();
1486 if (prev_wv)
1487 prev_wv->next = wv;
1488 else
1489 save_wv->contents = wv;
1490 wv->name = XSTRING (item_name)->data;
1491 if (!NILP (descrip))
1492 wv->key = XSTRING (descrip)->data;
1493 wv->value = 0;
1494 wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
1495 wv->enabled = !NILP (enable);
1496 prev_wv = wv;
1497
1498 i += MENU_ITEMS_ITEM_LENGTH;
1499 }
1500 }
1501
1502 /* Actually create the menu. */
18686d47 1503 menu_id = ++popup_id_tick;
78589e07 1504 menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
18686d47
RS
1505 f->display.x->widget, 1, 0,
1506 popup_selection_callback, popup_down_callback);
78589e07
RS
1507 /* Free the widget_value objects we used to specify the contents. */
1508 free_menubar_widget_value_tree (first_wv);
1509
1510 /* No selection has been chosen yet. */
1511 menu_item_selection = 0;
1512
1513 /* If the mouse moves out of the menu before we show the menu,
1514 don't show it at all. */
1515 if (check_mouse_other_menu_bar (f))
1516 {
1517 lw_destroy_all_widgets (menu_id);
1518 return Qnil;
1519 }
1520
18686d47 1521
78589e07
RS
1522 /* Highlight the menu bar item (if any) that led to this menu. */
1523 if (menubarp)
1524 {
1525 menubar_item->call_data = (XtPointer) 1;
1526 dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
1527 }
18686d47 1528
78589e07 1529 /* Display the menu. */
18686d47
RS
1530 {
1531 XButtonPressedEvent dummy;
1532 XlwMenuWidget mw;
1533
78589e07 1534 mw = (XlwMenuWidget) ((CompositeWidget)menu)->composite.children[0];
18686d47
RS
1535
1536 dummy.type = ButtonPress;
1537 dummy.serial = 0;
1538 dummy.send_event = 0;
1539 dummy.display = XtDisplay (menu);
1540 dummy.window = XtWindow (XtParent (menu));
1541 dummy.time = CurrentTime;
1542 dummy.button = 0;
1543 dummy.x_root = x;
1544 dummy.y_root = y;
1545
78589e07 1546 /* We activate directly the lucid implementation. */
18686d47
RS
1547 pop_up_menu (mw, &dummy);
1548 }
1549
cffa74ea
FP
1550 /* No need to check a second time since this is done in the XEvent loop.
1551 This slows done the execution. */
1552#if 0
78589e07
RS
1553 /* Check again whether the mouse has moved to another menu bar item. */
1554 if (check_mouse_other_menu_bar (f))
47e8f9a3 1555 {
78589e07
RS
1556 /* The mouse moved into a different menu bar item.
1557 We should bring up that item's menu instead.
1558 First pop down this menu. */
1559 XtUngrabPointer ((Widget)
1560 ((XlwMenuWidget)
1561 ((CompositeWidget)menu)->composite.children[0]),
1562 CurrentTime);
1563 lw_destroy_all_widgets (menu_id);
1564 goto pop_down;
47e8f9a3 1565 }
cffa74ea 1566#endif
47e8f9a3 1567
78589e07 1568 /* Process events that apply to the menu. */
18686d47
RS
1569 while (1)
1570 {
18686d47 1571 XEvent event;
78589e07 1572
18686d47
RS
1573 XtAppNextEvent (Xt_app_con, &event);
1574 if (event.type == ButtonRelease)
1575 {
1576 XtDispatchEvent (&event);
165e1749
FP
1577 if (! menubarp)
1578 {
1579 /* Do the work of construct_mouse_click since it can't
1580 be called. Initially, the popup menu has been called
1581 from a ButtonPress in the edit_widget. Then the mouse
1582 has been set to grabbed. Reset it now. */
1583 x_mouse_grabbed &= ~(1 << event.xbutton.button);
1584 if (!x_mouse_grabbed)
1585 Vmouse_depressed = Qnil;
1586 }
18686d47
RS
1587 break;
1588 }
78589e07
RS
1589 else if (event.type == Expose)
1590 process_expose_from_menu (event);
1591 else if (event.type == MotionNotify)
1592 {
1593 int event_x = (event.xmotion.x_root
1594 - (f->display.x->widget->core.x
1595 + f->display.x->widget->core.border_width));
1596 int event_y = (event.xmotion.y_root
1597 - (f->display.x->widget->core.y
1598 + f->display.x->widget->core.border_width));
1599
1600 if (other_menu_bar_item_p (f, event_x, event_y))
1601 {
1602 /* The mouse moved into a different menu bar item.
1603 We should bring up that item's menu instead.
1604 First pop down this menu. */
1605 XtUngrabPointer ((Widget)
1606 ((XlwMenuWidget)
1607 ((CompositeWidget)menu)->composite.children[0]),
1608 event.xbutton.time);
1609 lw_destroy_all_widgets (menu_id);
1610
1611 /* Put back an event that will bring up the other item's menu. */
1612 unread_menu_bar_button (f, event_x);
1613 /* Don't let us select anything in this case. */
1614 menu_item_selection = 0;
1615 break;
1616 }
1617 }
47e8f9a3 1618
18686d47 1619 XtDispatchEvent (&event);
6f3e8e34 1620 if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
78589e07 1621 {
6f3e8e34
RM
1622 queue_tmp
1623 = (struct event_queue *) malloc (sizeof (struct event_queue));
0f7159e3
FP
1624
1625 if (queue_tmp != NULL)
1626 {
1627 queue_tmp->event = event;
1628 queue_tmp->next = queue;
1629 queue = queue_tmp;
1630 }
78589e07 1631 }
18686d47 1632 }
78589e07
RS
1633
1634 pop_down:
1635 /* Unhighlight the menu bar item (if any) that led to this menu. */
18686d47
RS
1636 if (menubarp)
1637 {
78589e07 1638 menubar_item->call_data = (XtPointer) 0;
399703f1 1639 dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
18686d47
RS
1640 }
1641
165e1749 1642#if 0 /* No need to do that. The menu has disappeared. */
78589e07
RS
1643 /* Make sure the menu disappears. */
1644 lw_destroy_all_widgets (menu_id);
165e1749 1645#endif
78589e07
RS
1646
1647 /* Unread any events that we got but did not handle. */
1648 while (queue != NULL)
18686d47 1649 {
78589e07
RS
1650 queue_tmp = queue;
1651 XPutBackEvent (XDISPLAY &queue_tmp->event);
1652 queue = queue_tmp->next;
1653 free ((char *)queue_tmp);
18686d47
RS
1654 }
1655
78589e07
RS
1656 /* Find the selected item, and its pane, to return
1657 the proper value. */
1658 if (menu_item_selection != 0)
1659 {
1660 Lisp_Object prefix;
1661
1662 prefix = Qnil;
1663 i = 0;
1664 while (i < menu_items_used)
1665 {
1666 Lisp_Object entry;
18686d47 1667
101bb4a5
RS
1668 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1669 {
1670 subprefix_stack[submenu_depth++] = prefix;
1671 prefix = entry;
1672 i++;
1673 }
1674 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1675 {
1676 prefix = subprefix_stack[--submenu_depth];
1677 i++;
1678 }
1679 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
78589e07
RS
1680 {
1681 prefix
1682 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1683 i += MENU_ITEMS_PANE_LENGTH;
1684 }
1685 else
1686 {
1687 entry
1688 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
1689 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
1690 {
1691 if (keymaps != 0)
1692 {
101bb4a5
RS
1693 int j;
1694
78589e07
RS
1695 entry = Fcons (entry, Qnil);
1696 if (!NILP (prefix))
1697 entry = Fcons (prefix, entry);
101bb4a5
RS
1698 for (j = submenu_depth - 1; j >= 0; j--)
1699 entry = Fcons (subprefix_stack[j], entry);
78589e07
RS
1700 }
1701 return entry;
1702 }
1703 i += MENU_ITEMS_ITEM_LENGTH;
1704 }
1705 }
1706 }
1707
1708 return Qnil;
18686d47
RS
1709}
1710
165e1749
FP
1711static char * button_names [] = {
1712 "button1", "button2", "button3", "button4", "button5",
1713 "button6", "button7", "button8", "button9", "button10" };
1714
1715static Lisp_Object
1716xdialog_show (f, x, y, menubarp, keymaps, title, error)
1717 FRAME_PTR f;
1718 int x;
1719 int y;
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;
1729
1730 /* This is the menu bar item (if any) that led to this menu. */
1731 widget_value *menubar_item = 0;
1732
1733 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
1734
1735 /* Define a queue to save up for later unreading
1736 all X events that don't pertain to the menu. */
1737 struct event_queue
1738 {
1739 XEvent event;
1740 struct event_queue *next;
1741 };
1742
1743 struct event_queue *queue = NULL;
1744 struct event_queue *queue_tmp;
1745
1746 *error = NULL;
1747
1748 /* Create a tree of widget_value objects
1749 representing the text label and buttons. */
1750 {
1751 Lisp_Object pane_name, prefix;
1752 char *pane_string;
1753 pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
1754 prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
1755 pane_string = (NILP (pane_name)
1756 ? "" : (char *) XSTRING (pane_name)->data);
1757 prev_wv = malloc_widget_value ();
1758 prev_wv->value = pane_string;
1759 if (keymaps && !NILP (prefix))
1760 prev_wv->name++;
1761 prev_wv->enabled = 1;
1762 prev_wv->name = "message";
1763 first_wv = prev_wv;
1764
1765 /* Loop over all panes and items, filling in the tree. */
1766 i = MENU_ITEMS_PANE_LENGTH;
1767 while (i < menu_items_used)
1768 {
1769
1770 /* Create a new item within current pane. */
1771 Lisp_Object item_name, enable, descrip;
1772 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
1773 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
1774 descrip
1775 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
1776
1777 wv = malloc_widget_value ();
1778 prev_wv->next = wv;
1779 wv->name = (char *) button_names [nb_buttons];
1780 if (!NILP (descrip))
1781 wv->key = XSTRING (descrip)->data;
1782 wv->value = XSTRING (item_name)->data;
1783 wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
1784 wv->enabled = !NILP (enable);
1785 prev_wv = wv;
1786
1787 nb_buttons++;
1788 i += MENU_ITEMS_ITEM_LENGTH;
1789 }
1790
1791 wv = malloc_widget_value ();
1792 wv->name = "Q2BR1";
1793 wv->contents = first_wv;
1794 first_wv = wv;
1795
1796 }
1797
1798 /* Actually create the dialog. */
1799 dialog_id = ++popup_id_tick;
1800 menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
1801 f->display.x->widget, 1, 0,
1802 dialog_selection_callback, 0);
1803 lw_modify_all_widgets (dialog_id, first_wv, True);
1804 lw_modify_all_widgets (dialog_id, first_wv->contents, True);
1805 /* Free the widget_value objects we used to specify the contents. */
1806 free_menubar_widget_value_tree (first_wv);
1807
1808 /* No selection has been chosen yet. */
1809 menu_item_selection = 0;
1810
1811
1812 /* Display the menu. */
1813 lw_pop_up_all_widgets (dialog_id);
1814
1815 /* Process events that apply to the menu. */
1816 while (1)
1817 {
1818 XEvent event;
1819
1820 XtAppNextEvent (Xt_app_con, &event);
1821 if (event.type == ButtonRelease)
1822 {
1823 XtDispatchEvent (&event);
1824 break;
1825 }
1826 else if (event.type == Expose)
1827 process_expose_from_menu (event);
1828 XtDispatchEvent (&event);
1829 if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
1830 {
1831 queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
1832
1833 if (queue_tmp != NULL)
1834 {
1835 queue_tmp->event = event;
1836 queue_tmp->next = queue;
1837 queue = queue_tmp;
1838 }
1839 }
1840 }
1841 pop_down:
1842
1843 /* Unread any events that we got but did not handle. */
1844 while (queue != NULL)
1845 {
1846 queue_tmp = queue;
1847 XPutBackEvent (XDISPLAY &queue_tmp->event);
1848 queue = queue_tmp->next;
1849 free ((char *)queue_tmp);
1850 }
1851
1852 /* Find the selected item, and its pane, to return
1853 the proper value. */
1854 if (menu_item_selection != 0)
1855 {
1856 Lisp_Object prefix;
1857
1858 prefix = Qnil;
1859 i = 0;
1860 while (i < menu_items_used)
1861 {
1862 Lisp_Object entry;
1863
1864 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1865 {
1866 prefix
1867 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1868 i += MENU_ITEMS_PANE_LENGTH;
1869 }
1870 else
1871 {
1872 entry
1873 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
1874 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
1875 {
1876 if (keymaps != 0)
1877 {
1878 entry = Fcons (entry, Qnil);
1879 if (!NILP (prefix))
1880 entry = Fcons (prefix, entry);
1881 }
1882 return entry;
1883 }
1884 i += MENU_ITEMS_ITEM_LENGTH;
1885 }
1886 }
1887 }
1888
1889 return Qnil;
1890}
18686d47 1891#else /* not USE_X_TOOLKIT */
78589e07
RS
1892
1893static Lisp_Object
1894xmenu_show (f, x, y, menubarp, keymaps, title, error)
1895 FRAME_PTR f;
1896 int x, y;
1897 int keymaps;
1898 int menubarp;
1899 Lisp_Object title;
1900 char **error;
dcfdbac7 1901{
78589e07
RS
1902 Window root;
1903 XMenu *menu;
1904 int pane, selidx, lpane, status;
1905 Lisp_Object entry, pane_prefix;
dcfdbac7
JB
1906 char *datap;
1907 int ulx, uly, width, height;
1908 int dispwidth, dispheight;
78589e07
RS
1909 int i;
1910 int dummy_int;
1911 unsigned int dummy_uint;
088831f6 1912
07a675b7 1913 *error = 0;
78589e07
RS
1914 if (menu_items_n_panes == 0)
1915 return Qnil;
088831f6 1916
78589e07
RS
1917 /* Figure out which root window F is on. */
1918 XGetGeometry (x_current_display, FRAME_X_WINDOW (f), &root,
1919 &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
1920 &dummy_uint, &dummy_uint);
18686d47 1921
78589e07
RS
1922 /* Make the menu on that window. */
1923 menu = XMenuCreate (XDISPLAY root, "emacs");
1924 if (menu == NULL)
dcfdbac7
JB
1925 {
1926 *error = "Can't create menu";
78589e07 1927 return Qnil;
dcfdbac7 1928 }
78589e07
RS
1929
1930 /* Adjust coordinates to relative to the outer (window manager) window. */
1931#ifdef HAVE_X11
1932 {
1933 Window child;
1934 int win_x = 0, win_y = 0;
1935
1936 /* Find the position of the outside upper-left corner of
1937 the inner window, with respect to the outer window. */
1938 if (f->display.x->parent_desc != ROOT_WINDOW)
1939 {
1940 BLOCK_INPUT;
1941 XTranslateCoordinates (x_current_display,
1942
1943 /* From-window, to-window. */
1944 f->display.x->window_desc,
1945 f->display.x->parent_desc,
1946
1947 /* From-position, to-position. */
1948 0, 0, &win_x, &win_y,
1949
1950 /* Child of window. */
1951 &child);
1952 UNBLOCK_INPUT;
1953 x += win_x;
1954 y += win_y;
1955 }
1956 }
1957#endif /* HAVE_X11 */
1958
1959 /* Adjust coordinates to be root-window-relative. */
1960 x += f->display.x->left_pos;
1961 y += f->display.x->top_pos;
18686d47 1962
78589e07
RS
1963 /* Create all the necessary panes and their items. */
1964 i = 0;
1965 while (i < menu_items_used)
dcfdbac7 1966 {
78589e07 1967 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
dcfdbac7 1968 {
78589e07
RS
1969 /* Create a new pane. */
1970 Lisp_Object pane_name, prefix;
1971 char *pane_string;
1972
1973 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1974 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1975 pane_string = (NILP (pane_name)
1976 ? "" : (char *) XSTRING (pane_name)->data);
1977 if (keymaps && !NILP (prefix))
1978 pane_string++;
1979
1980 lpane = XMenuAddPane (XDISPLAY menu, pane_string, TRUE);
1981 if (lpane == XM_FAILURE)
1982 {
1983 XMenuDestroy (XDISPLAY menu);
1984 *error = "Can't create pane";
1985 return Qnil;
1986 }
1987 i += MENU_ITEMS_PANE_LENGTH;
dcfdbac7 1988 }
78589e07 1989 else
dcfdbac7 1990 {
78589e07
RS
1991 /* Create a new item within current pane. */
1992 Lisp_Object item_name, enable, descrip;
1993
1994 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
1995 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
1996 descrip
1997 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
1998 if (!NILP (descrip))
1999 item_name = concat2 (item_name, descrip);
2000
2001 if (XMenuAddSelection (XDISPLAY menu, lpane, 0,
2002 XSTRING (item_name)->data,
2003 !NILP (enable))
dcfdbac7
JB
2004 == XM_FAILURE)
2005 {
78589e07 2006 XMenuDestroy (XDISPLAY menu);
dcfdbac7 2007 *error = "Can't add selection to menu";
78589e07 2008 return Qnil;
dcfdbac7 2009 }
78589e07 2010 i += MENU_ITEMS_ITEM_LENGTH;
dcfdbac7
JB
2011 }
2012 }
78589e07
RS
2013
2014 /* All set and ready to fly. */
2015 XMenuRecompute (XDISPLAY menu);
dcfdbac7
JB
2016 dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
2017 dispheight = DisplayHeight (x_current_display, XDefaultScreen (x_current_display));
78589e07
RS
2018 x = min (x, dispwidth);
2019 y = min (y, dispheight);
2020 x = max (x, 1);
2021 y = max (y, 1);
2022 XMenuLocate (XDISPLAY menu, 0, 0, x, y,
dcfdbac7
JB
2023 &ulx, &uly, &width, &height);
2024 if (ulx+width > dispwidth)
2025 {
78589e07 2026 x -= (ulx + width) - dispwidth;
dcfdbac7
JB
2027 ulx = dispwidth - width;
2028 }
2029 if (uly+height > dispheight)
2030 {
78589e07 2031 y -= (uly + height) - dispheight;
dcfdbac7
JB
2032 uly = dispheight - height;
2033 }
78589e07
RS
2034 if (ulx < 0) x -= ulx;
2035 if (uly < 0) y -= uly;
dcfdbac7 2036
78589e07
RS
2037 XMenuSetFreeze (menu, TRUE);
2038 pane = selidx = 0;
dcfdbac7 2039
78589e07
RS
2040 status = XMenuActivate (XDISPLAY menu, &pane, &selidx,
2041 x, y, ButtonReleaseMask, &datap);
dcfdbac7
JB
2042 switch (status)
2043 {
2044 case XM_SUCCESS:
2045#ifdef XDEBUG
2046 fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
2047#endif
fa6d54d9 2048
78589e07
RS
2049 /* Find the item number SELIDX in pane number PANE. */
2050 i = 0;
2051 while (i < menu_items_used)
fa6d54d9 2052 {
78589e07 2053 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
088831f6 2054 {
78589e07
RS
2055 if (pane == 0)
2056 pane_prefix
2057 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2058 pane--;
2059 i += MENU_ITEMS_PANE_LENGTH;
088831f6 2060 }
78589e07 2061 else
ab6ee1a0 2062 {
78589e07 2063 if (pane == -1)
ab6ee1a0 2064 {
78589e07 2065 if (selidx == 0)
ab6ee1a0 2066 {
78589e07
RS
2067 entry
2068 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2069 if (keymaps != 0)
ab6ee1a0 2070 {
78589e07
RS
2071 entry = Fcons (entry, Qnil);
2072 if (!NILP (pane_prefix))
2073 entry = Fcons (pane_prefix, entry);
ab6ee1a0 2074 }
78589e07 2075 break;
ab6ee1a0 2076 }
78589e07 2077 selidx--;
ab6ee1a0 2078 }
78589e07 2079 i += MENU_ITEMS_ITEM_LENGTH;
ab6ee1a0
RS
2080 }
2081 }
78589e07 2082 break;
dcfdbac7 2083
78589e07
RS
2084 case XM_FAILURE:
2085 XMenuDestroy (XDISPLAY menu);
2086 *error = "Can't activate menu";
2087 case XM_IA_SELECT:
2088 case XM_NO_SELECT:
2089 entry = Qnil;
2090 break;
dcfdbac7 2091 }
78589e07
RS
2092 XMenuDestroy (XDISPLAY menu);
2093 return entry;
dcfdbac7 2094}
78589e07 2095#endif /* not USE_X_TOOLKIT */
088831f6 2096\f
78589e07 2097syms_of_xmenu ()
dcfdbac7 2098{
78589e07
RS
2099 staticpro (&menu_items);
2100 menu_items = Qnil;
dcfdbac7 2101
78589e07
RS
2102 popup_id_tick = (1<<16);
2103 defsubr (&Sx_popup_menu);
165e1749 2104 defsubr (&Sx_popup_dialog);
dcfdbac7 2105}