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