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