(QCfile): Moved to xdisp.c.
[bpt/emacs.git] / src / xmenu.c
CommitLineData
dcfdbac7 1/* X Communication module for terminals which understand the X protocol.
faf8a408 2 Copyright (C) 1986, 1988, 1993, 1994, 1996 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
3b7ad313
EN
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
dcfdbac7
JB
20
21/* X pop-up deck-of-cards menu facility for gnuemacs.
22 *
23 * Written by Jon Arnold and Roman Budzianowski
24 * Mods and rewrite by Robert Krawitz
25 *
26 */
27
18686d47
RS
28/* Modified by Fred Pierresteguy on December 93
29 to make the popup menus and menubar use the Xt. */
30
78589e07
RS
31/* Rewritten for clarity and GC protection by rms in Feb 94. */
32
dcfdbac7
JB
33/* On 4.3 this loses if it comes after xterm.h. */
34#include <signal.h>
18160b98 35#include <config.h>
c4a10daf
DL
36#ifdef HAVE_STDLIB_H
37#include <stdlib.h>
38#endif
565620a5
RS
39
40#include <stdio.h>
dcfdbac7 41#include "lisp.h"
18686d47 42#include "termhooks.h"
7708e9bd 43#include "frame.h"
dcfdbac7 44#include "window.h"
031b0e31 45#include "keyboard.h"
9ac0d9e0 46#include "blockinput.h"
88766961 47#include "buffer.h"
dcfdbac7 48
eeee3112
RS
49#ifdef MSDOS
50#include "msdos.h"
51#endif
52
87485d6f 53#ifdef HAVE_X_WINDOWS
dcfdbac7
JB
54/* This may include sys/types.h, and that somehow loses
55 if this is not done before the other system files. */
56#include "xterm.h"
87485d6f 57#endif
dcfdbac7
JB
58
59/* Load sys/types.h if not already loaded.
60 In some systems loading it twice is suicidal. */
61#ifndef makedev
62#include <sys/types.h>
63#endif
64
65#include "dispextern.h"
66
87485d6f 67#ifdef HAVE_X_WINDOWS
703dc2a8 68#undef HAVE_MULTILINGUAL_MENU
18686d47
RS
69#ifdef USE_X_TOOLKIT
70#include <X11/Xlib.h>
71#include <X11/IntrinsicP.h>
72#include <X11/CoreP.h>
73#include <X11/StringDefs.h>
60f312e2 74#include <X11/Shell.h>
5ee80bd3 75#ifdef USE_LUCID
1d1c1567 76#include <X11/Xaw/Paned.h>
5ee80bd3 77#endif /* USE_LUCID */
18686d47 78#include "../lwlib/lwlib.h"
a352a815
RS
79#else /* not USE_X_TOOLKIT */
80#include "../oldXMenu/XMenu.h"
81#endif /* not USE_X_TOOLKIT */
82#endif /* HAVE_X_WINDOWS */
18686d47 83
3427a3db
GM
84#ifdef USE_MOTIF
85#include <Xm/Xm.h> /* for LESSTIF_VERSION */
86#endif
87
dcfdbac7
JB
88#define min(x,y) (((x) < (y)) ? (x) : (y))
89#define max(x,y) (((x) > (y)) ? (x) : (y))
90
dcfdbac7
JB
91#ifndef TRUE
92#define TRUE 1
93#define FALSE 0
78589e07 94#endif /* no TRUE */
dcfdbac7 95
bfc524bc
RS
96Lisp_Object Vmenu_updating_frame;
97
0314aacb
RS
98Lisp_Object Qdebug_on_next_call;
99
18686d47 100extern Lisp_Object Qmenu_bar;
92280f67 101extern Lisp_Object Qmouse_click, Qevent_kind;
78589e07 102
de57a39c 103extern Lisp_Object QCtoggle, QCradio;
8fbc986d 104
88766961
RS
105extern Lisp_Object Voverriding_local_map;
106extern Lisp_Object Voverriding_local_map_menu_flag;
107
108extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
109
110extern Lisp_Object Qmenu_bar_update_hook;
111
18686d47 112#ifdef USE_X_TOOLKIT
88766961 113extern void set_frame_menubar ();
78589e07
RS
114extern void process_expose_from_menu ();
115extern XtAppContext Xt_app_con;
116
165e1749 117static Lisp_Object xdialog_show ();
4dedbfe0 118void popup_get_selection ();
18686d47
RS
119#endif
120
3427a3db
GM
121#ifdef USE_X_TOOLKIT
122
123/* Define HAVE_BOXES if meus can handle radio and toggle buttons. */
124
125#define HAVE_BOXES 1
126#endif
127
128static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
129 Lisp_Object, Lisp_Object, Lisp_Object,
130 Lisp_Object));
78589e07
RS
131static Lisp_Object xmenu_show ();
132static void keymap_panes ();
133static void single_keymap_panes ();
de57a39c 134static void single_menu_item ();
78589e07
RS
135static void list_of_panes ();
136static void list_of_items ();
137\f
138/* This holds a Lisp vector that holds the results of decoding
139 the keymaps or alist-of-alists that specify a menu.
dcfdbac7 140
78589e07 141 It describes the panes and items within the panes.
dcfdbac7 142
78589e07
RS
143 Each pane is described by 3 elements in the vector:
144 t, the pane name, the pane's prefix key.
a352a815 145 Then follow the pane's items, with 5 elements per item:
78589e07 146 the item string, the enable flag, the item's value,
a352a815 147 the definition, and the equivalent keyboard key's description string.
dcfdbac7 148
101bb4a5
RS
149 In some cases, multiple levels of menus may be described.
150 A single vector slot containing nil indicates the start of a submenu.
151 A single vector slot containing lambda indicates the end of a submenu.
152 The submenu follows a menu item which is the way to reach the submenu.
153
fcaa7665
RS
154 A single vector slot containing quote indicates that the
155 following items should appear on the right of a dialog box.
156
78589e07
RS
157 Using a Lisp vector to hold this information while we decode it
158 takes care of protecting all the data from GC. */
dcfdbac7 159
78589e07
RS
160#define MENU_ITEMS_PANE_NAME 1
161#define MENU_ITEMS_PANE_PREFIX 2
162#define MENU_ITEMS_PANE_LENGTH 3
088831f6 163
78589e07
RS
164#define MENU_ITEMS_ITEM_NAME 0
165#define MENU_ITEMS_ITEM_ENABLE 1
166#define MENU_ITEMS_ITEM_VALUE 2
167#define MENU_ITEMS_ITEM_EQUIV_KEY 3
a352a815 168#define MENU_ITEMS_ITEM_DEFINITION 4
3427a3db
GM
169#define MENU_ITEMS_ITEM_TYPE 5
170#define MENU_ITEMS_ITEM_SELECTED 6
171#define MENU_ITEMS_ITEM_LENGTH 7
7da99777 172
78589e07 173static Lisp_Object menu_items;
18686d47 174
78589e07
RS
175/* Number of slots currently allocated in menu_items. */
176static int menu_items_allocated;
18686d47 177
78589e07
RS
178/* This is the index in menu_items of the first empty slot. */
179static int menu_items_used;
18686d47 180
101bb4a5
RS
181/* The number of panes currently recorded in menu_items,
182 excluding those within submenus. */
78589e07 183static int menu_items_n_panes;
18686d47 184
101bb4a5
RS
185/* Current depth within submenus. */
186static int menu_items_submenu_depth;
187
4dedbfe0 188/* Flag which when set indicates a dialog or menu has been posted by
c98fcf4b 189 Xt on behalf of one of the widget sets. */
4dedbfe0
PR
190static int popup_activated_flag;
191
88766961 192static int next_menubar_widget_id;
745c34fb 193
a9be6839
RS
194/* This is set nonzero after the user activates the menu bar, and set
195 to zero again after the menu bars are redisplayed by prepare_menu_bar.
196 While it is nonzero, all calls to set_frame_menubar go deep.
197
198 I don't understand why this is needed, but it does seem to be
199 needed on Motif, according to Marcus Daniels <marcus@sysc.pdx.edu>. */
200
201int pending_menu_activation;
bd3a4da2 202\f
88766961 203#ifdef USE_X_TOOLKIT
bd3a4da2 204
7556890b 205/* Return the frame whose ->output_data.x->id equals ID, or 0 if none. */
bd3a4da2 206
88766961
RS
207static struct frame *
208menubar_id_to_frame (id)
209 LWLIB_ID id;
210{
211 Lisp_Object tail, frame;
212 FRAME_PTR f;
bd3a4da2 213
8e713be6 214 for (tail = Vframe_list; GC_CONSP (tail); tail = XCDR (tail))
bd3a4da2 215 {
8e713be6 216 frame = XCAR (tail);
88766961
RS
217 if (!GC_FRAMEP (frame))
218 continue;
219 f = XFRAME (frame);
54e9e953 220 if (f->output_data.nothing == 1)
88766961 221 continue;
7556890b 222 if (f->output_data.x->id == id)
88766961 223 return f;
bd3a4da2 224 }
88766961 225 return 0;
bd3a4da2 226}
88766961
RS
227
228#endif
4dedbfe0 229\f
78589e07
RS
230/* Initialize the menu_items structure if we haven't already done so.
231 Also mark it as currently empty. */
232
233static void
234init_menu_items ()
235{
236 if (NILP (menu_items))
237 {
238 menu_items_allocated = 60;
239 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
18686d47
RS
240 }
241
78589e07
RS
242 menu_items_used = 0;
243 menu_items_n_panes = 0;
101bb4a5 244 menu_items_submenu_depth = 0;
78589e07 245}
18686d47 246
78589e07
RS
247/* Call at the end of generating the data in menu_items.
248 This fills in the number of items in the last pane. */
1658603c 249
78589e07
RS
250static void
251finish_menu_items ()
252{
253}
1658603c 254
78589e07
RS
255/* Call when finished using the data for the current menu
256 in menu_items. */
1658603c 257
78589e07
RS
258static void
259discard_menu_items ()
260{
261 /* Free the structure if it is especially large.
262 Otherwise, hold on to it, to save time. */
263 if (menu_items_allocated > 200)
264 {
265 menu_items = Qnil;
266 menu_items_allocated = 0;
267 }
268}
1658603c 269
101bb4a5
RS
270/* Make the menu_items vector twice as large. */
271
272static void
273grow_menu_items ()
274{
275 Lisp_Object old;
276 int old_size = menu_items_allocated;
277 old = menu_items;
278
279 menu_items_allocated *= 2;
280 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
281 bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
282 old_size * sizeof (Lisp_Object));
283}
284
285/* Begin a submenu. */
286
287static void
288push_submenu_start ()
289{
290 if (menu_items_used + 1 > menu_items_allocated)
291 grow_menu_items ();
292
293 XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
294 menu_items_submenu_depth++;
295}
296
297/* End a submenu. */
298
299static void
300push_submenu_end ()
301{
302 if (menu_items_used + 1 > menu_items_allocated)
303 grow_menu_items ();
304
305 XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
306 menu_items_submenu_depth--;
307}
308
fcaa7665
RS
309/* Indicate boundary between left and right. */
310
311static void
312push_left_right_boundary ()
313{
314 if (menu_items_used + 1 > menu_items_allocated)
315 grow_menu_items ();
316
317 XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
318}
319
78589e07
RS
320/* Start a new menu pane in menu_items..
321 NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
1658603c 322
78589e07
RS
323static void
324push_menu_pane (name, prefix_vec)
325 Lisp_Object name, prefix_vec;
326{
327 if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
101bb4a5 328 grow_menu_items ();
dcfdbac7 329
101bb4a5
RS
330 if (menu_items_submenu_depth == 0)
331 menu_items_n_panes++;
78589e07
RS
332 XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
333 XVECTOR (menu_items)->contents[menu_items_used++] = name;
334 XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
335}
dcfdbac7 336
3427a3db
GM
337/* Push one menu item into the current pane. NAME is the string to
338 display. ENABLE if non-nil means this item can be selected. KEY
339 is the key generated by choosing this item, or nil if this item
340 doesn't really have a definition. DEF is the definition of this
341 item. EQUIV is the textual description of the keyboard equivalent
342 for this item (or nil if none). TYPE is the type of this menu
343 item, one of nil, `toggle' or `radio'. */
18686d47 344
78589e07 345static void
3427a3db
GM
346push_menu_item (name, enable, key, def, equiv, type, selected)
347 Lisp_Object name, enable, key, def, equiv, type, selected;
78589e07
RS
348{
349 if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
101bb4a5 350 grow_menu_items ();
088831f6 351
78589e07
RS
352 XVECTOR (menu_items)->contents[menu_items_used++] = name;
353 XVECTOR (menu_items)->contents[menu_items_used++] = enable;
354 XVECTOR (menu_items)->contents[menu_items_used++] = key;
355 XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
a352a815 356 XVECTOR (menu_items)->contents[menu_items_used++] = def;
3427a3db
GM
357 XVECTOR (menu_items)->contents[menu_items_used++] = type;
358 XVECTOR (menu_items)->contents[menu_items_used++] = selected;
78589e07
RS
359}
360\f
78589e07 361/* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
101bb4a5
RS
362 and generate menu panes for them in menu_items.
363 If NOTREAL is nonzero,
364 don't bother really computing whether an item is enabled. */
18686d47 365
78589e07 366static void
101bb4a5 367keymap_panes (keymaps, nmaps, notreal)
78589e07
RS
368 Lisp_Object *keymaps;
369 int nmaps;
101bb4a5 370 int notreal;
18686d47 371{
78589e07 372 int mapno;
18686d47 373
78589e07 374 init_menu_items ();
18686d47 375
78589e07
RS
376 /* Loop over the given keymaps, making a pane for each map.
377 But don't make a pane that is empty--ignore that map instead.
378 P is the number of panes we have made so far. */
379 for (mapno = 0; mapno < nmaps; mapno++)
98381e42 380 single_keymap_panes (keymaps[mapno], Qnil, Qnil, notreal, 10);
78589e07
RS
381
382 finish_menu_items ();
383}
384
385/* This is a recursive subroutine of keymap_panes.
386 It handles one keymap, KEYMAP.
387 The other arguments are passed along
101bb4a5 388 or point to local variables of the previous function.
de57a39c
RS
389 If NOTREAL is nonzero, only check for equivalent key bindings, don't
390 evaluate expressions in menu items and don't make any menu.
98381e42
RS
391
392 If we encounter submenus deeper than MAXDEPTH levels, ignore them. */
78589e07
RS
393
394static void
98381e42 395single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
78589e07
RS
396 Lisp_Object keymap;
397 Lisp_Object pane_name;
398 Lisp_Object prefix;
101bb4a5 399 int notreal;
98381e42 400 int maxdepth;
78589e07 401{
de57a39c
RS
402 Lisp_Object pending_maps = Qnil;
403 Lisp_Object tail, item;
404 struct gcpro gcpro1, gcpro2;
405 int notbuttons = 0;
78589e07 406
98381e42
RS
407 if (maxdepth <= 0)
408 return;
409
78589e07
RS
410 push_menu_pane (pane_name, prefix);
411
de57a39c
RS
412#ifndef HAVE_BOXES
413 /* Remember index for first item in this pane so we can go back and
414 add a prefix when (if) we see the first button. After that, notbuttons
415 is set to 0, to mark that we have seen a button and all non button
416 items need a prefix. */
417 notbuttons = menu_items_used;
418#endif
419
8e713be6 420 for (tail = keymap; CONSP (tail); tail = XCDR (tail))
18686d47 421 {
de57a39c
RS
422 GCPRO2 (keymap, pending_maps);
423 /* Look at each key binding, and if it is a menu item add it
424 to this menu. */
8e713be6 425 item = XCAR (tail);
b5bb2705 426 if (CONSP (item))
8e713be6 427 single_menu_item (XCAR (item), XCDR (item),
de57a39c 428 &pending_maps, notreal, maxdepth, &notbuttons);
b5bb2705 429 else if (VECTORP (item))
78589e07
RS
430 {
431 /* Loop over the char values represented in the vector. */
432 int len = XVECTOR (item)->size;
433 int c;
434 for (c = 0; c < len; c++)
435 {
436 Lisp_Object character;
33b43fa6 437 XSETFASTINT (character, c);
de57a39c
RS
438 single_menu_item (character, XVECTOR (item)->contents[c],
439 &pending_maps, notreal, maxdepth, &notbuttons);
78589e07 440 }
18686d47 441 }
de57a39c 442 UNGCPRO;
18686d47 443 }
78589e07
RS
444
445 /* Process now any submenus which want to be panes at this level. */
446 while (!NILP (pending_maps))
447 {
101bb4a5 448 Lisp_Object elt, eltcdr, string;
78589e07 449 elt = Fcar (pending_maps);
8e713be6
KR
450 eltcdr = XCDR (elt);
451 string = XCAR (eltcdr);
101bb4a5
RS
452 /* We no longer discard the @ from the beginning of the string here.
453 Instead, we do this in xmenu_show. */
454 single_keymap_panes (Fcar (elt), string,
8e713be6 455 XCDR (eltcdr), notreal, maxdepth - 1);
78589e07
RS
456 pending_maps = Fcdr (pending_maps);
457 }
18686d47 458}
78589e07 459\f
de57a39c
RS
460/* This is a subroutine of single_keymap_panes that handles one
461 keymap entry.
462 KEY is a key in a keymap and ITEM is its binding.
463 PENDING_MAPS_PTR points to a list of keymaps waiting to be made into
464 separate panes.
465 If NOTREAL is nonzero, only check for equivalent key bindings, don't
466 evaluate expressions in menu items and don't make any menu.
467 If we encounter submenus deeper than MAXDEPTH levels, ignore them.
468 NOTBUTTONS_PTR is only used when simulating toggle boxes and radio
469 buttons. It points to variable notbuttons in single_keymap_panes,
470 which keeps track of if we have seen a button in this menu or not. */
471
472static void
473single_menu_item (key, item, pending_maps_ptr, notreal, maxdepth,
474 notbuttons_ptr)
475 Lisp_Object key, item;
476 Lisp_Object *pending_maps_ptr;
477 int maxdepth, notreal;
478 int *notbuttons_ptr;
479{
480 Lisp_Object def, map, item_string, enabled;
481 struct gcpro gcpro1, gcpro2;
482 int res;
483
484 /* Parse the menu item and leave the result in item_properties. */
485 GCPRO2 (key, item);
486 res = parse_menu_item (item, notreal, 0);
487 UNGCPRO;
488 if (!res)
489 return; /* Not a menu item. */
490
491 map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
492
493 if (notreal)
494 {
495 /* We don't want to make a menu, just traverse the keymaps to
496 precompute equivalent key bindings. */
497 if (!NILP (map))
498 single_keymap_panes (map, Qnil, key, 1, maxdepth - 1);
499 return;
500 }
501
502 enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE];
503 item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME];
504
505 if (!NILP (map) && XSTRING (item_string)->data[0] == '@')
506 {
507 if (!NILP (enabled))
508 /* An enabled separate pane. Remember this to handle it later. */
509 *pending_maps_ptr = Fcons (Fcons (map, Fcons (item_string, key)),
510 *pending_maps_ptr);
511 return;
512 }
513
514#ifndef HAVE_BOXES
515 /* Simulate radio buttons and toggle boxes by putting a prefix in
516 front of them. */
517 {
518 Lisp_Object prefix = Qnil;
519 Lisp_Object type = XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE];
520 if (!NILP (type))
521 {
522 Lisp_Object selected
523 = XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED];
524
525 if (*notbuttons_ptr)
526 /* The first button. Line up previous items in this menu. */
527 {
528 int index = *notbuttons_ptr; /* Index for first item this menu. */
529 int submenu = 0;
530 Lisp_Object tem;
531 while (index < menu_items_used)
532 {
533 tem
534 = XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME];
535 if (NILP (tem))
536 {
537 index++;
538 submenu++; /* Skip sub menu. */
539 }
540 else if (EQ (tem, Qlambda))
541 {
542 index++;
543 submenu--; /* End sub menu. */
544 }
545 else if (EQ (tem, Qt))
546 index += 3; /* Skip new pane marker. */
547 else if (EQ (tem, Qquote))
548 index++; /* Skip a left, right divider. */
549 else
550 {
551 if (!submenu && XSTRING (tem)->data[0] != '\0'
552 && XSTRING (tem)->data[0] != '-')
553 XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME]
554 = concat2 (build_string (" "), tem);
555 index += MENU_ITEMS_ITEM_LENGTH;
556 }
557 }
558 *notbuttons_ptr = 0;
559 }
560
561 /* Calculate prefix, if any, for this item. */
562 if (EQ (type, QCtoggle))
563 prefix = build_string (NILP (selected) ? "[ ] " : "[X] ");
564 else if (EQ (type, QCradio))
565 prefix = build_string (NILP (selected) ? "( ) " : "(*) ");
566 }
567 /* Not a button. If we have earlier buttons, then we need a prefix. */
568 else if (!*notbuttons_ptr && XSTRING (item_string)->data[0] != '\0'
569 && XSTRING (item_string)->data[0] != '-')
570 prefix = build_string (" ");
571
572 if (!NILP (prefix))
573 item_string = concat2 (prefix, item_string);
574 }
575#endif /* not HAVE_BOXES */
576
577#ifndef USE_X_TOOLKIT
578 if (!NILP(map))
579 /* Indicate visually that this is a submenu. */
580 item_string = concat2 (item_string, build_string (" >"));
581#endif
582
583 push_menu_item (item_string, enabled, key,
584 XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
3427a3db
GM
585 XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
586 XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
587 XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED]);
de57a39c
RS
588
589#ifdef USE_X_TOOLKIT
590 /* Display a submenu using the toolkit. */
591 if (! (NILP (map) || NILP (enabled)))
592 {
593 push_submenu_start ();
594 single_keymap_panes (map, Qnil, key, 0, maxdepth - 1);
595 push_submenu_end ();
596 }
597#endif
598}
599\f
8e6208c5 600/* Push all the panes and items of a menu described by the
78589e07
RS
601 alist-of-alists MENU.
602 This handles old-fashioned calls to x-popup-menu. */
18686d47 603
78589e07
RS
604static void
605list_of_panes (menu)
18686d47 606 Lisp_Object menu;
18686d47 607{
78589e07
RS
608 Lisp_Object tail;
609
610 init_menu_items ();
611
612 for (tail = menu; !NILP (tail); tail = Fcdr (tail))
613 {
614 Lisp_Object elt, pane_name, pane_data;
615 elt = Fcar (tail);
616 pane_name = Fcar (elt);
617 CHECK_STRING (pane_name, 0);
618 push_menu_pane (pane_name, Qnil);
619 pane_data = Fcdr (elt);
620 CHECK_CONS (pane_data, 0);
621 list_of_items (pane_data);
622 }
623
624 finish_menu_items ();
625}
626
627/* Push the items in a single pane defined by the alist PANE. */
628
629static void
630list_of_items (pane)
631 Lisp_Object pane;
632{
633 Lisp_Object tail, item, item1;
634
635 for (tail = pane; !NILP (tail); tail = Fcdr (tail))
636 {
637 item = Fcar (tail);
638 if (STRINGP (item))
3427a3db 639 push_menu_item (item, Qnil, Qnil, Qt, Qnil, Qnil, Qnil);
fcaa7665
RS
640 else if (NILP (item))
641 push_left_right_boundary ();
78589e07
RS
642 else
643 {
644 CHECK_CONS (item, 0);
645 item1 = Fcar (item);
646 CHECK_STRING (item1, 1);
3427a3db 647 push_menu_item (item1, Qt, Fcdr (item), Qt, Qnil, Qnil, Qnil);
78589e07
RS
648 }
649 }
650}
651\f
540e52d1 652DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
78589e07
RS
653 "Pop up a deck-of-cards menu and return user's selection.\n\
654POSITION is a position specification. This is either a mouse button event\n\
655or a list ((XOFFSET YOFFSET) WINDOW)\n\
748a0e1f 656where XOFFSET and YOFFSET are positions in pixels from the top left\n\
78589e07
RS
657corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.)\n\
658This controls the position of the center of the first line\n\
659in the first pane of the menu, not the top left of the menu as a whole.\n\
660If POSITION is t, it means to use the current mouse position.\n\
661\n\
662MENU is a specifier for a menu. For the simplest case, MENU is a keymap.\n\
663The menu items come from key bindings that have a menu string as well as\n\
664a definition; actually, the \"definition\" in such a key binding looks like\n\
665\(STRING . REAL-DEFINITION). To give the menu a title, put a string into\n\
666the keymap as a top-level element.\n\n\
a7ebd443
RS
667If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.\n\
668Otherwise, REAL-DEFINITION should be a valid key binding definition.\n\
669\n\
78589e07
RS
670You can also use a list of keymaps as MENU.\n\
671 Then each keymap makes a separate pane.\n\
672When MENU is a keymap or a list of keymaps, the return value\n\
673is a list of events.\n\n\
a7ebd443 674\n\
78589e07
RS
675Alternatively, you can specify a menu of multiple panes\n\
676 with a list of the form (TITLE PANE1 PANE2...),\n\
677where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
678Each ITEM is normally a cons cell (STRING . VALUE);\n\
679but a string can appear as an item--that makes a nonselectable line\n\
680in the menu.\n\
681With this form of menu, the return value is VALUE from the chosen item.\n\
682\n\
683If POSITION is nil, don't display the menu at all, just precalculate the\n\
684cached information about equivalent key sequences.")
685 (position, menu)
686 Lisp_Object position, menu;
687{
688 int number_of_panes, panes;
18686d47 689 Lisp_Object keymap, tem;
78589e07
RS
690 int xpos, ypos;
691 Lisp_Object title;
692 char *error_name;
693 Lisp_Object selection;
18686d47 694 int i, j;
78589e07
RS
695 FRAME_PTR f;
696 Lisp_Object x, y, window;
697 int keymaps = 0;
9685a93f 698 int for_click = 0;
78589e07
RS
699 struct gcpro gcpro1;
700
1e659e4c 701#ifdef HAVE_MENUS
78589e07
RS
702 if (! NILP (position))
703 {
101bb4a5
RS
704 check_x ();
705
78589e07 706 /* Decode the first argument: find the window and the coordinates. */
9572375b 707 if (EQ (position, Qt)
8e713be6 708 || (CONSP (position) && EQ (XCAR (position), Qmenu_bar)))
78589e07
RS
709 {
710 /* Use the mouse's current position. */
ac8f8f7d 711 FRAME_PTR new_f = selected_frame;
78589e07 712 Lisp_Object bar_window;
dfcf069d 713 enum scroll_bar_part part;
78589e07
RS
714 unsigned long time;
715
b137c582 716 if (mouse_position_hook)
46b657e2
RS
717 (*mouse_position_hook) (&new_f, 1, &bar_window,
718 &part, &x, &y, &time);
5ca2ef64 719 if (new_f != 0)
a39f0477 720 XSETFRAME (window, new_f);
5ca2ef64
RS
721 else
722 {
723 window = selected_window;
33b43fa6
KH
724 XSETFASTINT (x, 0);
725 XSETFASTINT (y, 0);
5ca2ef64 726 }
78589e07
RS
727 }
728 else
729 {
730 tem = Fcar (position);
b5bb2705 731 if (CONSP (tem))
78589e07
RS
732 {
733 window = Fcar (Fcdr (position));
734 x = Fcar (tem);
735 y = Fcar (Fcdr (tem));
736 }
737 else
738 {
9685a93f 739 for_click = 1;
78589e07
RS
740 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
741 window = Fcar (tem); /* POSN_WINDOW (tem) */
742 tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
743 x = Fcar (tem);
744 y = Fcdr (tem);
78589e07
RS
745 }
746 }
747
748 CHECK_NUMBER (x, 0);
749 CHECK_NUMBER (y, 0);
750
751 /* Decode where to put the menu. */
752
b5bb2705 753 if (FRAMEP (window))
78589e07
RS
754 {
755 f = XFRAME (window);
78589e07
RS
756 xpos = 0;
757 ypos = 0;
758 }
b5bb2705 759 else if (WINDOWP (window))
78589e07
RS
760 {
761 CHECK_LIVE_WINDOW (window, 0);
762 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
763
99d3fac7
KH
764 xpos = (FONT_WIDTH (f->output_data.x->font)
765 * XFASTINT (XWINDOW (window)->left));
766 ypos = (f->output_data.x->line_height
767 * XFASTINT (XWINDOW (window)->top));
78589e07
RS
768 }
769 else
770 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
771 but I don't want to make one now. */
772 CHECK_WINDOW (window, 0);
773
774 xpos += XINT (x);
775 ypos += XINT (y);
bfc524bc
RS
776
777 XSETFRAME (Vmenu_updating_frame, f);
78589e07 778 }
bfc524bc 779 Vmenu_updating_frame = Qnil;
1e659e4c 780#endif /* HAVE_MENUS */
78589e07
RS
781
782 title = Qnil;
783 GCPRO1 (title);
784
785 /* Decode the menu items from what was specified. */
18686d47
RS
786
787 keymap = Fkeymapp (menu);
788 tem = Qnil;
b5bb2705 789 if (CONSP (menu))
18686d47
RS
790 tem = Fkeymapp (Fcar (menu));
791 if (!NILP (keymap))
792 {
793 /* We were given a keymap. Extract menu info from the keymap. */
794 Lisp_Object prompt;
795 keymap = get_keymap (menu);
796
78589e07 797 /* Extract the detailed info to make one pane. */
101bb4a5 798 keymap_panes (&menu, 1, NILP (position));
78589e07 799
18686d47
RS
800 /* Search for a string appearing directly as an element of the keymap.
801 That string is the title of the menu. */
802 prompt = map_prompt (keymap);
4f9ad016
RS
803 if (NILP (title) && !NILP (prompt))
804 title = prompt;
18686d47 805
78589e07
RS
806 /* Make that be the pane title of the first pane. */
807 if (!NILP (prompt) && menu_items_n_panes >= 0)
808 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
809
810 keymaps = 1;
18686d47
RS
811 }
812 else if (!NILP (tem))
813 {
814 /* We were given a list of keymaps. */
18686d47
RS
815 int nmaps = XFASTINT (Flength (menu));
816 Lisp_Object *maps
817 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
818 int i;
78589e07
RS
819
820 title = Qnil;
18686d47
RS
821
822 /* The first keymap that has a prompt string
823 supplies the menu title. */
b5bb2705 824 for (tem = menu, i = 0; CONSP (tem); tem = Fcdr (tem))
18686d47 825 {
78589e07
RS
826 Lisp_Object prompt;
827
18686d47
RS
828 maps[i++] = keymap = get_keymap (Fcar (tem));
829
830 prompt = map_prompt (keymap);
78589e07
RS
831 if (NILP (title) && !NILP (prompt))
832 title = prompt;
18686d47
RS
833 }
834
835 /* Extract the detailed info to make one pane. */
101bb4a5 836 keymap_panes (maps, nmaps, NILP (position));
78589e07
RS
837
838 /* Make the title be the pane title of the first pane. */
839 if (!NILP (title) && menu_items_n_panes >= 0)
840 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
841
842 keymaps = 1;
18686d47
RS
843 }
844 else
845 {
846 /* We were given an old-fashioned menu. */
78589e07
RS
847 title = Fcar (menu);
848 CHECK_STRING (title, 1);
18686d47 849
78589e07 850 list_of_panes (Fcdr (menu));
18686d47 851
78589e07
RS
852 keymaps = 0;
853 }
18686d47 854
78589e07 855 if (NILP (position))
18686d47 856 {
78589e07
RS
857 discard_menu_items ();
858 UNGCPRO;
859 return Qnil;
18686d47
RS
860 }
861
1e659e4c 862#ifdef HAVE_MENUS
78589e07
RS
863 /* Display them in a menu. */
864 BLOCK_INPUT;
18686d47 865
673a6211 866 selection = xmenu_show (f, xpos, ypos, for_click,
78589e07
RS
867 keymaps, title, &error_name);
868 UNBLOCK_INPUT;
18686d47 869
78589e07 870 discard_menu_items ();
18686d47 871
78589e07 872 UNGCPRO;
1e659e4c 873#endif /* HAVE_MENUS */
18686d47 874
78589e07
RS
875 if (error_name) error (error_name);
876 return selection;
18686d47 877}
165e1749 878
1e659e4c
RS
879#ifdef HAVE_MENUS
880
540e52d1 881DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 2, 0,
165e1749 882 "Pop up a dialog box and return user's selection.\n\
99fe880d
RS
883POSITION specifies which frame to use.\n\
884This is normally a mouse button event or a window or frame.\n\
885If POSITION is t, it means to use the frame the mouse is on.\n\
886The dialog box appears in the middle of the specified frame.\n\
165e1749 887\n\
99fe880d
RS
888CONTENTS specifies the alternatives to display in the dialog box.\n\
889It is a list of the form (TITLE ITEM1 ITEM2...).\n\
890Each ITEM is a cons cell (STRING . VALUE).\n\
fcaa7665
RS
891The return value is VALUE from the chosen item.\n\n\
892An ITEM may also be just a string--that makes a nonselectable item.\n\
893An ITEM may also be nil--that means to put all preceding items\n\
894on the left of the dialog box and all following items on the right.\n\
895\(By default, approximately half appear on each side.)")
99fe880d
RS
896 (position, contents)
897 Lisp_Object position, contents;
165e1749 898{
165e1749 899 FRAME_PTR f;
99fe880d 900 Lisp_Object window;
165e1749
FP
901
902 check_x ();
903
99fe880d 904 /* Decode the first argument: find the window or frame to use. */
88d4f6ec 905 if (EQ (position, Qt)
8e713be6 906 || (CONSP (position) && EQ (XCAR (position), Qmenu_bar)))
165e1749 907 {
b14db4d7 908#if 0 /* Using the frame the mouse is on may not be right. */
99fe880d 909 /* Use the mouse's current position. */
ac8f8f7d 910 FRAME_PTR new_f = selected_frame;
99fe880d
RS
911 Lisp_Object bar_window;
912 int part;
913 unsigned long time;
914 Lisp_Object x, y;
165e1749 915
46b657e2 916 (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
5ca2ef64 917
99fe880d 918 if (new_f != 0)
a39f0477 919 XSETFRAME (window, new_f);
99fe880d
RS
920 else
921 window = selected_window;
b14db4d7 922#endif
88d4f6ec 923 window = selected_window;
99fe880d
RS
924 }
925 else if (CONSP (position))
926 {
927 Lisp_Object tem;
928 tem = Fcar (position);
b5bb2705 929 if (CONSP (tem))
99fe880d 930 window = Fcar (Fcdr (position));
80670155
RS
931 else
932 {
99fe880d
RS
933 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
934 window = Fcar (tem); /* POSN_WINDOW (tem) */
165e1749 935 }
165e1749 936 }
99fe880d
RS
937 else if (WINDOWP (position) || FRAMEP (position))
938 window = position;
a9be6839
RS
939 else
940 window = Qnil;
165e1749 941
99fe880d 942 /* Decode where to put the menu. */
165e1749 943
b5bb2705 944 if (FRAMEP (window))
99fe880d 945 f = XFRAME (window);
b5bb2705 946 else if (WINDOWP (window))
165e1749 947 {
99fe880d
RS
948 CHECK_LIVE_WINDOW (window, 0);
949 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
165e1749 950 }
99fe880d
RS
951 else
952 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
953 but I don't want to make one now. */
954 CHECK_WINDOW (window, 0);
165e1749 955
99fe880d
RS
956#ifndef USE_X_TOOLKIT
957 /* Display a menu with these alternatives
958 in the middle of frame F. */
959 {
960 Lisp_Object x, y, frame, newpos;
a39f0477
KH
961 XSETFRAME (frame, f);
962 XSETINT (x, x_pixel_width (f) / 2);
963 XSETINT (y, x_pixel_height (f) / 2);
99fe880d
RS
964 newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
965
966 return Fx_popup_menu (newpos,
967 Fcons (Fcar (contents), Fcons (contents, Qnil)));
968 }
969#else
970 {
971 Lisp_Object title;
972 char *error_name;
973 Lisp_Object selection;
165e1749 974
99fe880d
RS
975 /* Decode the dialog items from what was specified. */
976 title = Fcar (contents);
977 CHECK_STRING (title, 1);
165e1749 978
99fe880d 979 list_of_panes (Fcons (contents, Qnil));
165e1749 980
99fe880d
RS
981 /* Display them in a dialog box. */
982 BLOCK_INPUT;
f8c2630d 983 selection = xdialog_show (f, 0, title, &error_name);
99fe880d 984 UNBLOCK_INPUT;
165e1749 985
99fe880d
RS
986 discard_menu_items ();
987
988 if (error_name) error (error_name);
989 return selection;
990 }
7464b131 991#endif
392d3f4b 992}
78589e07
RS
993\f
994#ifdef USE_X_TOOLKIT
18686d47 995
4dedbfe0 996/* Loop in Xt until the menu pulldown or dialog popup has been
f56e7ad2
RS
997 popped down (deactivated). This is used for x-popup-menu
998 and x-popup-dialog; it is not used for the menu bar any more.
c98fcf4b 999
2e2b8e22 1000 NOTE: All calls to popup_get_selection should be protected
c98fcf4b 1001 with BLOCK_INPUT, UNBLOCK_INPUT wrappers. */
aa669def 1002
4dedbfe0 1003void
2e2b8e22 1004popup_get_selection (initial_event, dpyinfo, id)
4dedbfe0 1005 XEvent *initial_event;
aa669def 1006 struct x_display_info *dpyinfo;
2e2b8e22 1007 LWLIB_ID id;
78589e07 1008{
4dedbfe0 1009 XEvent event;
78589e07 1010
aa669def
RS
1011 /* Define a queue to save up for later unreading
1012 all X events that don't pertain to the menu. */
1013 struct event_queue
1014 {
1015 XEvent event;
1016 struct event_queue *next;
1017 };
1018
1019 struct event_queue *queue = NULL;
1020 struct event_queue *queue_tmp;
1021
4dedbfe0
PR
1022 if (initial_event)
1023 event = *initial_event;
1024 else
1025 XtAppNextEvent (Xt_app_con, &event);
78589e07 1026
4dedbfe0 1027 while (1)
78589e07 1028 {
aa669def
RS
1029 /* Handle expose events for editor frames right away. */
1030 if (event.type == Expose)
1031 process_expose_from_menu (event);
10624005
KH
1032 /* Make sure we don't consider buttons grabbed after menu goes.
1033 And make sure to deactivate for any ButtonRelease,
1034 even if XtDispatchEvent doesn't do that. */
aa669def
RS
1035 else if (event.type == ButtonRelease
1036 && dpyinfo->display == event.xbutton.display)
10624005
KH
1037 {
1038 dpyinfo->grabbed &= ~(1 << event.xbutton.button);
1039 popup_activated_flag = 0;
c8b5aa3d
RS
1040#ifdef USE_MOTIF /* Pretending that the event came from a
1041 Btn1Down seems the only way to convince Motif to
1042 activate its callbacks; setting the XmNmenuPost
1043 isn't working. --marcus@sysc.pdx.edu. */
1044 event.xbutton.button = 1;
1045#endif
10624005 1046 }
2e2b8e22
KH
1047 /* If the user presses a key, deactivate the menu.
1048 The user is likely to do that if we get wedged. */
1049 else if (event.type == KeyPress
1050 && dpyinfo->display == event.xbutton.display)
1051 {
09155689
AS
1052 KeySym keysym = XLookupKeysym (&event.xkey, 0);
1053 if (!IsModifierKey (keysym))
1054 {
1055 popup_activated_flag = 0;
1056 break;
1057 }
2e2b8e22 1058 }
d31d42cc
RS
1059 /* Button presses outside the menu also pop it down. */
1060 else if (event.type == ButtonPress
1061 && event.xany.display == dpyinfo->display
1062 && x_any_window_to_frame (dpyinfo, event.xany.window))
1063 {
1064 popup_activated_flag = 0;
1065 break;
1066 }
aa669def
RS
1067
1068 /* Queue all events not for this popup,
10624005 1069 except for Expose, which we've already handled, and ButtonRelease.
9572375b
KH
1070 Note that the X window is associated with the frame if this
1071 is a menu bar popup, but not if it's a dialog box. So we use
1072 x_non_menubar_window_to_frame, not x_any_window_to_frame. */
aa669def 1073 if (event.type != Expose
10624005
KH
1074 && !(event.type == ButtonRelease
1075 && dpyinfo->display == event.xbutton.display)
aa669def 1076 && (event.xany.display != dpyinfo->display
9572375b 1077 || x_non_menubar_window_to_frame (dpyinfo, event.xany.window)))
aa669def 1078 {
aa669def
RS
1079 queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
1080
1081 if (queue_tmp != NULL)
1082 {
1083 queue_tmp->event = event;
1084 queue_tmp->next = queue;
1085 queue = queue_tmp;
1086 }
1087 }
9572375b
KH
1088 else
1089 XtDispatchEvent (&event);
aa669def
RS
1090
1091 if (!popup_activated ())
4dedbfe0
PR
1092 break;
1093 XtAppNextEvent (Xt_app_con, &event);
78589e07 1094 }
aa669def
RS
1095
1096 /* Unread any events that we got but did not handle. */
1097 while (queue != NULL)
1098 {
1099 queue_tmp = queue;
1100 XPutBackEvent (queue_tmp->event.xany.display, &queue_tmp->event);
1101 queue = queue_tmp->next;
1102 free ((char *)queue_tmp);
1103 /* Cause these events to get read as soon as we UNBLOCK_INPUT. */
1104 interrupt_input_pending = 1;
1105 }
78589e07
RS
1106}
1107
88766961
RS
1108/* Activate the menu bar of frame F.
1109 This is called from keyboard.c when it gets the
1110 menu_bar_activate_event out of the Emacs event queue.
1111
1112 To activate the menu bar, we use the X button-press event
ac78b144 1113 that was saved in saved_menu_event.
88766961
RS
1114 That makes the toolkit do its thing.
1115
1116 But first we recompute the menu bar contents (the whole tree).
1117
1118 The reason for saving the button event until here, instead of
1119 passing it to the toolkit right away, is that we can safely
1120 execute Lisp code. */
1121
dfcf069d 1122void
88766961
RS
1123x_activate_menubar (f)
1124 FRAME_PTR f;
1125{
ac78b144 1126 if (!f->output_data.x->saved_menu_event->type)
88766961
RS
1127 return;
1128
a9be6839 1129 set_frame_menubar (f, 0, 1);
88766961 1130 BLOCK_INPUT;
ac78b144 1131 XtDispatchEvent ((XEvent *) f->output_data.x->saved_menu_event);
88766961 1132 UNBLOCK_INPUT;
a9be6839 1133#ifdef USE_MOTIF
745c34fb
RS
1134 if (f->output_data.x->saved_menu_event->type == ButtonRelease)
1135 pending_menu_activation = 1;
a9be6839 1136#endif
745c34fb 1137
88766961 1138 /* Ignore this if we get it a second time. */
ac78b144 1139 f->output_data.x->saved_menu_event->type = 0;
88766961
RS
1140}
1141
c98fcf4b 1142/* Detect if a dialog or menu has been posted. */
aa669def 1143
4dedbfe0
PR
1144int
1145popup_activated ()
1146{
1147 return popup_activated_flag;
1148}
1149
1150
1151/* This callback is invoked when the user selects a menubar cascade
1152 pushbutton, but before the pulldown menu is posted. */
78589e07
RS
1153
1154static void
4dedbfe0 1155popup_activate_callback (widget, id, client_data)
78589e07
RS
1156 Widget widget;
1157 LWLIB_ID id;
1158 XtPointer client_data;
1159{
4dedbfe0 1160 popup_activated_flag = 1;
78589e07
RS
1161}
1162
4dedbfe0
PR
1163/* This callback is called from the menu bar pulldown menu
1164 when the user makes a selection.
1165 Figure out what the user chose
1166 and put the appropriate events into the keyboard buffer. */
1167
78589e07 1168static void
4dedbfe0 1169menubar_selection_callback (widget, id, client_data)
78589e07
RS
1170 Widget widget;
1171 LWLIB_ID id;
1172 XtPointer client_data;
1173{
c63f6952 1174 Lisp_Object prefix, entry;
88766961 1175 FRAME_PTR f = menubar_id_to_frame (id);
4dedbfe0
PR
1176 Lisp_Object vector;
1177 Lisp_Object *subprefix_stack;
1178 int submenu_depth = 0;
1179 int i;
1180
1181 if (!f)
1182 return;
1183 subprefix_stack = (Lisp_Object *) alloca (f->menu_bar_items_used * sizeof (Lisp_Object));
1184 vector = f->menu_bar_vector;
1185 prefix = Qnil;
1186 i = 0;
1187 while (i < f->menu_bar_items_used)
1188 {
4dedbfe0
PR
1189 if (EQ (XVECTOR (vector)->contents[i], Qnil))
1190 {
1191 subprefix_stack[submenu_depth++] = prefix;
1192 prefix = entry;
1193 i++;
1194 }
1195 else if (EQ (XVECTOR (vector)->contents[i], Qlambda))
1196 {
1197 prefix = subprefix_stack[--submenu_depth];
1198 i++;
1199 }
1200 else if (EQ (XVECTOR (vector)->contents[i], Qt))
1201 {
4cb35c39 1202 prefix = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
4dedbfe0
PR
1203 i += MENU_ITEMS_PANE_LENGTH;
1204 }
1205 else
1206 {
4cb35c39 1207 entry = XVECTOR (vector)->contents[i + MENU_ITEMS_ITEM_VALUE];
01d5e892
RS
1208 /* The EMACS_INT cast avoids a warning. There's no problem
1209 as long as pointers have enough bits to hold small integers. */
1210 if ((int) (EMACS_INT) client_data == i)
4dedbfe0
PR
1211 {
1212 int j;
1213 struct input_event buf;
4cb35c39 1214 Lisp_Object frame;
4dedbfe0 1215
4cb35c39 1216 XSETFRAME (frame, f);
4dedbfe0 1217 buf.kind = menu_bar_event;
9572375b 1218 buf.frame_or_window = Fcons (frame, Fcons (Qmenu_bar, Qnil));
4dedbfe0
PR
1219 kbd_buffer_store_event (&buf);
1220
1221 for (j = 0; j < submenu_depth; j++)
1222 if (!NILP (subprefix_stack[j]))
1223 {
1224 buf.kind = menu_bar_event;
4cb35c39 1225 buf.frame_or_window = Fcons (frame, subprefix_stack[j]);
4dedbfe0
PR
1226 kbd_buffer_store_event (&buf);
1227 }
1228
1229 if (!NILP (prefix))
1230 {
1231 buf.kind = menu_bar_event;
4cb35c39 1232 buf.frame_or_window = Fcons (frame, prefix);
4dedbfe0
PR
1233 kbd_buffer_store_event (&buf);
1234 }
1235
1236 buf.kind = menu_bar_event;
4cb35c39 1237 buf.frame_or_window = Fcons (frame, entry);
4dedbfe0
PR
1238 kbd_buffer_store_event (&buf);
1239
1240 return;
1241 }
1242 i += MENU_ITEMS_ITEM_LENGTH;
1243 }
1244 }
18686d47
RS
1245}
1246
4dedbfe0 1247/* This callback is invoked when a dialog or menu is finished being
c98fcf4b 1248 used and has been unposted. */
4dedbfe0 1249
165e1749 1250static void
4dedbfe0 1251popup_deactivate_callback (widget, id, client_data)
165e1749
FP
1252 Widget widget;
1253 LWLIB_ID id;
1254 XtPointer client_data;
1255{
4dedbfe0 1256 popup_activated_flag = 0;
165e1749
FP
1257}
1258
f9655c60
RS
1259/* Allocate a widget_value, blocking input. */
1260
1261widget_value *
1262xmalloc_widget_value ()
1263{
1264 widget_value *value;
1265
1266 BLOCK_INPUT;
1267 value = malloc_widget_value ();
1268 UNBLOCK_INPUT;
1269
1270 return value;
1271}
4dedbfe0
PR
1272
1273/* This recursively calls free_widget_value on the tree of widgets.
18686d47 1274 It must free all data that was malloc'ed for these widget_values.
78589e07
RS
1275 In Emacs, many slots are pointers into the data of Lisp_Strings, and
1276 must be left alone. */
1277
18686d47
RS
1278void
1279free_menubar_widget_value_tree (wv)
1280 widget_value *wv;
1281{
1282 if (! wv) return;
18686d47
RS
1283
1284 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
1285
1286 if (wv->contents && (wv->contents != (widget_value*)1))
1287 {
1288 free_menubar_widget_value_tree (wv->contents);
1289 wv->contents = (widget_value *) 0xDEADBEEF;
1290 }
1291 if (wv->next)
1292 {
1293 free_menubar_widget_value_tree (wv->next);
1294 wv->next = (widget_value *) 0xDEADBEEF;
1295 }
1296 BLOCK_INPUT;
1297 free_widget_value (wv);
1298 UNBLOCK_INPUT;
1299}
4dedbfe0
PR
1300\f
1301/* Return a tree of widget_value structures for a menu bar item
1302 whose event type is ITEM_KEY (with string ITEM_NAME)
1303 and whose contents come from the list of keymaps MAPS. */
1304
1305static widget_value *
1306single_submenu (item_key, item_name, maps)
1307 Lisp_Object item_key, item_name, maps;
1308{
1309 widget_value *wv, *prev_wv, *save_wv, *first_wv;
1310 int i;
1311 int submenu_depth = 0;
1312 Lisp_Object length;
1313 int len;
1314 Lisp_Object *mapvec;
1315 widget_value **submenu_stack;
1316 int mapno;
1317 int previous_items = menu_items_used;
71dca3e3 1318 int top_level_items = 0;
4dedbfe0
PR
1319
1320 length = Flength (maps);
1321 len = XINT (length);
1322
1323 /* Convert the list MAPS into a vector MAPVEC. */
1324 mapvec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
1325 for (i = 0; i < len; i++)
1326 {
1327 mapvec[i] = Fcar (maps);
1328 maps = Fcdr (maps);
1329 }
1330
1331 menu_items_n_panes = 0;
1332
1333 /* Loop over the given keymaps, making a pane for each map.
1334 But don't make a pane that is empty--ignore that map instead. */
1335 for (i = 0; i < len; i++)
71dca3e3 1336 {
846e8c10
RS
1337 if (SYMBOLP (mapvec[i])
1338 || (CONSP (mapvec[i])
1339 && NILP (Fkeymapp (mapvec[i]))))
71dca3e3 1340 {
846e8c10
RS
1341 /* Here we have a command at top level in the menu bar
1342 as opposed to a submenu. */
71dca3e3
RS
1343 top_level_items = 1;
1344 push_menu_pane (Qnil, Qnil);
3427a3db 1345 push_menu_item (item_name, Qt, item_key, mapvec[i], Qnil, Qnil, Qnil);
71dca3e3
RS
1346 }
1347 else
98381e42 1348 single_keymap_panes (mapvec[i], item_name, item_key, 0, 10);
71dca3e3 1349 }
4dedbfe0
PR
1350
1351 /* Create a tree of widget_value objects
1352 representing the panes and their items. */
1353
1354 submenu_stack
1355 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
f7fab165 1356 wv = xmalloc_widget_value ();
4dedbfe0
PR
1357 wv->name = "menu";
1358 wv->value = 0;
1359 wv->enabled = 1;
3427a3db 1360 wv->button_type = BUTTON_TYPE_NONE;
4dedbfe0
PR
1361 first_wv = wv;
1362 save_wv = 0;
71dca3e3 1363 prev_wv = 0;
4dedbfe0
PR
1364
1365 /* Loop over all panes and items made during this call
1366 and construct a tree of widget_value objects.
1367 Ignore the panes and items made by previous calls to
1368 single_submenu, even though those are also in menu_items. */
1369 i = previous_items;
1370 while (i < menu_items_used)
1371 {
1372 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1373 {
1374 submenu_stack[submenu_depth++] = save_wv;
1375 save_wv = prev_wv;
1376 prev_wv = 0;
1377 i++;
1378 }
1379 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1380 {
1381 prev_wv = save_wv;
1382 save_wv = submenu_stack[--submenu_depth];
1383 i++;
1384 }
1385 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1386 && submenu_depth != 0)
1387 i += MENU_ITEMS_PANE_LENGTH;
1388 /* Ignore a nil in the item list.
1389 It's meaningful only for dialog boxes. */
1390 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1391 i += 1;
1392 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1393 {
1394 /* Create a new pane. */
1395 Lisp_Object pane_name, prefix;
1396 char *pane_string;
1397 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1398 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
703dc2a8
KH
1399#ifndef HAVE_MULTILINGUAL_MENU
1400 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
1401 pane_name = string_make_unibyte (pane_name);
1402#endif
4dedbfe0
PR
1403 pane_string = (NILP (pane_name)
1404 ? "" : (char *) XSTRING (pane_name)->data);
1405 /* If there is just one top-level pane, put all its items directly
1406 under the top-level menu. */
1407 if (menu_items_n_panes == 1)
1408 pane_string = "";
1409
1410 /* If the pane has a meaningful name,
1411 make the pane a top-level menu item
1412 with its items as a submenu beneath it. */
1413 if (strcmp (pane_string, ""))
1414 {
f7fab165 1415 wv = xmalloc_widget_value ();
4dedbfe0
PR
1416 if (save_wv)
1417 save_wv->next = wv;
1418 else
1419 first_wv->contents = wv;
1420 wv->name = pane_string;
ffcb5a51
RS
1421 /* Ignore the @ that means "separate pane".
1422 This is a kludge, but this isn't worth more time. */
1423 if (!NILP (prefix) && wv->name[0] == '@')
4dedbfe0
PR
1424 wv->name++;
1425 wv->value = 0;
1426 wv->enabled = 1;
3427a3db 1427 wv->button_type = BUTTON_TYPE_NONE;
4dedbfe0
PR
1428 }
1429 save_wv = wv;
1430 prev_wv = 0;
1431 i += MENU_ITEMS_PANE_LENGTH;
1432 }
1433 else
1434 {
1435 /* Create a new item within current pane. */
3427a3db 1436 Lisp_Object item_name, enable, descrip, def, type, selected;
4dedbfe0
PR
1437 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
1438 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
1439 descrip
1440 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
a352a815 1441 def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
3427a3db
GM
1442 type = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_TYPE];
1443 selected = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_SELECTED];
1444
703dc2a8 1445#ifndef HAVE_MULTILINGUAL_MENU
3427a3db
GM
1446 if (STRING_MULTIBYTE (item_name))
1447 item_name = string_make_unibyte (item_name);
1448 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
1449 descrip = string_make_unibyte (descrip);
703dc2a8 1450#endif
3427a3db 1451
f7fab165 1452 wv = xmalloc_widget_value ();
4dedbfe0
PR
1453 if (prev_wv)
1454 prev_wv->next = wv;
71dca3e3 1455 else
4dedbfe0 1456 save_wv->contents = wv;
71dca3e3 1457
4dedbfe0
PR
1458 wv->name = (char *) XSTRING (item_name)->data;
1459 if (!NILP (descrip))
1460 wv->key = (char *) XSTRING (descrip)->data;
1461 wv->value = 0;
01d5e892
RS
1462 /* The EMACS_INT cast avoids a warning. There's no problem
1463 as long as pointers have enough bits to hold small integers. */
1464 wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0);
4dedbfe0 1465 wv->enabled = !NILP (enable);
3427a3db
GM
1466
1467 if (NILP (type))
1468 wv->button_type = BUTTON_TYPE_NONE;
1469 else if (EQ (type, QCradio))
1470 wv->button_type = BUTTON_TYPE_RADIO;
1471 else if (EQ (type, QCtoggle))
1472 wv->button_type = BUTTON_TYPE_TOGGLE;
1473 else
1474 abort ();
1475
1476 wv->selected = !NILP (selected);
1477
4dedbfe0
PR
1478 prev_wv = wv;
1479
1480 i += MENU_ITEMS_ITEM_LENGTH;
1481 }
1482 }
1483
71dca3e3
RS
1484 /* If we have just one "menu item"
1485 that was originally a button, return it by itself. */
1486 if (top_level_items && first_wv->contents && first_wv->contents->next == 0)
1487 {
1488 wv = first_wv->contents;
1489 free_widget_value (first_wv);
1490 return wv;
1491 }
1492
4dedbfe0
PR
1493 return first_wv;
1494}
1495\f
cffa74ea
FP
1496extern void EmacsFrameSetCharSize ();
1497
4bcdbab1
KH
1498/* Recompute all the widgets of frame F, when the menu bar
1499 has been changed. */
4dedbfe0 1500
18686d47 1501static void
6af6cbb5 1502update_frame_menubar (f)
18686d47
RS
1503 FRAME_PTR f;
1504{
7556890b 1505 struct x_output *x = f->output_data.x;
cffa74ea 1506 int columns, rows;
18686d47
RS
1507 int menubar_changed;
1508
4dedbfe0
PR
1509 Dimension shell_height;
1510
1511 /* We assume the menubar contents has changed if the global flag is set,
1512 or if the current buffer has changed, or if the menubar has never
1513 been updated before.
1514 */
18686d47
RS
1515 menubar_changed = (x->menubar_widget
1516 && !XtIsManaged (x->menubar_widget));
1517
1518 if (! (menubar_changed))
1519 return;
1520
1521 BLOCK_INPUT;
cffa74ea
FP
1522 /* Save the size of the frame because the pane widget doesn't accept to
1523 resize itself. So force it. */
1524 columns = f->width;
1525 rows = f->height;
1526
4dedbfe0
PR
1527 /* Do the voodoo which means "I'm changing lots of things, don't try to
1528 refigure sizes until I'm done." */
1529 lw_refigure_widget (x->column_widget, False);
cffa74ea 1530
18686d47
RS
1531 /* the order in which children are managed is the top to
1532 bottom order in which they are displayed in the paned window.
1533 First, remove the text-area widget.
1534 */
1535 XtUnmanageChild (x->edit_widget);
1536
1537 /* remove the menubar that is there now, and put up the menubar that
1538 should be there.
1539 */
1540 if (menubar_changed)
1541 {
1542 XtManageChild (x->menubar_widget);
1543 XtMapWidget (x->menubar_widget);
1544 XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0);
1545 }
1546
c98fcf4b 1547 /* Re-manage the text-area widget, and then thrash the sizes. */
18686d47 1548 XtManageChild (x->edit_widget);
4dedbfe0 1549 lw_refigure_widget (x->column_widget, True);
cffa74ea
FP
1550
1551 /* Force the pane widget to resize itself with the right values. */
1552 EmacsFrameSetCharSize (x->edit_widget, columns, rows);
1553
18686d47
RS
1554 UNBLOCK_INPUT;
1555}
1556
4bcdbab1
KH
1557/* Set the contents of the menubar widgets of frame F.
1558 The argument FIRST_TIME is currently ignored;
1559 it is set the first time this is called, from initialize_frame_menubar. */
1560
18686d47 1561void
88766961 1562set_frame_menubar (f, first_time, deep_p)
18686d47 1563 FRAME_PTR f;
706aa2f2 1564 int first_time;
88766961 1565 int deep_p;
18686d47 1566{
7556890b 1567 Widget menubar_widget = f->output_data.x->menubar_widget;
bd3a4da2 1568 Lisp_Object tail, items, frame;
4d19cb8e 1569 widget_value *wv, *first_wv, *prev_wv = 0;
5b3557df 1570 int i;
88766961 1571 LWLIB_ID id;
18686d47 1572
bfc524bc
RS
1573 XSETFRAME (Vmenu_updating_frame, f);
1574
7556890b
RS
1575 if (f->output_data.x->id == 0)
1576 f->output_data.x->id = next_menubar_widget_id++;
1577 id = f->output_data.x->id;
37a98547 1578
88766961
RS
1579 if (! menubar_widget)
1580 deep_p = 1;
745c34fb 1581 else if (pending_menu_activation && !deep_p)
a9be6839
RS
1582 deep_p = 1;
1583 /* Make the first call for any given frame always go deep. */
1584 else if (!f->output_data.x->saved_menu_event && !deep_p)
745c34fb
RS
1585 {
1586 deep_p = 1;
a9be6839
RS
1587 f->output_data.x->saved_menu_event = (XEvent*)xmalloc (sizeof (XEvent));
1588 f->output_data.x->saved_menu_event->type = 0;
745c34fb 1589 }
18686d47 1590
f7fab165 1591 wv = xmalloc_widget_value ();
18686d47
RS
1592 wv->name = "menubar";
1593 wv->value = 0;
1594 wv->enabled = 1;
3427a3db 1595 wv->button_type = BUTTON_TYPE_NONE;
4d19cb8e 1596 first_wv = wv;
8d8a3494 1597
88766961 1598 if (deep_p)
18686d47 1599 {
88766961
RS
1600 /* Make a widget-value tree representing the entire menu trees. */
1601
1602 struct buffer *prev = current_buffer;
1603 Lisp_Object buffer;
1604 int specpdl_count = specpdl_ptr - specpdl;
1605 int previous_menu_items_used = f->menu_bar_items_used;
1606 Lisp_Object *previous_items
1607 = (Lisp_Object *) alloca (previous_menu_items_used
1608 * sizeof (Lisp_Object));
1609
0b1cf399
RS
1610 /* If we are making a new widget, its contents are empty,
1611 do always reinitialize them. */
1612 if (! menubar_widget)
1613 previous_menu_items_used = 0;
1614
88766961
RS
1615 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
1616 specbind (Qinhibit_quit, Qt);
1617 /* Don't let the debugger step into this code
1618 because it is not reentrant. */
1619 specbind (Qdebug_on_next_call, Qnil);
1620
58226364 1621 record_unwind_protect (Fset_match_data, Fmatch_data (Qnil, Qnil));
88766961
RS
1622 if (NILP (Voverriding_local_map_menu_flag))
1623 {
1624 specbind (Qoverriding_terminal_local_map, Qnil);
1625 specbind (Qoverriding_local_map, Qnil);
1626 }
18686d47 1627
88766961 1628 set_buffer_internal_1 (XBUFFER (buffer));
18686d47 1629
88766961
RS
1630 /* Run the Lucid hook. */
1631 call1 (Vrun_hooks, Qactivate_menubar_hook);
1632 /* If it has changed current-menubar from previous value,
1633 really recompute the menubar from the value. */
1634 if (! NILP (Vlucid_menu_bar_dirty_flag))
1635 call0 (Qrecompute_lucid_menubar);
a57634d4 1636 safe_run_hooks (Qmenu_bar_update_hook);
88766961 1637 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
18686d47 1638
88766961 1639 items = FRAME_MENU_BAR_ITEMS (f);
8d8a3494 1640
88766961 1641 inhibit_garbage_collection ();
8d8a3494 1642
88766961
RS
1643 /* Save the frame's previous menu bar contents data. */
1644 bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
1645 previous_menu_items_used * sizeof (Lisp_Object));
8d8a3494 1646
88766961
RS
1647 /* Fill in the current menu bar contents. */
1648 menu_items = f->menu_bar_vector;
1649 menu_items_allocated = XVECTOR (menu_items)->size;
1650 init_menu_items ();
f04366cb 1651 for (i = 0; i < XVECTOR (items)->size; i += 4)
88766961
RS
1652 {
1653 Lisp_Object key, string, maps;
1654
1655 key = XVECTOR (items)->contents[i];
1656 string = XVECTOR (items)->contents[i + 1];
1657 maps = XVECTOR (items)->contents[i + 2];
1658 if (NILP (string))
1659 break;
1660
1661 wv = single_submenu (key, string, maps);
1662 if (prev_wv)
1663 prev_wv->next = wv;
1664 else
1665 first_wv->contents = wv;
1666 /* Don't set wv->name here; GC during the loop might relocate it. */
1667 wv->enabled = 1;
3427a3db 1668 wv->button_type = BUTTON_TYPE_NONE;
88766961
RS
1669 prev_wv = wv;
1670 }
1671
1672 finish_menu_items ();
1673
1674 set_buffer_internal_1 (prev);
8d8a3494 1675 unbind_to (specpdl_count, Qnil);
8d8a3494 1676
88766961
RS
1677 /* If there has been no change in the Lisp-level contents
1678 of the menu bar, skip redisplaying it. Just exit. */
1679
1680 for (i = 0; i < previous_menu_items_used; i++)
1681 if (menu_items_used == i
99d3fac7 1682 || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
88766961 1683 break;
62555c22 1684 if (i == menu_items_used && i == previous_menu_items_used && i != 0)
88766961
RS
1685 {
1686 free_menubar_widget_value_tree (first_wv);
1687 menu_items = Qnil;
1688
1689 return;
1690 }
1691
1692 /* Now GC cannot happen during the lifetime of the widget_value,
1693 so it's safe to store data from a Lisp_String. */
1694 wv = first_wv->contents;
f04366cb 1695 for (i = 0; i < XVECTOR (items)->size; i += 4)
88766961
RS
1696 {
1697 Lisp_Object string;
1698 string = XVECTOR (items)->contents[i + 1];
1699 if (NILP (string))
1700 break;
1701 wv->name = (char *) XSTRING (string)->data;
1702 wv = wv->next;
1703 }
1704
1705 f->menu_bar_vector = menu_items;
1706 f->menu_bar_items_used = menu_items_used;
1707 menu_items = Qnil;
4d19cb8e 1708 }
88766961
RS
1709 else
1710 {
1711 /* Make a widget-value tree containing
1712 just the top level menu bar strings. */
4d19cb8e 1713
88766961 1714 items = FRAME_MENU_BAR_ITEMS (f);
f04366cb 1715 for (i = 0; i < XVECTOR (items)->size; i += 4)
88766961
RS
1716 {
1717 Lisp_Object string;
1718
1719 string = XVECTOR (items)->contents[i + 1];
1720 if (NILP (string))
1721 break;
1722
f7fab165 1723 wv = xmalloc_widget_value ();
88766961
RS
1724 wv->name = (char *) XSTRING (string)->data;
1725 wv->value = 0;
1726 wv->enabled = 1;
3427a3db 1727 wv->button_type = BUTTON_TYPE_NONE;
fe8fa62f
RS
1728 /* This prevents lwlib from assuming this
1729 menu item is really supposed to be empty. */
1730 /* The EMACS_INT cast avoids a warning.
1731 This value just has to be different from small integers. */
1732 wv->call_data = (void *) (EMACS_INT) (-1);
88766961
RS
1733
1734 if (prev_wv)
1735 prev_wv->next = wv;
1736 else
1737 first_wv->contents = wv;
1738 prev_wv = wv;
1739 }
62555c22
RS
1740
1741 /* Forget what we thought we knew about what is in the
1742 detailed contents of the menu bar menus.
1743 Changing the top level always destroys the contents. */
1744 f->menu_bar_items_used = 0;
88766961 1745 }
4dedbfe0 1746
88766961 1747 /* Create or update the menu bar widget. */
aa669def
RS
1748
1749 BLOCK_INPUT;
1750
18686d47 1751 if (menubar_widget)
4dedbfe0
PR
1752 {
1753 /* Disable resizing (done for Motif!) */
7556890b 1754 lw_allow_resizing (f->output_data.x->widget, False);
4dedbfe0
PR
1755
1756 /* The third arg is DEEP_P, which says to consider the entire
1757 menu trees we supply, rather than just the menu bar item names. */
88766961 1758 lw_modify_all_widgets (id, first_wv, deep_p);
4dedbfe0 1759
c98fcf4b 1760 /* Re-enable the edit widget to resize. */
7556890b 1761 lw_allow_resizing (f->output_data.x->widget, True);
4dedbfe0 1762 }
18686d47
RS
1763 else
1764 {
88766961 1765 menubar_widget = lw_create_widget ("menubar", "menubar", id, first_wv,
7556890b 1766 f->output_data.x->column_widget,
4dedbfe0
PR
1767 0,
1768 popup_activate_callback,
1769 menubar_selection_callback,
1770 popup_deactivate_callback);
7556890b 1771 f->output_data.x->menubar_widget = menubar_widget;
18686d47 1772 }
1d1c1567
KH
1773
1774 {
1775 int menubar_size
7556890b
RS
1776 = (f->output_data.x->menubar_widget
1777 ? (f->output_data.x->menubar_widget->core.height
1778 + f->output_data.x->menubar_widget->core.border_width)
1d1c1567
KH
1779 : 0);
1780
f42aa681
RS
1781#if 0 /* Experimentally, we now get the right results
1782 for -geometry -0-0 without this. 24 Aug 96, rms. */
5ee80bd3 1783#ifdef USE_LUCID
1d1c1567
KH
1784 if (FRAME_EXTERNAL_MENU_BAR (f))
1785 {
1786 Dimension ibw = 0;
7556890b 1787 XtVaGetValues (f->output_data.x->column_widget,
1d1c1567
KH
1788 XtNinternalBorderWidth, &ibw, NULL);
1789 menubar_size += ibw;
1790 }
5ee80bd3 1791#endif /* USE_LUCID */
f42aa681 1792#endif /* 0 */
1d1c1567 1793
7556890b 1794 f->output_data.x->menubar_height = menubar_size;
1d1c1567 1795 }
18686d47
RS
1796
1797 free_menubar_widget_value_tree (first_wv);
1798
4bcdbab1 1799 update_frame_menubar (f);
18686d47
RS
1800
1801 UNBLOCK_INPUT;
1802}
85f487d1 1803
8e6208c5 1804/* Called from Fx_create_frame to create the initial menubar of a frame
4dedbfe0
PR
1805 before it is mapped, so that the window is mapped with the menubar already
1806 there instead of us tacking it on later and thrashing the window after it
1807 is visible. */
1808
1809void
1810initialize_frame_menubar (f)
1811 FRAME_PTR f;
1812{
1813 /* This function is called before the first chance to redisplay
1814 the frame. It has to be, so the frame will have the right size. */
1815 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
88766961 1816 set_frame_menubar (f, 1, 1);
4dedbfe0
PR
1817}
1818
1819/* Get rid of the menu bar of frame F, and free its storage.
1820 This is used when deleting a frame, and when turning off the menu bar. */
1821
85f487d1
FP
1822void
1823free_frame_menubar (f)
1824 FRAME_PTR f;
1825{
1826 Widget menubar_widget;
1827 int id;
1828
7556890b 1829 menubar_widget = f->output_data.x->menubar_widget;
a45bad2a
RS
1830
1831 f->output_data.x->menubar_height = 0;
85f487d1
FP
1832
1833 if (menubar_widget)
1834 {
1835 BLOCK_INPUT;
7556890b 1836 lw_destroy_all_widgets ((LWLIB_ID) f->output_data.x->id);
85f487d1
FP
1837 UNBLOCK_INPUT;
1838 }
1839}
78589e07 1840
78589e07
RS
1841#endif /* USE_X_TOOLKIT */
1842\f
1843/* xmenu_show actually displays a menu using the panes and items in menu_items
1844 and returns the value selected from it.
1845 There are two versions of xmenu_show, one for Xt and one for Xlib.
1846 Both assume input is blocked by the caller. */
1847
1848/* F is the frame the menu is for.
1849 X and Y are the frame-relative specified position,
1850 relative to the inside upper left corner of the frame F.
c8b5aa3d 1851 FOR_CLICK is nonzero if this menu was invoked for a mouse click.
78589e07
RS
1852 KEYMAPS is 1 if this menu was specified with keymaps;
1853 in that case, we return a list containing the chosen item's value
1854 and perhaps also the pane's prefix.
1855 TITLE is the specified menu title.
1856 ERROR is a place to store an error message string in case of failure.
1857 (We return nil on failure, but the value doesn't actually matter.) */
18686d47
RS
1858
1859#ifdef USE_X_TOOLKIT
18686d47 1860
8ed87156 1861/* We need a unique id for each widget handled by the Lucid Widget
cc17e9bf
KH
1862 library.
1863
1864 For the main windows, and popup menus, we use this counter,
88766961 1865 which we increment each time after use. This starts from 1<<16.
cc17e9bf 1866
88766961
RS
1867 For menu bars, we use numbers starting at 0, counted in
1868 next_menubar_widget_id. */
8ed87156 1869LWLIB_ID widget_id_tick;
165e1749 1870
4dedbfe0
PR
1871#ifdef __STDC__
1872static Lisp_Object *volatile menu_item_selection;
1873#else
1874static Lisp_Object *menu_item_selection;
1875#endif
1876
1877static void
1878popup_selection_callback (widget, id, client_data)
1879 Widget widget;
1880 LWLIB_ID id;
1881 XtPointer client_data;
1882{
1883 menu_item_selection = (Lisp_Object *) client_data;
1884}
1885
78589e07 1886static Lisp_Object
673a6211 1887xmenu_show (f, x, y, for_click, keymaps, title, error)
18686d47 1888 FRAME_PTR f;
18686d47
RS
1889 int x;
1890 int y;
9685a93f 1891 int for_click;
78589e07
RS
1892 int keymaps;
1893 Lisp_Object title;
1894 char **error;
18686d47 1895{
78589e07 1896 int i;
cc17e9bf 1897 LWLIB_ID menu_id;
18686d47 1898 Widget menu;
ffcb5a51 1899 Arg av[2];
60f312e2 1900 int ac = 0;
78589e07 1901 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
101bb4a5
RS
1902 widget_value **submenu_stack
1903 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
1904 Lisp_Object *subprefix_stack
1905 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
1906 int submenu_depth = 0;
ffcb5a51 1907 XButtonPressedEvent dummy;
4e8d3549 1908
78c8278d 1909 int first_pane;
a51b963c 1910 int next_release_must_exit = 0;
78c8278d 1911
78589e07
RS
1912 *error = NULL;
1913
742f715d
KH
1914 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
1915 {
1916 *error = "Empty menu";
1917 return Qnil;
1918 }
63c414df 1919
78589e07
RS
1920 /* Create a tree of widget_value objects
1921 representing the panes and their items. */
f7fab165 1922 wv = xmalloc_widget_value ();
78589e07
RS
1923 wv->name = "menu";
1924 wv->value = 0;
1925 wv->enabled = 1;
3427a3db 1926 wv->button_type = BUTTON_TYPE_NONE;
78589e07 1927 first_wv = wv;
78c8278d 1928 first_pane = 1;
78589e07
RS
1929
1930 /* Loop over all panes and items, filling in the tree. */
1931 i = 0;
1932 while (i < menu_items_used)
1933 {
101bb4a5
RS
1934 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1935 {
1936 submenu_stack[submenu_depth++] = save_wv;
1937 save_wv = prev_wv;
1938 prev_wv = 0;
78c8278d 1939 first_pane = 1;
101bb4a5
RS
1940 i++;
1941 }
1942 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1943 {
1944 prev_wv = save_wv;
1945 save_wv = submenu_stack[--submenu_depth];
78c8278d 1946 first_pane = 0;
101bb4a5
RS
1947 i++;
1948 }
1949 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1950 && submenu_depth != 0)
1951 i += MENU_ITEMS_PANE_LENGTH;
fcaa7665
RS
1952 /* Ignore a nil in the item list.
1953 It's meaningful only for dialog boxes. */
1954 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1955 i += 1;
101bb4a5 1956 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
78589e07
RS
1957 {
1958 /* Create a new pane. */
1959 Lisp_Object pane_name, prefix;
1960 char *pane_string;
1961 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1962 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
703dc2a8
KH
1963#ifndef HAVE_MULTILINGUAL_MENU
1964 if (!NILP (pane_name) && STRING_MULTIBYTE (pane_name))
1965 pane_name = string_make_unibyte (pane_name);
1966#endif
78589e07
RS
1967 pane_string = (NILP (pane_name)
1968 ? "" : (char *) XSTRING (pane_name)->data);
101bb4a5 1969 /* If there is just one top-level pane, put all its items directly
78589e07
RS
1970 under the top-level menu. */
1971 if (menu_items_n_panes == 1)
1972 pane_string = "";
1973
1974 /* If the pane has a meaningful name,
1975 make the pane a top-level menu item
1976 with its items as a submenu beneath it. */
78c8278d 1977 if (!keymaps && strcmp (pane_string, ""))
78589e07 1978 {
f7fab165 1979 wv = xmalloc_widget_value ();
78589e07
RS
1980 if (save_wv)
1981 save_wv->next = wv;
1982 else
1983 first_wv->contents = wv;
1984 wv->name = pane_string;
1985 if (keymaps && !NILP (prefix))
1986 wv->name++;
1987 wv->value = 0;
1988 wv->enabled = 1;
3427a3db 1989 wv->button_type = BUTTON_TYPE_NONE;
78c8278d
RS
1990 save_wv = wv;
1991 prev_wv = 0;
78589e07 1992 }
78c8278d
RS
1993 else if (first_pane)
1994 {
1995 save_wv = wv;
1996 prev_wv = 0;
1997 }
1998 first_pane = 0;
78589e07
RS
1999 i += MENU_ITEMS_PANE_LENGTH;
2000 }
2001 else
2002 {
2003 /* Create a new item within current pane. */
3427a3db 2004 Lisp_Object item_name, enable, descrip, def, type, selected;
78589e07
RS
2005 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2006 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2007 descrip
2008 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
a352a815 2009 def = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_DEFINITION];
3427a3db
GM
2010 type = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_TYPE];
2011 selected = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_SELECTED];
2012
703dc2a8 2013#ifndef HAVE_MULTILINGUAL_MENU
3427a3db
GM
2014 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
2015 item_name = string_make_unibyte (item_name);
2016 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
2017 item_name = string_make_unibyte (descrip);
703dc2a8 2018#endif
3427a3db 2019
f7fab165 2020 wv = xmalloc_widget_value ();
78589e07
RS
2021 if (prev_wv)
2022 prev_wv->next = wv;
2023 else
2024 save_wv->contents = wv;
4e8d3549 2025 wv->name = (char *) XSTRING (item_name)->data;
78589e07 2026 if (!NILP (descrip))
4e8d3549 2027 wv->key = (char *) XSTRING (descrip)->data;
78589e07 2028 wv->value = 0;
a352a815
RS
2029 /* If this item has a null value,
2030 make the call_data null so that it won't display a box
2031 when the mouse is on it. */
2032 wv->call_data
2033 = (!NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0);
78589e07 2034 wv->enabled = !NILP (enable);
3427a3db
GM
2035
2036 if (NILP (type))
2037 wv->button_type = BUTTON_TYPE_NONE;
2038 else if (EQ (type, QCtoggle))
2039 wv->button_type = BUTTON_TYPE_TOGGLE;
2040 else if (EQ (type, QCradio))
2041 wv->button_type = BUTTON_TYPE_RADIO;
2042 else
2043 abort ();
2044
2045 wv->selected = !NILP (selected);
2046
78589e07
RS
2047 prev_wv = wv;
2048
2049 i += MENU_ITEMS_ITEM_LENGTH;
2050 }
2051 }
2052
c98fcf4b 2053 /* Deal with the title, if it is non-nil. */
4dedbfe0
PR
2054 if (!NILP (title))
2055 {
f7fab165
RS
2056 widget_value *wv_title = xmalloc_widget_value ();
2057 widget_value *wv_sep1 = xmalloc_widget_value ();
2058 widget_value *wv_sep2 = xmalloc_widget_value ();
4dedbfe0
PR
2059
2060 wv_sep2->name = "--";
2061 wv_sep2->next = first_wv->contents;
2062
2063 wv_sep1->name = "--";
2064 wv_sep1->next = wv_sep2;
2065
703dc2a8
KH
2066#ifndef HAVE_MULTILINGUAL_MENU
2067 if (STRING_MULTIBYTE (title))
2068 title = string_make_unibyte (title);
2069#endif
4dedbfe0
PR
2070 wv_title->name = (char *) XSTRING (title)->data;
2071 wv_title->enabled = True;
3427a3db 2072 wv_title->button_type = BUTTON_TYPE_NONE;
4dedbfe0
PR
2073 wv_title->next = wv_sep1;
2074 first_wv->contents = wv_title;
2075 }
2076
78589e07 2077 /* Actually create the menu. */
cc17e9bf 2078 menu_id = widget_id_tick++;
78589e07 2079 menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
7556890b 2080 f->output_data.x->widget, 1, 0,
4dedbfe0
PR
2081 popup_selection_callback,
2082 popup_deactivate_callback);
60f312e2 2083
ffcb5a51
RS
2084 /* Adjust coordinates to relative to the outer (window manager) window. */
2085 {
2086 Window child;
2087 int win_x = 0, win_y = 0;
2088
2089 /* Find the position of the outside upper-left corner of
2090 the inner window, with respect to the outer window. */
2091 if (f->output_data.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
2092 {
2093 BLOCK_INPUT;
2094 XTranslateCoordinates (FRAME_X_DISPLAY (f),
2095
2096 /* From-window, to-window. */
2097 f->output_data.x->window_desc,
2098 f->output_data.x->parent_desc,
2099
2100 /* From-position, to-position. */
2101 0, 0, &win_x, &win_y,
2102
2103 /* Child of window. */
2104 &child);
2105 UNBLOCK_INPUT;
2106 x += win_x;
2107 y += win_y;
2108 }
2109 }
2110
2111 /* Adjust coordinates to be root-window-relative. */
2112 x += f->output_data.x->left_pos;
2113 y += f->output_data.x->top_pos;
2114
2115 dummy.type = ButtonPress;
2116 dummy.serial = 0;
2117 dummy.send_event = 0;
2118 dummy.display = FRAME_X_DISPLAY (f);
2119 dummy.time = CurrentTime;
ffcb5a51
RS
2120 dummy.root = FRAME_X_DISPLAY_INFO (f)->root_window;
2121 dummy.window = dummy.root;
2122 dummy.subwindow = dummy.root;
2123 dummy.x_root = x;
2124 dummy.y_root = y;
2125 dummy.x = x;
2126 dummy.y = y;
c8b5aa3d
RS
2127 dummy.state = (FRAME_X_DISPLAY_INFO (f)->grabbed >> 1) * Button1Mask;
2128 dummy.button = 0;
2129 for (i = 0; i < 5; i++)
2130 if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
2131 dummy.button = i;
ffcb5a51 2132
60f312e2
RS
2133 /* Don't allow any geometry request from the user. */
2134 XtSetArg (av[ac], XtNgeometry, 0); ac++;
2135 XtSetValues (menu, av, ac);
2136
78589e07
RS
2137 /* Free the widget_value objects we used to specify the contents. */
2138 free_menubar_widget_value_tree (first_wv);
2139
2140 /* No selection has been chosen yet. */
2141 menu_item_selection = 0;
2142
78589e07 2143 /* Display the menu. */
ffcb5a51 2144 lw_popup_menu (menu, &dummy);
4dedbfe0 2145 popup_activated_flag = 1;
18686d47 2146
78589e07 2147 /* Process events that apply to the menu. */
2e2b8e22 2148 popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id);
78589e07 2149
3427a3db
GM
2150#ifdef LESSTIF_VERSION
2151 /* Nov 1998: For an unknown reason a button grab remains active
2152 after the popup menu has gone. */
2153 XUngrabButton (XtDisplay (f->output_data.x->widget),
2154 AnyButton, AnyModifier,
2155 XtWindow (f->output_data.x->widget));
2156 XUngrabButton (XtDisplay (f->output_data.x->edit_widget),
2157 AnyButton, AnyModifier,
2158 XtWindow (f->output_data.x->edit_widget));
2159#endif /* LESSTIF_VERSION */
2160
a9c90b7c
RS
2161 /* fp turned off the following statement and wrote a comment
2162 that it is unnecessary--that the menu has already disappeared.
21af8a68
KH
2163 Nowadays the menu disappears ok, all right, but
2164 we need to delete the widgets or multiple ones will pile up. */
78589e07 2165 lw_destroy_all_widgets (menu_id);
18686d47 2166
78589e07
RS
2167 /* Find the selected item, and its pane, to return
2168 the proper value. */
2169 if (menu_item_selection != 0)
2170 {
c63f6952 2171 Lisp_Object prefix, entry;
78589e07
RS
2172
2173 prefix = Qnil;
2174 i = 0;
2175 while (i < menu_items_used)
2176 {
101bb4a5
RS
2177 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
2178 {
2179 subprefix_stack[submenu_depth++] = prefix;
2180 prefix = entry;
2181 i++;
2182 }
2183 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
2184 {
2185 prefix = subprefix_stack[--submenu_depth];
2186 i++;
2187 }
2188 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
78589e07
RS
2189 {
2190 prefix
2191 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2192 i += MENU_ITEMS_PANE_LENGTH;
2193 }
d31d42cc
RS
2194 /* Ignore a nil in the item list.
2195 It's meaningful only for dialog boxes. */
2196 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2197 i += 1;
78589e07
RS
2198 else
2199 {
2200 entry
2201 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2202 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
2203 {
2204 if (keymaps != 0)
2205 {
101bb4a5
RS
2206 int j;
2207
78589e07
RS
2208 entry = Fcons (entry, Qnil);
2209 if (!NILP (prefix))
2210 entry = Fcons (prefix, entry);
101bb4a5 2211 for (j = submenu_depth - 1; j >= 0; j--)
e48087b7 2212 if (!NILP (subprefix_stack[j]))
5964e450 2213 entry = Fcons (subprefix_stack[j], entry);
78589e07
RS
2214 }
2215 return entry;
2216 }
2217 i += MENU_ITEMS_ITEM_LENGTH;
2218 }
2219 }
2220 }
2221
2222 return Qnil;
18686d47 2223}
4dedbfe0
PR
2224\f
2225static void
2226dialog_selection_callback (widget, id, client_data)
2227 Widget widget;
2228 LWLIB_ID id;
2229 XtPointer client_data;
2230{
01d5e892
RS
2231 /* The EMACS_INT cast avoids a warning. There's no problem
2232 as long as pointers have enough bits to hold small integers. */
2233 if ((int) (EMACS_INT) client_data != -1)
4dedbfe0
PR
2234 menu_item_selection = (Lisp_Object *) client_data;
2235 BLOCK_INPUT;
2236 lw_destroy_all_widgets (id);
2237 UNBLOCK_INPUT;
9572375b 2238 popup_activated_flag = 0;
4dedbfe0 2239}
18686d47 2240
165e1749
FP
2241static char * button_names [] = {
2242 "button1", "button2", "button3", "button4", "button5",
2243 "button6", "button7", "button8", "button9", "button10" };
2244
2245static Lisp_Object
673a6211 2246xdialog_show (f, keymaps, title, error)
165e1749 2247 FRAME_PTR f;
165e1749
FP
2248 int keymaps;
2249 Lisp_Object title;
2250 char **error;
2251{
2252 int i, nb_buttons=0;
cc17e9bf 2253 LWLIB_ID dialog_id;
165e1749 2254 Widget menu;
80670155 2255 char dialog_name[6];
165e1749 2256
165e1749
FP
2257 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
2258
fcaa7665
RS
2259 /* Number of elements seen so far, before boundary. */
2260 int left_count = 0;
2261 /* 1 means we've seen the boundary between left-hand elts and right-hand. */
2262 int boundary_seen = 0;
2263
165e1749
FP
2264 *error = NULL;
2265
80670155
RS
2266 if (menu_items_n_panes > 1)
2267 {
2268 *error = "Multiple panes in dialog box";
2269 return Qnil;
2270 }
2271
165e1749
FP
2272 /* Create a tree of widget_value objects
2273 representing the text label and buttons. */
2274 {
2275 Lisp_Object pane_name, prefix;
2276 char *pane_string;
2277 pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
2278 prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
2279 pane_string = (NILP (pane_name)
2280 ? "" : (char *) XSTRING (pane_name)->data);
f7fab165 2281 prev_wv = xmalloc_widget_value ();
165e1749
FP
2282 prev_wv->value = pane_string;
2283 if (keymaps && !NILP (prefix))
2284 prev_wv->name++;
2285 prev_wv->enabled = 1;
2286 prev_wv->name = "message";
2287 first_wv = prev_wv;
2288
2289 /* Loop over all panes and items, filling in the tree. */
2290 i = MENU_ITEMS_PANE_LENGTH;
2291 while (i < menu_items_used)
2292 {
2293
2294 /* Create a new item within current pane. */
2295 Lisp_Object item_name, enable, descrip;
2296 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2297 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2298 descrip
2299 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
2300
80670155
RS
2301 if (NILP (item_name))
2302 {
2303 free_menubar_widget_value_tree (first_wv);
2304 *error = "Submenu in dialog items";
2305 return Qnil;
2306 }
fcaa7665
RS
2307 if (EQ (item_name, Qquote))
2308 {
2309 /* This is the boundary between left-side elts
2310 and right-side elts. Stop incrementing right_count. */
2311 boundary_seen = 1;
2312 i++;
2313 continue;
2314 }
86e71abf 2315 if (nb_buttons >= 9)
80670155
RS
2316 {
2317 free_menubar_widget_value_tree (first_wv);
2318 *error = "Too many dialog items";
2319 return Qnil;
2320 }
2321
f7fab165 2322 wv = xmalloc_widget_value ();
165e1749 2323 prev_wv->next = wv;
80670155 2324 wv->name = (char *) button_names[nb_buttons];
165e1749 2325 if (!NILP (descrip))
4e8d3549
RS
2326 wv->key = (char *) XSTRING (descrip)->data;
2327 wv->value = (char *) XSTRING (item_name)->data;
165e1749
FP
2328 wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
2329 wv->enabled = !NILP (enable);
2330 prev_wv = wv;
2331
fcaa7665
RS
2332 if (! boundary_seen)
2333 left_count++;
2334
165e1749
FP
2335 nb_buttons++;
2336 i += MENU_ITEMS_ITEM_LENGTH;
2337 }
2338
fcaa7665
RS
2339 /* If the boundary was not specified,
2340 by default put half on the left and half on the right. */
2341 if (! boundary_seen)
2342 left_count = nb_buttons - nb_buttons / 2;
2343
f7fab165 2344 wv = xmalloc_widget_value ();
80670155
RS
2345 wv->name = dialog_name;
2346
2347 /* Dialog boxes use a really stupid name encoding
2348 which specifies how many buttons to use
2349 and how many buttons are on the right.
2350 The Q means something also. */
2351 dialog_name[0] = 'Q';
2352 dialog_name[1] = '0' + nb_buttons;
2353 dialog_name[2] = 'B';
2354 dialog_name[3] = 'R';
fcaa7665
RS
2355 /* Number of buttons to put on the right. */
2356 dialog_name[4] = '0' + nb_buttons - left_count;
80670155 2357 dialog_name[5] = 0;
165e1749
FP
2358 wv->contents = first_wv;
2359 first_wv = wv;
165e1749
FP
2360 }
2361
2362 /* Actually create the dialog. */
cc17e9bf 2363 dialog_id = widget_id_tick++;
165e1749 2364 menu = lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
7556890b 2365 f->output_data.x->widget, 1, 0,
165e1749 2366 dialog_selection_callback, 0);
b5587215 2367 lw_modify_all_widgets (dialog_id, first_wv->contents, True);
165e1749
FP
2368 /* Free the widget_value objects we used to specify the contents. */
2369 free_menubar_widget_value_tree (first_wv);
2370
2371 /* No selection has been chosen yet. */
2372 menu_item_selection = 0;
2373
165e1749
FP
2374 /* Display the menu. */
2375 lw_pop_up_all_widgets (dialog_id);
aa669def 2376 popup_activated_flag = 1;
165e1749
FP
2377
2378 /* Process events that apply to the menu. */
2e2b8e22 2379 popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), dialog_id);
165e1749 2380
21af8a68
KH
2381 lw_destroy_all_widgets (dialog_id);
2382
165e1749
FP
2383 /* Find the selected item, and its pane, to return
2384 the proper value. */
2385 if (menu_item_selection != 0)
2386 {
2387 Lisp_Object prefix;
2388
2389 prefix = Qnil;
2390 i = 0;
2391 while (i < menu_items_used)
2392 {
2393 Lisp_Object entry;
2394
2395 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2396 {
2397 prefix
2398 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2399 i += MENU_ITEMS_PANE_LENGTH;
2400 }
2401 else
2402 {
2403 entry
2404 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2405 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
2406 {
2407 if (keymaps != 0)
2408 {
2409 entry = Fcons (entry, Qnil);
2410 if (!NILP (prefix))
2411 entry = Fcons (prefix, entry);
2412 }
2413 return entry;
2414 }
2415 i += MENU_ITEMS_ITEM_LENGTH;
2416 }
2417 }
2418 }
2419
2420 return Qnil;
2421}
18686d47 2422#else /* not USE_X_TOOLKIT */
78589e07
RS
2423
2424static Lisp_Object
673a6211 2425xmenu_show (f, x, y, for_click, keymaps, title, error)
78589e07
RS
2426 FRAME_PTR f;
2427 int x, y;
9685a93f
RS
2428 int for_click;
2429 int keymaps;
78589e07
RS
2430 Lisp_Object title;
2431 char **error;
dcfdbac7 2432{
78589e07
RS
2433 Window root;
2434 XMenu *menu;
2435 int pane, selidx, lpane, status;
2436 Lisp_Object entry, pane_prefix;
dcfdbac7
JB
2437 char *datap;
2438 int ulx, uly, width, height;
2439 int dispwidth, dispheight;
4e8d3549
RS
2440 int i, j;
2441 int maxwidth;
78589e07
RS
2442 int dummy_int;
2443 unsigned int dummy_uint;
088831f6 2444
07a675b7 2445 *error = 0;
78589e07
RS
2446 if (menu_items_n_panes == 0)
2447 return Qnil;
088831f6 2448
742f715d
KH
2449 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
2450 {
2451 *error = "Empty menu";
2452 return Qnil;
2453 }
2454
78589e07 2455 /* Figure out which root window F is on. */
92280f67 2456 XGetGeometry (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &root,
78589e07
RS
2457 &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
2458 &dummy_uint, &dummy_uint);
18686d47 2459
78589e07 2460 /* Make the menu on that window. */
92280f67 2461 menu = XMenuCreate (FRAME_X_DISPLAY (f), root, "emacs");
78589e07 2462 if (menu == NULL)
dcfdbac7
JB
2463 {
2464 *error = "Can't create menu";
78589e07 2465 return Qnil;
dcfdbac7 2466 }
78589e07 2467
87485d6f 2468#ifdef HAVE_X_WINDOWS
78589e07 2469 /* Adjust coordinates to relative to the outer (window manager) window. */
78589e07
RS
2470 {
2471 Window child;
2472 int win_x = 0, win_y = 0;
2473
2474 /* Find the position of the outside upper-left corner of
2475 the inner window, with respect to the outer window. */
7556890b 2476 if (f->output_data.x->parent_desc != FRAME_X_DISPLAY_INFO (f)->root_window)
78589e07
RS
2477 {
2478 BLOCK_INPUT;
92280f67 2479 XTranslateCoordinates (FRAME_X_DISPLAY (f),
78589e07
RS
2480
2481 /* From-window, to-window. */
7556890b
RS
2482 f->output_data.x->window_desc,
2483 f->output_data.x->parent_desc,
78589e07
RS
2484
2485 /* From-position, to-position. */
2486 0, 0, &win_x, &win_y,
2487
2488 /* Child of window. */
2489 &child);
2490 UNBLOCK_INPUT;
2491 x += win_x;
2492 y += win_y;
2493 }
2494 }
87485d6f 2495#endif /* HAVE_X_WINDOWS */
78589e07
RS
2496
2497 /* Adjust coordinates to be root-window-relative. */
7556890b
RS
2498 x += f->output_data.x->left_pos;
2499 y += f->output_data.x->top_pos;
18686d47 2500
78589e07
RS
2501 /* Create all the necessary panes and their items. */
2502 i = 0;
2503 while (i < menu_items_used)
dcfdbac7 2504 {
78589e07 2505 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
dcfdbac7 2506 {
78589e07
RS
2507 /* Create a new pane. */
2508 Lisp_Object pane_name, prefix;
2509 char *pane_string;
2510
2511 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
2512 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2513 pane_string = (NILP (pane_name)
2514 ? "" : (char *) XSTRING (pane_name)->data);
2515 if (keymaps && !NILP (prefix))
2516 pane_string++;
2517
92280f67 2518 lpane = XMenuAddPane (FRAME_X_DISPLAY (f), menu, pane_string, TRUE);
78589e07
RS
2519 if (lpane == XM_FAILURE)
2520 {
92280f67 2521 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
78589e07
RS
2522 *error = "Can't create pane";
2523 return Qnil;
2524 }
2525 i += MENU_ITEMS_PANE_LENGTH;
4e8d3549
RS
2526
2527 /* Find the width of the widest item in this pane. */
2528 maxwidth = 0;
2529 j = i;
2530 while (j < menu_items_used)
2531 {
2532 Lisp_Object item;
2533 item = XVECTOR (menu_items)->contents[j];
2534 if (EQ (item, Qt))
2535 break;
2536 if (NILP (item))
2537 {
2538 j++;
2539 continue;
2540 }
fc932ac6 2541 width = STRING_BYTES (XSTRING (item));
4e8d3549
RS
2542 if (width > maxwidth)
2543 maxwidth = width;
2544
2545 j += MENU_ITEMS_ITEM_LENGTH;
2546 }
dcfdbac7 2547 }
fcaa7665
RS
2548 /* Ignore a nil in the item list.
2549 It's meaningful only for dialog boxes. */
2550 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2551 i += 1;
78589e07 2552 else
dcfdbac7 2553 {
78589e07
RS
2554 /* Create a new item within current pane. */
2555 Lisp_Object item_name, enable, descrip;
4e8d3549 2556 unsigned char *item_data;
78589e07
RS
2557
2558 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2559 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2560 descrip
2561 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
2562 if (!NILP (descrip))
4e8d3549 2563 {
fc932ac6 2564 int gap = maxwidth - STRING_BYTES (XSTRING (item_name));
4e8d3549
RS
2565#ifdef C_ALLOCA
2566 Lisp_Object spacer;
2567 spacer = Fmake_string (make_number (gap), make_number (' '));
2568 item_name = concat2 (item_name, spacer);
2569 item_name = concat2 (item_name, descrip);
2570 item_data = XSTRING (item_name)->data;
2571#else
2572 /* if alloca is fast, use that to make the space,
2573 to reduce gc needs. */
2574 item_data
2575 = (unsigned char *) alloca (maxwidth
fc932ac6 2576 + STRING_BYTES (XSTRING (descrip)) + 1);
4e8d3549 2577 bcopy (XSTRING (item_name)->data, item_data,
fc932ac6 2578 STRING_BYTES (XSTRING (item_name)));
4e8d3549
RS
2579 for (j = XSTRING (item_name)->size; j < maxwidth; j++)
2580 item_data[j] = ' ';
2581 bcopy (XSTRING (descrip)->data, item_data + j,
fc932ac6
RS
2582 STRING_BYTES (XSTRING (descrip)));
2583 item_data[j + STRING_BYTES (XSTRING (descrip))] = 0;
4e8d3549
RS
2584#endif
2585 }
2586 else
2587 item_data = XSTRING (item_name)->data;
78589e07 2588
92280f67
RS
2589 if (XMenuAddSelection (FRAME_X_DISPLAY (f),
2590 menu, lpane, 0, item_data,
78589e07 2591 !NILP (enable))
dcfdbac7
JB
2592 == XM_FAILURE)
2593 {
92280f67 2594 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
dcfdbac7 2595 *error = "Can't add selection to menu";
78589e07 2596 return Qnil;
dcfdbac7 2597 }
78589e07 2598 i += MENU_ITEMS_ITEM_LENGTH;
dcfdbac7
JB
2599 }
2600 }
4e8d3549 2601
78589e07 2602 /* All set and ready to fly. */
92280f67
RS
2603 XMenuRecompute (FRAME_X_DISPLAY (f), menu);
2604 dispwidth = DisplayWidth (FRAME_X_DISPLAY (f),
f1847de3 2605 XScreenNumberOfScreen (FRAME_X_SCREEN (f)));
92280f67 2606 dispheight = DisplayHeight (FRAME_X_DISPLAY (f),
f1847de3 2607 XScreenNumberOfScreen (FRAME_X_SCREEN (f)));
78589e07
RS
2608 x = min (x, dispwidth);
2609 y = min (y, dispheight);
2610 x = max (x, 1);
2611 y = max (y, 1);
92280f67 2612 XMenuLocate (FRAME_X_DISPLAY (f), menu, 0, 0, x, y,
dcfdbac7
JB
2613 &ulx, &uly, &width, &height);
2614 if (ulx+width > dispwidth)
2615 {
78589e07 2616 x -= (ulx + width) - dispwidth;
dcfdbac7
JB
2617 ulx = dispwidth - width;
2618 }
2619 if (uly+height > dispheight)
2620 {
78589e07 2621 y -= (uly + height) - dispheight;
dcfdbac7
JB
2622 uly = dispheight - height;
2623 }
78589e07
RS
2624 if (ulx < 0) x -= ulx;
2625 if (uly < 0) y -= uly;
121e4555
KH
2626
2627 XMenuSetAEQ (menu, TRUE);
78589e07
RS
2628 XMenuSetFreeze (menu, TRUE);
2629 pane = selidx = 0;
dcfdbac7 2630
92280f67 2631 status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
78589e07 2632 x, y, ButtonReleaseMask, &datap);
a352a815
RS
2633
2634
f1df80a8 2635#ifdef HAVE_X_WINDOWS
a352a815
RS
2636 /* Assume the mouse has moved out of the X window.
2637 If it has actually moved in, we will get an EnterNotify. */
29e460bd 2638 x_mouse_leave (FRAME_X_DISPLAY_INFO (f));
f1df80a8 2639#endif
a352a815 2640
dcfdbac7
JB
2641 switch (status)
2642 {
2643 case XM_SUCCESS:
2644#ifdef XDEBUG
2645 fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
2646#endif
fa6d54d9 2647
78589e07
RS
2648 /* Find the item number SELIDX in pane number PANE. */
2649 i = 0;
2650 while (i < menu_items_used)
fa6d54d9 2651 {
78589e07 2652 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
088831f6 2653 {
78589e07
RS
2654 if (pane == 0)
2655 pane_prefix
2656 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2657 pane--;
2658 i += MENU_ITEMS_PANE_LENGTH;
088831f6 2659 }
78589e07 2660 else
ab6ee1a0 2661 {
78589e07 2662 if (pane == -1)
ab6ee1a0 2663 {
78589e07 2664 if (selidx == 0)
ab6ee1a0 2665 {
78589e07
RS
2666 entry
2667 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2668 if (keymaps != 0)
ab6ee1a0 2669 {
78589e07
RS
2670 entry = Fcons (entry, Qnil);
2671 if (!NILP (pane_prefix))
2672 entry = Fcons (pane_prefix, entry);
ab6ee1a0 2673 }
78589e07 2674 break;
ab6ee1a0 2675 }
78589e07 2676 selidx--;
ab6ee1a0 2677 }
78589e07 2678 i += MENU_ITEMS_ITEM_LENGTH;
ab6ee1a0
RS
2679 }
2680 }
78589e07 2681 break;
dcfdbac7 2682
78589e07 2683 case XM_FAILURE:
78589e07
RS
2684 *error = "Can't activate menu";
2685 case XM_IA_SELECT:
2686 case XM_NO_SELECT:
2687 entry = Qnil;
2688 break;
dcfdbac7 2689 }
92280f67 2690 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
a5285df3 2691
87485d6f 2692#ifdef HAVE_X_WINDOWS
a5285df3
RS
2693 /* State that no mouse buttons are now held.
2694 (The oldXMenu code doesn't track this info for us.)
2695 That is not necessarily true, but the fiction leads to reasonable
2696 results, and it is a pain to ask which are actually held now. */
e9a79fb2 2697 FRAME_X_DISPLAY_INFO (f)->grabbed = 0;
87485d6f 2698#endif
a5285df3 2699
78589e07 2700 return entry;
dcfdbac7 2701}
4dedbfe0 2702
78589e07 2703#endif /* not USE_X_TOOLKIT */
1e659e4c
RS
2704
2705#endif /* HAVE_MENUS */
088831f6 2706\f
dfcf069d 2707void
78589e07 2708syms_of_xmenu ()
dcfdbac7 2709{
78589e07
RS
2710 staticpro (&menu_items);
2711 menu_items = Qnil;
dcfdbac7 2712
0314aacb
RS
2713 Qdebug_on_next_call = intern ("debug-on-next-call");
2714 staticpro (&Qdebug_on_next_call);
2715
bfc524bc
RS
2716 DEFVAR_LISP ("menu-updating-frame", &Vmenu_updating_frame,
2717 "Frame for which we are updating a menu.\n\
2718The enable predicate for a menu command should check this variable.");
2719 Vmenu_updating_frame = Qnil;
2720
8ed87156 2721#ifdef USE_X_TOOLKIT
4dedbfe0 2722 widget_id_tick = (1<<16);
88766961 2723 next_menubar_widget_id = 1;
8ed87156
RS
2724#endif
2725
78589e07 2726 defsubr (&Sx_popup_menu);
1e659e4c 2727#ifdef HAVE_MENUS
165e1749 2728 defsubr (&Sx_popup_dialog);
1e659e4c 2729#endif
dcfdbac7 2730}