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