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