(struct xftface_info): Delete the member xft_draw.
[bpt/emacs.git] / src / xmenu.c
CommitLineData
dcfdbac7 1/* X Communication module for terminals which understand the X protocol.
0b5538bd 2 Copyright (C) 1986, 1988, 1993, 1994, 1996, 1999, 2000, 2001, 2002, 2003,
4e6835db 3 2004, 2005, 2006, 2007 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
684d6f5b 9the Free Software Foundation; either version 3, 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
4fc5845f
LK
19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20Boston, MA 02110-1301, USA. */
dcfdbac7 21
c9e60620 22/* X pop-up deck-of-cards menu facility for GNU Emacs.
dcfdbac7
JB
23 *
24 * Written by Jon Arnold and Roman Budzianowski
25 * Mods and rewrite by Robert Krawitz
26 *
27 */
28
18686d47
RS
29/* Modified by Fred Pierresteguy on December 93
30 to make the popup menus and menubar use the Xt. */
31
78589e07
RS
32/* Rewritten for clarity and GC protection by rms in Feb 94. */
33
68c45bf0
PE
34#include <config.h>
35
8faf6057 36#if 0 /* Why was this included? And without syssignal.h? */
dcfdbac7
JB
37/* On 4.3 this loses if it comes after xterm.h. */
38#include <signal.h>
8faf6057 39#endif
565620a5
RS
40
41#include <stdio.h>
7ee72033 42
dcfdbac7 43#include "lisp.h"
02067692 44#include "keyboard.h"
5ee707b8 45#include "keymap.h"
7708e9bd 46#include "frame.h"
428a555e 47#include "termhooks.h"
dcfdbac7 48#include "window.h"
9ac0d9e0 49#include "blockinput.h"
88766961 50#include "buffer.h"
cc45fb40
SM
51#include "charset.h"
52#include "coding.h"
c3438661 53#include "sysselect.h"
dcfdbac7 54
eeee3112
RS
55#ifdef MSDOS
56#include "msdos.h"
57#endif
58
87485d6f 59#ifdef HAVE_X_WINDOWS
dcfdbac7
JB
60/* This may include sys/types.h, and that somehow loses
61 if this is not done before the other system files. */
62#include "xterm.h"
87485d6f 63#endif
dcfdbac7
JB
64
65/* Load sys/types.h if not already loaded.
66 In some systems loading it twice is suicidal. */
67#ifndef makedev
68#include <sys/types.h>
69#endif
70
71#include "dispextern.h"
72
87485d6f 73#ifdef HAVE_X_WINDOWS
646f98ec
DL
74/* Defining HAVE_MULTILINGUAL_MENU would mean that the toolkit menu
75 code accepts the Emacs internal encoding. */
703dc2a8 76#undef HAVE_MULTILINGUAL_MENU
18686d47 77#ifdef USE_X_TOOLKIT
991786dd 78#include "widget.h"
18686d47
RS
79#include <X11/Xlib.h>
80#include <X11/IntrinsicP.h>
81#include <X11/CoreP.h>
82#include <X11/StringDefs.h>
60f312e2 83#include <X11/Shell.h>
5ee80bd3 84#ifdef USE_LUCID
5262c5c7
CY
85#ifdef HAVE_XAW3D
86#include <X11/Xaw3d/Paned.h>
87#else /* !HAVE_XAW3D */
1d1c1567 88#include <X11/Xaw/Paned.h>
5262c5c7 89#endif /* HAVE_XAW3D */
5ee80bd3 90#endif /* USE_LUCID */
18686d47 91#include "../lwlib/lwlib.h"
a352a815 92#else /* not USE_X_TOOLKIT */
488dd4c4 93#ifndef USE_GTK
a352a815 94#include "../oldXMenu/XMenu.h"
488dd4c4 95#endif
a352a815
RS
96#endif /* not USE_X_TOOLKIT */
97#endif /* HAVE_X_WINDOWS */
18686d47 98
dcfdbac7
JB
99#ifndef TRUE
100#define TRUE 1
101#define FALSE 0
78589e07 102#endif /* no TRUE */
dcfdbac7 103
0314aacb
RS
104Lisp_Object Qdebug_on_next_call;
105
4248ca0c
RS
106extern Lisp_Object Vmenu_updating_frame;
107
18686d47 108extern Lisp_Object Qmenu_bar;
78589e07 109
de57a39c 110extern Lisp_Object QCtoggle, QCradio;
8fbc986d 111
88766961
RS
112extern Lisp_Object Voverriding_local_map;
113extern Lisp_Object Voverriding_local_map_menu_flag;
114
115extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
116
117extern Lisp_Object Qmenu_bar_update_hook;
118
18686d47 119#ifdef USE_X_TOOLKIT
495ef86b 120extern void set_frame_menubar P_ ((FRAME_PTR, int, int));
78589e07
RS
121extern XtAppContext Xt_app_con;
122
6a040d6a
NR
123static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, Lisp_Object,
124 char **));
495ef86b 125static void popup_get_selection P_ ((XEvent *, struct x_display_info *,
95bdef2e 126 LWLIB_ID, int));
3427a3db 127
c9e60620 128/* Define HAVE_BOXES if menus can handle radio and toggle buttons. */
3427a3db
GM
129
130#define HAVE_BOXES 1
488dd4c4
JD
131#endif /* USE_X_TOOLKIT */
132
133#ifdef USE_GTK
134#include "gtkutil.h"
135#define HAVE_BOXES 1
495ef86b 136extern void set_frame_menubar P_ ((FRAME_PTR, int, int));
6a040d6a
NR
137static Lisp_Object xdialog_show P_ ((FRAME_PTR, int, Lisp_Object, Lisp_Object,
138 char **));
3427a3db
GM
139#endif
140
646f98ec
DL
141/* This is how to deal with multibyte text if HAVE_MULTILINGUAL_MENU
142 isn't defined. The use of HAVE_MULTILINGUAL_MENU could probably be
143 confined to an extended version of this with sections of code below
144 using it unconditionally. */
145#ifdef USE_GTK
146/* gtk just uses utf-8. */
147# define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
b64b78cc
SM
148#elif defined HAVE_X_I18N
149# define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str)
646f98ec 150#else
8af9fa55 151# define ENCODE_MENU_STRING(str) string_make_unibyte (str)
646f98ec
DL
152#endif
153
3427a3db
GM
154static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
155 Lisp_Object, Lisp_Object, Lisp_Object,
850df50b 156 Lisp_Object, Lisp_Object));
f61a541b
GM
157static int update_frame_menubar P_ ((struct frame *));
158static Lisp_Object xmenu_show P_ ((struct frame *, int, int, int, int,
159 Lisp_Object, char **));
160static void keymap_panes P_ ((Lisp_Object *, int, int));
161static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
162 int, int));
f61a541b
GM
163static void list_of_panes P_ ((Lisp_Object));
164static void list_of_items P_ ((Lisp_Object));
f02cac82 165
78589e07
RS
166\f
167/* This holds a Lisp vector that holds the results of decoding
168 the keymaps or alist-of-alists that specify a menu.
dcfdbac7 169
78589e07 170 It describes the panes and items within the panes.
dcfdbac7 171
78589e07
RS
172 Each pane is described by 3 elements in the vector:
173 t, the pane name, the pane's prefix key.
a352a815 174 Then follow the pane's items, with 5 elements per item:
78589e07 175 the item string, the enable flag, the item's value,
a352a815 176 the definition, and the equivalent keyboard key's description string.
dcfdbac7 177
101bb4a5
RS
178 In some cases, multiple levels of menus may be described.
179 A single vector slot containing nil indicates the start of a submenu.
180 A single vector slot containing lambda indicates the end of a submenu.
181 The submenu follows a menu item which is the way to reach the submenu.
182
fcaa7665
RS
183 A single vector slot containing quote indicates that the
184 following items should appear on the right of a dialog box.
185
78589e07
RS
186 Using a Lisp vector to hold this information while we decode it
187 takes care of protecting all the data from GC. */
dcfdbac7 188
78589e07
RS
189#define MENU_ITEMS_PANE_NAME 1
190#define MENU_ITEMS_PANE_PREFIX 2
191#define MENU_ITEMS_PANE_LENGTH 3
088831f6 192
850df50b
GM
193enum menu_item_idx
194{
195 MENU_ITEMS_ITEM_NAME = 0,
196 MENU_ITEMS_ITEM_ENABLE,
197 MENU_ITEMS_ITEM_VALUE,
198 MENU_ITEMS_ITEM_EQUIV_KEY,
199 MENU_ITEMS_ITEM_DEFINITION,
200 MENU_ITEMS_ITEM_TYPE,
201 MENU_ITEMS_ITEM_SELECTED,
202 MENU_ITEMS_ITEM_HELP,
203 MENU_ITEMS_ITEM_LENGTH
204};
7da99777 205
78589e07 206static Lisp_Object menu_items;
18686d47 207
86fad4ec
SM
208/* If non-nil, means that the global vars defined here are already in use.
209 Used to detect cases where we try to re-enter this non-reentrant code. */
210static Lisp_Object menu_items_inuse;
211
78589e07
RS
212/* Number of slots currently allocated in menu_items. */
213static int menu_items_allocated;
18686d47 214
78589e07
RS
215/* This is the index in menu_items of the first empty slot. */
216static int menu_items_used;
18686d47 217
101bb4a5
RS
218/* The number of panes currently recorded in menu_items,
219 excluding those within submenus. */
78589e07 220static int menu_items_n_panes;
18686d47 221
101bb4a5
RS
222/* Current depth within submenus. */
223static int menu_items_submenu_depth;
224
4dedbfe0 225/* Flag which when set indicates a dialog or menu has been posted by
c98fcf4b 226 Xt on behalf of one of the widget sets. */
488dd4c4 227static int popup_activated_flag;
4dedbfe0 228
88766961 229static int next_menubar_widget_id;
745c34fb 230
a9be6839
RS
231/* This is set nonzero after the user activates the menu bar, and set
232 to zero again after the menu bars are redisplayed by prepare_menu_bar.
233 While it is nonzero, all calls to set_frame_menubar go deep.
234
235 I don't understand why this is needed, but it does seem to be
236 needed on Motif, according to Marcus Daniels <marcus@sysc.pdx.edu>. */
237
238int pending_menu_activation;
bd3a4da2 239\f
88766961 240#ifdef USE_X_TOOLKIT
bd3a4da2 241
7556890b 242/* Return the frame whose ->output_data.x->id equals ID, or 0 if none. */
bd3a4da2 243
88766961
RS
244static struct frame *
245menubar_id_to_frame (id)
246 LWLIB_ID id;
247{
248 Lisp_Object tail, frame;
249 FRAME_PTR f;
bd3a4da2 250
8e50cc2d 251 for (tail = Vframe_list; CONSP (tail); tail = XCDR (tail))
bd3a4da2 252 {
8e713be6 253 frame = XCAR (tail);
8e50cc2d 254 if (!FRAMEP (frame))
88766961
RS
255 continue;
256 f = XFRAME (frame);
2d764c78 257 if (!FRAME_WINDOW_P (f))
88766961 258 continue;
7556890b 259 if (f->output_data.x->id == id)
88766961 260 return f;
bd3a4da2 261 }
88766961 262 return 0;
bd3a4da2 263}
88766961
RS
264
265#endif
4dedbfe0 266\f
78589e07
RS
267/* Initialize the menu_items structure if we haven't already done so.
268 Also mark it as currently empty. */
269
270static void
271init_menu_items ()
272{
1fc4d463
RS
273 if (!NILP (menu_items_inuse))
274 error ("Trying to use a menu from within a menu-entry");
275
78589e07
RS
276 if (NILP (menu_items))
277 {
278 menu_items_allocated = 60;
279 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
18686d47
RS
280 }
281
86fad4ec 282 menu_items_inuse = Qt;
78589e07
RS
283 menu_items_used = 0;
284 menu_items_n_panes = 0;
101bb4a5 285 menu_items_submenu_depth = 0;
78589e07 286}
18686d47 287
0b1f4572 288/* Call at the end of generating the data in menu_items. */
1658603c 289
78589e07
RS
290static void
291finish_menu_items ()
292{
293}
1658603c 294
86fad4ec
SM
295static Lisp_Object
296unuse_menu_items (dummy)
af89e871 297 Lisp_Object dummy;
86fad4ec
SM
298{
299 return menu_items_inuse = Qnil;
300}
301
78589e07
RS
302/* Call when finished using the data for the current menu
303 in menu_items. */
1658603c 304
78589e07
RS
305static void
306discard_menu_items ()
307{
308 /* Free the structure if it is especially large.
309 Otherwise, hold on to it, to save time. */
310 if (menu_items_allocated > 200)
311 {
312 menu_items = Qnil;
313 menu_items_allocated = 0;
314 }
86fad4ec 315 xassert (NILP (menu_items_inuse));
78589e07 316}
1658603c 317
1fc4d463
RS
318/* This undoes save_menu_items, and it is called by the specpdl unwind
319 mechanism. */
320
321static Lisp_Object
322restore_menu_items (saved)
323 Lisp_Object saved;
324{
325 menu_items = XCAR (saved);
326 menu_items_inuse = (! NILP (menu_items) ? Qt : Qnil);
327 menu_items_allocated = (VECTORP (menu_items) ? ASIZE (menu_items) : 0);
328 saved = XCDR (saved);
329 menu_items_used = XINT (XCAR (saved));
330 saved = XCDR (saved);
331 menu_items_n_panes = XINT (XCAR (saved));
ce33e8eb 332 saved = XCDR (saved);
1fc4d463 333 menu_items_submenu_depth = XINT (XCAR (saved));
64ec8b2f 334 return Qnil;
1fc4d463
RS
335}
336
337/* Push the whole state of menu_items processing onto the specpdl.
338 It will be restored when the specpdl is unwound. */
339
340static void
341save_menu_items ()
342{
343 Lisp_Object saved = list4 (!NILP (menu_items_inuse) ? menu_items : Qnil,
344 make_number (menu_items_used),
345 make_number (menu_items_n_panes),
346 make_number (menu_items_submenu_depth));
347 record_unwind_protect (restore_menu_items, saved);
348 menu_items_inuse = Qnil;
349 menu_items = Qnil;
350}
351\f
101bb4a5
RS
352/* Make the menu_items vector twice as large. */
353
354static void
355grow_menu_items ()
356{
101bb4a5 357 menu_items_allocated *= 2;
71d00ce4 358 menu_items = larger_vector (menu_items, menu_items_allocated, Qnil);
101bb4a5
RS
359}
360
361/* Begin a submenu. */
362
363static void
364push_submenu_start ()
365{
366 if (menu_items_used + 1 > menu_items_allocated)
367 grow_menu_items ();
368
369 XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
370 menu_items_submenu_depth++;
371}
372
373/* End a submenu. */
374
375static void
376push_submenu_end ()
377{
378 if (menu_items_used + 1 > menu_items_allocated)
379 grow_menu_items ();
380
381 XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
382 menu_items_submenu_depth--;
383}
384
fcaa7665
RS
385/* Indicate boundary between left and right. */
386
387static void
388push_left_right_boundary ()
389{
390 if (menu_items_used + 1 > menu_items_allocated)
391 grow_menu_items ();
392
393 XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
394}
395
c9e60620 396/* Start a new menu pane in menu_items.
78589e07 397 NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
1658603c 398
78589e07
RS
399static void
400push_menu_pane (name, prefix_vec)
401 Lisp_Object name, prefix_vec;
402{
403 if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
101bb4a5 404 grow_menu_items ();
dcfdbac7 405
101bb4a5
RS
406 if (menu_items_submenu_depth == 0)
407 menu_items_n_panes++;
78589e07
RS
408 XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
409 XVECTOR (menu_items)->contents[menu_items_used++] = name;
410 XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
411}
dcfdbac7 412
3427a3db
GM
413/* Push one menu item into the current pane. NAME is the string to
414 display. ENABLE if non-nil means this item can be selected. KEY
415 is the key generated by choosing this item, or nil if this item
416 doesn't really have a definition. DEF is the definition of this
417 item. EQUIV is the textual description of the keyboard equivalent
418 for this item (or nil if none). TYPE is the type of this menu
419 item, one of nil, `toggle' or `radio'. */
18686d47 420
78589e07 421static void
850df50b
GM
422push_menu_item (name, enable, key, def, equiv, type, selected, help)
423 Lisp_Object name, enable, key, def, equiv, type, selected, help;
78589e07
RS
424{
425 if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
101bb4a5 426 grow_menu_items ();
088831f6 427
78589e07
RS
428 XVECTOR (menu_items)->contents[menu_items_used++] = name;
429 XVECTOR (menu_items)->contents[menu_items_used++] = enable;
430 XVECTOR (menu_items)->contents[menu_items_used++] = key;
431 XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
a352a815 432 XVECTOR (menu_items)->contents[menu_items_used++] = def;
3427a3db
GM
433 XVECTOR (menu_items)->contents[menu_items_used++] = type;
434 XVECTOR (menu_items)->contents[menu_items_used++] = selected;
850df50b 435 XVECTOR (menu_items)->contents[menu_items_used++] = help;
78589e07
RS
436}
437\f
78589e07 438/* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
101bb4a5
RS
439 and generate menu panes for them in menu_items.
440 If NOTREAL is nonzero,
441 don't bother really computing whether an item is enabled. */
18686d47 442
78589e07 443static void
101bb4a5 444keymap_panes (keymaps, nmaps, notreal)
78589e07
RS
445 Lisp_Object *keymaps;
446 int nmaps;
101bb4a5 447 int notreal;
18686d47 448{
78589e07 449 int mapno;
18686d47 450
78589e07 451 init_menu_items ();
18686d47 452
78589e07
RS
453 /* Loop over the given keymaps, making a pane for each map.
454 But don't make a pane that is empty--ignore that map instead.
455 P is the number of panes we have made so far. */
456 for (mapno = 0; mapno < nmaps; mapno++)
488205c0 457 single_keymap_panes (keymaps[mapno],
5ee707b8 458 Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10);
78589e07
RS
459
460 finish_menu_items ();
461}
462
0695ce91
SM
463/* Args passed between single_keymap_panes and single_menu_item. */
464struct skp
465 {
466 Lisp_Object pending_maps;
467 int maxdepth, notreal;
468 int notbuttons;
469 };
470
471static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
aea448d2 472 void *));
0695ce91 473
78589e07
RS
474/* This is a recursive subroutine of keymap_panes.
475 It handles one keymap, KEYMAP.
476 The other arguments are passed along
101bb4a5 477 or point to local variables of the previous function.
de57a39c
RS
478 If NOTREAL is nonzero, only check for equivalent key bindings, don't
479 evaluate expressions in menu items and don't make any menu.
98381e42
RS
480
481 If we encounter submenus deeper than MAXDEPTH levels, ignore them. */
78589e07
RS
482
483static void
98381e42 484single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
78589e07
RS
485 Lisp_Object keymap;
486 Lisp_Object pane_name;
487 Lisp_Object prefix;
101bb4a5 488 int notreal;
98381e42 489 int maxdepth;
78589e07 490{
0695ce91
SM
491 struct skp skp;
492 struct gcpro gcpro1;
493
494 skp.pending_maps = Qnil;
495 skp.maxdepth = maxdepth;
496 skp.notreal = notreal;
497 skp.notbuttons = 0;
78589e07 498
98381e42
RS
499 if (maxdepth <= 0)
500 return;
501
78589e07
RS
502 push_menu_pane (pane_name, prefix);
503
de57a39c
RS
504#ifndef HAVE_BOXES
505 /* Remember index for first item in this pane so we can go back and
506 add a prefix when (if) we see the first button. After that, notbuttons
507 is set to 0, to mark that we have seen a button and all non button
508 items need a prefix. */
0695ce91 509 skp.notbuttons = menu_items_used;
de57a39c
RS
510#endif
511
0695ce91
SM
512 GCPRO1 (skp.pending_maps);
513 map_keymap (keymap, single_menu_item, Qnil, &skp, 1);
514 UNGCPRO;
78589e07
RS
515
516 /* Process now any submenus which want to be panes at this level. */
0695ce91 517 while (CONSP (skp.pending_maps))
78589e07 518 {
101bb4a5 519 Lisp_Object elt, eltcdr, string;
0695ce91 520 elt = XCAR (skp.pending_maps);
8e713be6
KR
521 eltcdr = XCDR (elt);
522 string = XCAR (eltcdr);
101bb4a5
RS
523 /* We no longer discard the @ from the beginning of the string here.
524 Instead, we do this in xmenu_show. */
525 single_keymap_panes (Fcar (elt), string,
8e713be6 526 XCDR (eltcdr), notreal, maxdepth - 1);
0695ce91 527 skp.pending_maps = XCDR (skp.pending_maps);
78589e07 528 }
18686d47 529}
78589e07 530\f
de57a39c
RS
531/* This is a subroutine of single_keymap_panes that handles one
532 keymap entry.
177c0ea7 533 KEY is a key in a keymap and ITEM is its binding.
0695ce91 534 SKP->PENDING_MAPS_PTR is a list of keymaps waiting to be made into
de57a39c 535 separate panes.
0695ce91 536 If SKP->NOTREAL is nonzero, only check for equivalent key bindings, don't
de57a39c 537 evaluate expressions in menu items and don't make any menu.
0695ce91
SM
538 If we encounter submenus deeper than SKP->MAXDEPTH levels, ignore them.
539 SKP->NOTBUTTONS is only used when simulating toggle boxes and radio
540 buttons. It keeps track of if we have seen a button in this menu or
541 not. */
de57a39c
RS
542
543static void
aea448d2 544single_menu_item (key, item, dummy, skp_v)
0695ce91 545 Lisp_Object key, item, dummy;
aea448d2 546 void *skp_v;
de57a39c 547{
faa935b6 548 Lisp_Object map, item_string, enabled;
de57a39c
RS
549 struct gcpro gcpro1, gcpro2;
550 int res;
aea448d2 551 struct skp *skp = skp_v;
177c0ea7 552
de57a39c
RS
553 /* Parse the menu item and leave the result in item_properties. */
554 GCPRO2 (key, item);
0695ce91 555 res = parse_menu_item (item, skp->notreal, 0);
de57a39c
RS
556 UNGCPRO;
557 if (!res)
558 return; /* Not a menu item. */
559
560 map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
b9de078a 561
0695ce91 562 if (skp->notreal)
de57a39c
RS
563 {
564 /* We don't want to make a menu, just traverse the keymaps to
565 precompute equivalent key bindings. */
566 if (!NILP (map))
0695ce91 567 single_keymap_panes (map, Qnil, key, 1, skp->maxdepth - 1);
de57a39c
RS
568 return;
569 }
570
571 enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE];
177c0ea7 572 item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME];
de57a39c 573
d5db4077 574 if (!NILP (map) && SREF (item_string, 0) == '@')
de57a39c
RS
575 {
576 if (!NILP (enabled))
577 /* An enabled separate pane. Remember this to handle it later. */
0695ce91
SM
578 skp->pending_maps = Fcons (Fcons (map, Fcons (item_string, key)),
579 skp->pending_maps);
de57a39c
RS
580 return;
581 }
582
583#ifndef HAVE_BOXES
584 /* Simulate radio buttons and toggle boxes by putting a prefix in
585 front of them. */
586 {
587 Lisp_Object prefix = Qnil;
588 Lisp_Object type = XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE];
589 if (!NILP (type))
590 {
591 Lisp_Object selected
592 = XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED];
593
0695ce91 594 if (skp->notbuttons)
de57a39c
RS
595 /* The first button. Line up previous items in this menu. */
596 {
0695ce91 597 int index = skp->notbuttons; /* Index for first item this menu. */
de57a39c
RS
598 int submenu = 0;
599 Lisp_Object tem;
600 while (index < menu_items_used)
601 {
602 tem
603 = XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME];
604 if (NILP (tem))
605 {
606 index++;
607 submenu++; /* Skip sub menu. */
608 }
609 else if (EQ (tem, Qlambda))
610 {
611 index++;
612 submenu--; /* End sub menu. */
613 }
614 else if (EQ (tem, Qt))
615 index += 3; /* Skip new pane marker. */
616 else if (EQ (tem, Qquote))
617 index++; /* Skip a left, right divider. */
618 else
619 {
d5db4077
KR
620 if (!submenu && SREF (tem, 0) != '\0'
621 && SREF (tem, 0) != '-')
de57a39c
RS
622 XVECTOR (menu_items)->contents[index + MENU_ITEMS_ITEM_NAME]
623 = concat2 (build_string (" "), tem);
624 index += MENU_ITEMS_ITEM_LENGTH;
625 }
626 }
0695ce91 627 skp->notbuttons = 0;
de57a39c
RS
628 }
629
630 /* Calculate prefix, if any, for this item. */
631 if (EQ (type, QCtoggle))
632 prefix = build_string (NILP (selected) ? "[ ] " : "[X] ");
633 else if (EQ (type, QCradio))
634 prefix = build_string (NILP (selected) ? "( ) " : "(*) ");
635 }
636 /* Not a button. If we have earlier buttons, then we need a prefix. */
0695ce91 637 else if (!skp->notbuttons && SREF (item_string, 0) != '\0'
d5db4077 638 && SREF (item_string, 0) != '-')
de57a39c
RS
639 prefix = build_string (" ");
640
641 if (!NILP (prefix))
642 item_string = concat2 (prefix, item_string);
643 }
644#endif /* not HAVE_BOXES */
177c0ea7 645
488dd4c4 646#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
6b61353c 647 if (!NILP (map))
de57a39c
RS
648 /* Indicate visually that this is a submenu. */
649 item_string = concat2 (item_string, build_string (" >"));
650#endif
651
652 push_menu_item (item_string, enabled, key,
653 XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
3427a3db
GM
654 XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
655 XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
850df50b
GM
656 XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
657 XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
de57a39c 658
488dd4c4 659#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
de57a39c
RS
660 /* Display a submenu using the toolkit. */
661 if (! (NILP (map) || NILP (enabled)))
662 {
663 push_submenu_start ();
0695ce91 664 single_keymap_panes (map, Qnil, key, 0, skp->maxdepth - 1);
de57a39c
RS
665 push_submenu_end ();
666 }
667#endif
668}
669\f
8e6208c5 670/* Push all the panes and items of a menu described by the
78589e07
RS
671 alist-of-alists MENU.
672 This handles old-fashioned calls to x-popup-menu. */
18686d47 673
78589e07
RS
674static void
675list_of_panes (menu)
18686d47 676 Lisp_Object menu;
18686d47 677{
78589e07
RS
678 Lisp_Object tail;
679
680 init_menu_items ();
681
8af9fa55 682 for (tail = menu; CONSP (tail); tail = XCDR (tail))
78589e07
RS
683 {
684 Lisp_Object elt, pane_name, pane_data;
8af9fa55 685 elt = XCAR (tail);
78589e07 686 pane_name = Fcar (elt);
b7826503 687 CHECK_STRING (pane_name);
646f98ec 688 push_menu_pane (ENCODE_MENU_STRING (pane_name), Qnil);
78589e07 689 pane_data = Fcdr (elt);
b7826503 690 CHECK_CONS (pane_data);
78589e07
RS
691 list_of_items (pane_data);
692 }
693
694 finish_menu_items ();
695}
696
697/* Push the items in a single pane defined by the alist PANE. */
698
699static void
700list_of_items (pane)
701 Lisp_Object pane;
702{
703 Lisp_Object tail, item, item1;
704
8af9fa55 705 for (tail = pane; CONSP (tail); tail = XCDR (tail))
78589e07 706 {
8af9fa55 707 item = XCAR (tail);
78589e07 708 if (STRINGP (item))
646f98ec
DL
709 push_menu_item (ENCODE_MENU_STRING (item), Qnil, Qnil, Qt,
710 Qnil, Qnil, Qnil, Qnil);
8af9fa55 711 else if (CONSP (item))
78589e07 712 {
8af9fa55 713 item1 = XCAR (item);
b7826503 714 CHECK_STRING (item1);
8af9fa55 715 push_menu_item (ENCODE_MENU_STRING (item1), Qt, XCDR (item),
646f98ec 716 Qt, Qnil, Qnil, Qnil, Qnil);
78589e07 717 }
8af9fa55
SM
718 else
719 push_left_right_boundary ();
720
78589e07
RS
721 }
722}
723\f
a894238b
JD
724#ifdef HAVE_X_WINDOWS
725/* Return the mouse position in *X and *Y. The coordinates are window
726 relative for the edit window in frame F.
727 This is for Fx_popup_menu. The mouse_position_hook can not
728 be used for X, as it returns window relative coordinates
729 for the window where the mouse is in. This could be the menu bar,
730 the scroll bar or the edit window. Fx_popup_menu needs to be
731 sure it is the edit window. */
732static void
6b61353c 733mouse_position_for_popup (f, x, y)
a894238b
JD
734 FRAME_PTR f;
735 int *x;
736 int *y;
737{
738 Window root, dummy_window;
739 int dummy;
740
62af879c
KL
741 if (! FRAME_X_P (f))
742 abort ();
743
a894238b 744 BLOCK_INPUT;
177c0ea7 745
a894238b
JD
746 XQueryPointer (FRAME_X_DISPLAY (f),
747 DefaultRootWindow (FRAME_X_DISPLAY (f)),
748
749 /* The root window which contains the pointer. */
750 &root,
751
752 /* Window pointer is on, not used */
753 &dummy_window,
754
755 /* The position on that root window. */
756 x, y,
757
758 /* x/y in dummy_window coordinates, not used. */
759 &dummy, &dummy,
760
761 /* Modifier keys and pointer buttons, about which
762 we don't care. */
763 (unsigned int *) &dummy);
764
765 UNBLOCK_INPUT;
766
767 /* xmenu_show expects window coordinates, not root window
768 coordinates. Translate. */
9882535b
KS
769 *x -= f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
770 *y -= f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
a894238b
JD
771}
772
773#endif /* HAVE_X_WINDOWS */
774
540e52d1 775DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
f6e34773 776 doc: /* Pop up a deck-of-cards menu and return user's selection.
228299fa
GM
777POSITION is a position specification. This is either a mouse button event
778or a list ((XOFFSET YOFFSET) WINDOW)
779where XOFFSET and YOFFSET are positions in pixels from the top left
e3fbac65 780corner of WINDOW. (WINDOW may be a window or a frame object.)
453a4f1b 781This controls the position of the top left of the menu as a whole.
228299fa
GM
782If POSITION is t, it means to use the current mouse position.
783
784MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
785The menu items come from key bindings that have a menu string as well as
786a definition; actually, the "definition" in such a key binding looks like
787\(STRING . REAL-DEFINITION). To give the menu a title, put a string into
788the keymap as a top-level element.
789
790If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
791Otherwise, REAL-DEFINITION should be a valid key binding definition.
792
793You can also use a list of keymaps as MENU.
794 Then each keymap makes a separate pane.
e3fbac65
LT
795
796When MENU is a keymap or a list of keymaps, the return value is the
797list of events corresponding to the user's choice. Note that
798`x-popup-menu' does not actually execute the command bound to that
799sequence of events.
228299fa
GM
800
801Alternatively, you can specify a menu of multiple panes
802 with a list of the form (TITLE PANE1 PANE2...),
803where each pane is a list of form (TITLE ITEM1 ITEM2...).
804Each ITEM is normally a cons cell (STRING . VALUE);
805but a string can appear as an item--that makes a nonselectable line
806in the menu.
807With this form of menu, the return value is VALUE from the chosen item.
808
809If POSITION is nil, don't display the menu at all, just precalculate the
c05fe5d5
LT
810cached information about equivalent key sequences.
811
812If the user gets rid of the menu without making a valid choice, for
813instance by clicking the mouse away from a valid choice or by typing
814keyboard input, then this normally results in a quit and
815`x-popup-menu' does not return. But if POSITION is a mouse button
816event (indicating that the user invoked the menu with the mouse) then
817no quit occurs and `x-popup-menu' returns nil. */)
7ee72033 818 (position, menu)
78589e07
RS
819 Lisp_Object position, menu;
820{
18686d47 821 Lisp_Object keymap, tem;
6bbd7a29 822 int xpos = 0, ypos = 0;
78589e07 823 Lisp_Object title;
999cdb8f 824 char *error_name = NULL;
93977219 825 Lisp_Object selection = Qnil;
cc45fb40 826 FRAME_PTR f = NULL;
78589e07
RS
827 Lisp_Object x, y, window;
828 int keymaps = 0;
9685a93f 829 int for_click = 0;
86fad4ec 830 int specpdl_count = SPECPDL_INDEX ();
78589e07
RS
831 struct gcpro gcpro1;
832
1e659e4c 833#ifdef HAVE_MENUS
78589e07
RS
834 if (! NILP (position))
835 {
488dd4c4 836 int get_current_pos_p = 0;
101bb4a5
RS
837 check_x ();
838
78589e07 839 /* Decode the first argument: find the window and the coordinates. */
9572375b 840 if (EQ (position, Qt)
2b06561a
DL
841 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
842 || EQ (XCAR (position), Qtool_bar))))
78589e07 843 {
488dd4c4
JD
844 get_current_pos_p = 1;
845 }
846 else
847 {
848 tem = Fcar (position);
849 if (CONSP (tem))
850 {
851 window = Fcar (Fcdr (position));
8af9fa55
SM
852 x = XCAR (tem);
853 y = Fcar (XCDR (tem));
488dd4c4
JD
854 }
855 else
856 {
857 for_click = 1;
858 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
859 window = Fcar (tem); /* POSN_WINDOW (tem) */
860 tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
861 x = Fcar (tem);
862 y = Fcdr (tem);
863 }
864
865 /* If a click happens in an external tool bar or a detached
866 tool bar, x and y is NIL. In that case, use the current
867 mouse position. This happens for the help button in the
868 tool bar. Ideally popup-menu should pass NIL to
869 this function, but it doesn't. */
870 if (NILP (x) && NILP (y))
871 get_current_pos_p = 1;
872 }
873
874 if (get_current_pos_p)
875 {
78589e07 876 /* Use the mouse's current position. */
b404828f 877 FRAME_PTR new_f = SELECTED_FRAME ();
a894238b
JD
878#ifdef HAVE_X_WINDOWS
879 /* Can't use mouse_position_hook for X since it returns
880 coordinates relative to the window the mouse is in,
881 we need coordinates relative to the edit widget always. */
882 if (new_f != 0)
883 {
884 int cur_x, cur_y;
885
886 mouse_position_for_popup (new_f, &cur_x, &cur_y);
887 /* cur_x/y may be negative, so use make_number. */
888 x = make_number (cur_x);
889 y = make_number (cur_y);
890 }
177c0ea7 891
a894238b
JD
892#else /* not HAVE_X_WINDOWS */
893 Lisp_Object bar_window;
894 enum scroll_bar_part part;
895 unsigned long time;
896
897 if (mouse_position_hook)
898 (*mouse_position_hook) (&new_f, 1, &bar_window,
899 &part, &x, &y, &time);
900#endif /* not HAVE_X_WINDOWS */
901
902 if (new_f != 0)
903 XSETFRAME (window, new_f);
904 else
905 {
906 window = selected_window;
907 XSETFASTINT (x, 0);
908 XSETFASTINT (y, 0);
909 }
78589e07 910 }
78589e07 911
b7826503
PJ
912 CHECK_NUMBER (x);
913 CHECK_NUMBER (y);
78589e07
RS
914
915 /* Decode where to put the menu. */
916
b5bb2705 917 if (FRAMEP (window))
78589e07
RS
918 {
919 f = XFRAME (window);
78589e07
RS
920 xpos = 0;
921 ypos = 0;
922 }
b5bb2705 923 else if (WINDOWP (window))
78589e07 924 {
b7826503 925 CHECK_LIVE_WINDOW (window);
78589e07
RS
926 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
927
9882535b
KS
928 xpos = WINDOW_LEFT_EDGE_X (XWINDOW (window));
929 ypos = WINDOW_TOP_EDGE_Y (XWINDOW (window));
78589e07
RS
930 }
931 else
932 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
933 but I don't want to make one now. */
b7826503 934 CHECK_WINDOW (window);
78589e07
RS
935
936 xpos += XINT (x);
937 ypos += XINT (y);
76a3c2cd 938
62af879c
KL
939 if (! FRAME_X_P (f))
940 error ("Can not put X menu on non-X terminal");
feba0cd6 941
76a3c2cd 942 XSETFRAME (Vmenu_updating_frame, f);
78589e07 943 }
76a3c2cd
JL
944 else
945 Vmenu_updating_frame = Qnil;
1e659e4c 946#endif /* HAVE_MENUS */
78589e07 947
86fad4ec 948 record_unwind_protect (unuse_menu_items, Qnil);
78589e07
RS
949 title = Qnil;
950 GCPRO1 (title);
951
952 /* Decode the menu items from what was specified. */
18686d47 953
02067692
SM
954 keymap = get_keymap (menu, 0, 0);
955 if (CONSP (keymap))
18686d47
RS
956 {
957 /* We were given a keymap. Extract menu info from the keymap. */
958 Lisp_Object prompt;
18686d47 959
78589e07 960 /* Extract the detailed info to make one pane. */
101bb4a5 961 keymap_panes (&menu, 1, NILP (position));
78589e07 962
18686d47
RS
963 /* Search for a string appearing directly as an element of the keymap.
964 That string is the title of the menu. */
5ee707b8 965 prompt = Fkeymap_prompt (keymap);
4f9ad016
RS
966 if (NILP (title) && !NILP (prompt))
967 title = prompt;
18686d47 968
78589e07
RS
969 /* Make that be the pane title of the first pane. */
970 if (!NILP (prompt) && menu_items_n_panes >= 0)
971 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
972
973 keymaps = 1;
18686d47 974 }
02067692 975 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
18686d47
RS
976 {
977 /* We were given a list of keymaps. */
18686d47
RS
978 int nmaps = XFASTINT (Flength (menu));
979 Lisp_Object *maps
980 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
981 int i;
78589e07
RS
982
983 title = Qnil;
18686d47
RS
984
985 /* The first keymap that has a prompt string
986 supplies the menu title. */
8af9fa55 987 for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
18686d47 988 {
78589e07
RS
989 Lisp_Object prompt;
990
8af9fa55 991 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
18686d47 992
5ee707b8 993 prompt = Fkeymap_prompt (keymap);
78589e07
RS
994 if (NILP (title) && !NILP (prompt))
995 title = prompt;
18686d47
RS
996 }
997
998 /* Extract the detailed info to make one pane. */
101bb4a5 999 keymap_panes (maps, nmaps, NILP (position));
78589e07
RS
1000
1001 /* Make the title be the pane title of the first pane. */
1002 if (!NILP (title) && menu_items_n_panes >= 0)
1003 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
1004
1005 keymaps = 1;
18686d47
RS
1006 }
1007 else
1008 {
1009 /* We were given an old-fashioned menu. */
78589e07 1010 title = Fcar (menu);
b7826503 1011 CHECK_STRING (title);
18686d47 1012
78589e07 1013 list_of_panes (Fcdr (menu));
18686d47 1014
78589e07
RS
1015 keymaps = 0;
1016 }
177c0ea7 1017
86fad4ec
SM
1018 unbind_to (specpdl_count, Qnil);
1019
78589e07 1020 if (NILP (position))
18686d47 1021 {
78589e07
RS
1022 discard_menu_items ();
1023 UNGCPRO;
1024 return Qnil;
18686d47
RS
1025 }
1026
1e659e4c 1027#ifdef HAVE_MENUS
78589e07
RS
1028 /* Display them in a menu. */
1029 BLOCK_INPUT;
18686d47 1030
673a6211 1031 selection = xmenu_show (f, xpos, ypos, for_click,
78589e07
RS
1032 keymaps, title, &error_name);
1033 UNBLOCK_INPUT;
18686d47 1034
78589e07 1035 discard_menu_items ();
18686d47 1036
78589e07 1037 UNGCPRO;
1e659e4c 1038#endif /* HAVE_MENUS */
18686d47 1039
78589e07
RS
1040 if (error_name) error (error_name);
1041 return selection;
18686d47 1042}
165e1749 1043
1e659e4c
RS
1044#ifdef HAVE_MENUS
1045
6a040d6a 1046DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
f6e34773 1047 doc: /* Pop up a dialog box and return user's selection.
228299fa
GM
1048POSITION specifies which frame to use.
1049This is normally a mouse button event or a window or frame.
1050If POSITION is t, it means to use the frame the mouse is on.
1051The dialog box appears in the middle of the specified frame.
1052
1053CONTENTS specifies the alternatives to display in the dialog box.
6a040d6a 1054It is a list of the form (DIALOG ITEM1 ITEM2...).
228299fa
GM
1055Each ITEM is a cons cell (STRING . VALUE).
1056The return value is VALUE from the chosen item.
1057
1058An ITEM may also be just a string--that makes a nonselectable item.
1059An ITEM may also be nil--that means to put all preceding items
1060on the left of the dialog box and all following items on the right.
c05fe5d5
LT
1061\(By default, approximately half appear on each side.)
1062
6a040d6a
NR
1063If HEADER is non-nil, the frame title for the box is "Information",
1064otherwise it is "Question".
1065
c05fe5d5
LT
1066If the user gets rid of the dialog box without making a valid choice,
1067for instance using the window manager, then this produces a quit and
1068`x-popup-dialog' does not return. */)
6a040d6a
NR
1069 (position, contents, header)
1070 Lisp_Object position, contents, header;
165e1749 1071{
cc45fb40 1072 FRAME_PTR f = NULL;
99fe880d 1073 Lisp_Object window;
165e1749
FP
1074
1075 check_x ();
1076
99fe880d 1077 /* Decode the first argument: find the window or frame to use. */
88d4f6ec 1078 if (EQ (position, Qt)
2b06561a
DL
1079 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
1080 || EQ (XCAR (position), Qtool_bar))))
165e1749 1081 {
b14db4d7 1082#if 0 /* Using the frame the mouse is on may not be right. */
99fe880d 1083 /* Use the mouse's current position. */
b404828f 1084 FRAME_PTR new_f = SELECTED_FRAME ();
99fe880d 1085 Lisp_Object bar_window;
cc45fb40 1086 enum scroll_bar_part part;
99fe880d
RS
1087 unsigned long time;
1088 Lisp_Object x, y;
165e1749 1089
46b657e2 1090 (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
5ca2ef64 1091
99fe880d 1092 if (new_f != 0)
a39f0477 1093 XSETFRAME (window, new_f);
99fe880d
RS
1094 else
1095 window = selected_window;
b14db4d7 1096#endif
88d4f6ec 1097 window = selected_window;
99fe880d
RS
1098 }
1099 else if (CONSP (position))
1100 {
1101 Lisp_Object tem;
1102 tem = Fcar (position);
b5bb2705 1103 if (CONSP (tem))
99fe880d 1104 window = Fcar (Fcdr (position));
80670155
RS
1105 else
1106 {
99fe880d
RS
1107 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
1108 window = Fcar (tem); /* POSN_WINDOW (tem) */
165e1749 1109 }
165e1749 1110 }
99fe880d
RS
1111 else if (WINDOWP (position) || FRAMEP (position))
1112 window = position;
a9be6839
RS
1113 else
1114 window = Qnil;
165e1749 1115
99fe880d 1116 /* Decode where to put the menu. */
165e1749 1117
b5bb2705 1118 if (FRAMEP (window))
99fe880d 1119 f = XFRAME (window);
b5bb2705 1120 else if (WINDOWP (window))
165e1749 1121 {
b7826503 1122 CHECK_LIVE_WINDOW (window);
99fe880d 1123 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
165e1749 1124 }
99fe880d
RS
1125 else
1126 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
1127 but I don't want to make one now. */
b7826503 1128 CHECK_WINDOW (window);
165e1749 1129
62af879c
KL
1130 if (! FRAME_X_P (f))
1131 error ("Can not put X dialog on non-X terminal");
1132
488dd4c4 1133#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
99fe880d
RS
1134 /* Display a menu with these alternatives
1135 in the middle of frame F. */
1136 {
1137 Lisp_Object x, y, frame, newpos;
a39f0477
KH
1138 XSETFRAME (frame, f);
1139 XSETINT (x, x_pixel_width (f) / 2);
1140 XSETINT (y, x_pixel_height (f) / 2);
99fe880d
RS
1141 newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
1142
1143 return Fx_popup_menu (newpos,
1144 Fcons (Fcar (contents), Fcons (contents, Qnil)));
1145 }
1146#else
1147 {
1148 Lisp_Object title;
1149 char *error_name;
1150 Lisp_Object selection;
86fad4ec 1151 int specpdl_count = SPECPDL_INDEX ();
165e1749 1152
99fe880d
RS
1153 /* Decode the dialog items from what was specified. */
1154 title = Fcar (contents);
b7826503 1155 CHECK_STRING (title);
86fad4ec 1156 record_unwind_protect (unuse_menu_items, Qnil);
165e1749 1157
6b61353c
KH
1158 if (NILP (Fcar (Fcdr (contents))))
1159 /* No buttons specified, add an "Ok" button so users can pop down
1160 the dialog. Also, the lesstif/motif version crashes if there are
1161 no buttons. */
1162 contents = Fcons (title, Fcons (Fcons (build_string ("Ok"), Qt), Qnil));
b9de078a 1163
99fe880d 1164 list_of_panes (Fcons (contents, Qnil));
165e1749 1165
99fe880d
RS
1166 /* Display them in a dialog box. */
1167 BLOCK_INPUT;
6a040d6a 1168 selection = xdialog_show (f, 0, title, header, &error_name);
99fe880d 1169 UNBLOCK_INPUT;
165e1749 1170
86fad4ec 1171 unbind_to (specpdl_count, Qnil);
99fe880d
RS
1172 discard_menu_items ();
1173
1174 if (error_name) error (error_name);
1175 return selection;
1176 }
7464b131 1177#endif
392d3f4b 1178}
c3438661
JD
1179
1180
1181#ifndef MSDOS
1182
b3935289
JD
1183/* Set menu_items_inuse so no other popup menu or dialog is created. */
1184
1185void
1186x_menu_set_in_use (in_use)
1187 int in_use;
1188{
1189 menu_items_inuse = in_use ? Qt : Qnil;
5ae53dcf 1190 popup_activated_flag = in_use;
98a20c65
CY
1191#ifdef USE_X_TOOLKIT
1192 if (popup_activated_flag)
1193 x_activate_timeout_atimer ();
1194#endif
b3935289
JD
1195}
1196
c3438661
JD
1197/* Wait for an X event to arrive or for a timer to expire. */
1198
b3935289 1199void
c3438661
JD
1200x_menu_wait_for_event (void *data)
1201{
1202 extern EMACS_TIME timer_check P_ ((int));
1203
1204 /* Another way to do this is to register a timer callback, that can be
1205 done in GTK and Xt. But we have to do it like this when using only X
1206 anyway, and with callbacks we would have three variants for timer handling
1207 instead of the small ifdefs below. */
1208
1209 while (
1210#ifdef USE_X_TOOLKIT
f1d1cd24 1211 ! XtAppPending (Xt_app_con)
c3438661
JD
1212#elif defined USE_GTK
1213 ! gtk_events_pending ()
1214#else
1215 ! XPending ((Display*) data)
1216#endif
1217 )
1218 {
1219 EMACS_TIME next_time = timer_check (1);
1220 long secs = EMACS_SECS (next_time);
1221 long usecs = EMACS_USECS (next_time);
1222 SELECT_TYPE read_fds;
1223 struct x_display_info *dpyinfo;
1224 int n = 0;
1225
1226 FD_ZERO (&read_fds);
1227 for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
1228 {
1229 int fd = ConnectionNumber (dpyinfo->display);
1230 FD_SET (fd, &read_fds);
1231 if (fd > n) n = fd;
1232 }
1233
1234 if (secs < 0 || (secs == 0 && usecs == 0))
1235 {
1236 /* Sometimes timer_check returns -1 (no timers) even if there are
1237 timers. So do a timeout anyway. */
1238 EMACS_SET_SECS (next_time, 1);
1239 EMACS_SET_USECS (next_time, 0);
1240 }
1241
1242 select (n + 1, &read_fds, (SELECT_TYPE *)0, (SELECT_TYPE *)0, &next_time);
1243 }
1244}
1245#endif /* ! MSDOS */
1246
78589e07 1247\f
488dd4c4 1248#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
18686d47 1249
f1d1cd24
JD
1250#ifdef USE_X_TOOLKIT
1251
4dedbfe0 1252/* Loop in Xt until the menu pulldown or dialog popup has been
f56e7ad2 1253 popped down (deactivated). This is used for x-popup-menu
f02cac82
RS
1254 and x-popup-dialog; it is not used for the menu bar.
1255
2e2b8e22 1256 NOTE: All calls to popup_get_selection should be protected
c98fcf4b 1257 with BLOCK_INPUT, UNBLOCK_INPUT wrappers. */
aa669def 1258
f02cac82 1259static void
95bdef2e 1260popup_get_selection (initial_event, dpyinfo, id, do_timers)
4dedbfe0 1261 XEvent *initial_event;
aa669def 1262 struct x_display_info *dpyinfo;
2e2b8e22 1263 LWLIB_ID id;
df470e3b 1264 int do_timers;
78589e07 1265{
4dedbfe0 1266 XEvent event;
f02cac82 1267
8b806a85
JD
1268 while (popup_activated_flag)
1269 {
c0b0f54b 1270 if (initial_event)
8b806a85
JD
1271 {
1272 event = *initial_event;
1273 initial_event = 0;
1274 }
1275 else
c3438661
JD
1276 {
1277 if (do_timers) x_menu_wait_for_event (0);
1278 XtAppNextEvent (Xt_app_con, &event);
1279 }
78589e07 1280
10624005 1281 /* Make sure we don't consider buttons grabbed after menu goes.
8b806a85
JD
1282 And make sure to deactivate for any ButtonRelease,
1283 even if XtDispatchEvent doesn't do that. */
1284 if (event.type == ButtonRelease
1285 && dpyinfo->display == event.xbutton.display)
10624005
KH
1286 {
1287 dpyinfo->grabbed &= ~(1 << event.xbutton.button);
177c0ea7 1288#ifdef USE_MOTIF /* Pretending that the event came from a
8b806a85
JD
1289 Btn1Down seems the only way to convince Motif to
1290 activate its callbacks; setting the XmNmenuPost
1291 isn't working. --marcus@sysc.pdx.edu. */
1292 event.xbutton.button = 1;
1293 /* Motif only pops down menus when no Ctrl, Alt or Mod
1294 key is pressed and the button is released. So reset key state
1295 so Motif thinks this is the case. */
1296 event.xbutton.state = 0;
c8b5aa3d 1297#endif
10624005 1298 }
33385c6f 1299 /* Pop down on C-g and Escape. */
2e2b8e22 1300 else if (event.type == KeyPress
10624005 1301 && dpyinfo->display == event.xbutton.display)
8b806a85
JD
1302 {
1303 KeySym keysym = XLookupKeysym (&event.xkey, 0);
9f6fcdc5 1304
33385c6f
JD
1305 if ((keysym == XK_g && (event.xkey.state & ControlMask) != 0)
1306 || keysym == XK_Escape) /* Any escape, ignore modifiers. */
9f6fcdc5 1307 popup_activated_flag = 0;
8b806a85 1308 }
177c0ea7 1309
8b806a85 1310 x_dispatch_event (&event, event.xany.display);
aa669def 1311 }
78589e07
RS
1312}
1313
12b6af5c 1314DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
22badffe
JD
1315 doc: /* Start key navigation of the menu bar in FRAME.
1316This initially opens the first menu bar item and you can then navigate with the
1317arrow keys, select a menu entry with the return key or cancel with the
1318escape key. If FRAME has no menu bar this function does nothing.
1319
1320If FRAME is nil or not given, use the selected frame. */)
1321 (frame)
1322 Lisp_Object frame;
1323{
1324 XEvent ev;
1325 FRAME_PTR f = check_x_frame (frame);
1326 Widget menubar;
1327 BLOCK_INPUT;
1328
1329 if (FRAME_EXTERNAL_MENU_BAR (f))
1330 set_frame_menubar (f, 0, 1);
1331
1332 menubar = FRAME_X_OUTPUT (f)->menubar_widget;
1333 if (menubar)
1334 {
1335 Window child;
1336 int error_p = 0;
1337
1338 x_catch_errors (FRAME_X_DISPLAY (f));
1339 memset (&ev, 0, sizeof ev);
1340 ev.xbutton.display = FRAME_X_DISPLAY (f);
1341 ev.xbutton.window = XtWindow (menubar);
1342 ev.xbutton.root = FRAME_X_DISPLAY_INFO (f)->root_window;
1343 ev.xbutton.time = XtLastTimestampProcessed (FRAME_X_DISPLAY (f));
1344 ev.xbutton.button = Button1;
1345 ev.xbutton.x = ev.xbutton.y = FRAME_MENUBAR_HEIGHT (f) / 2;
1346 ev.xbutton.same_screen = True;
1347
1348#ifdef USE_MOTIF
1349 {
1350 Arg al[2];
1351 WidgetList list;
1352 Cardinal nr;
1353 XtSetArg (al[0], XtNchildren, &list);
1354 XtSetArg (al[1], XtNnumChildren, &nr);
1355 XtGetValues (menubar, al, 2);
1356 ev.xbutton.window = XtWindow (list[0]);
1357 }
1358#endif
1359
1360 XTranslateCoordinates (FRAME_X_DISPLAY (f),
1361 /* From-window, to-window. */
1362 ev.xbutton.window, ev.xbutton.root,
1363
1364 /* From-position, to-position. */
1365 ev.xbutton.x, ev.xbutton.y,
1366 &ev.xbutton.x_root, &ev.xbutton.y_root,
1367
1368 /* Child of win. */
1369 &child);
1370 error_p = x_had_errors_p (FRAME_X_DISPLAY (f));
1371 x_uncatch_errors ();
1372
1373 if (! error_p)
1374 {
1375 ev.type = ButtonPress;
1376 ev.xbutton.state = 0;
1377
1378 XtDispatchEvent (&ev);
1379 ev.xbutton.type = ButtonRelease;
1380 ev.xbutton.state = Button1Mask;
1381 XtDispatchEvent (&ev);
1382 }
1383 }
1384
1385 UNBLOCK_INPUT;
57d671b4
AS
1386
1387 return Qnil;
22badffe 1388}
488dd4c4
JD
1389#endif /* USE_X_TOOLKIT */
1390
22badffe 1391
488dd4c4 1392#ifdef USE_GTK
12b6af5c 1393DEFUN ("x-menu-bar-open-internal", Fx_menu_bar_open_internal, Sx_menu_bar_open_internal, 0, 1, "i",
22badffe
JD
1394 doc: /* Start key navigation of the menu bar in FRAME.
1395This initially opens the first menu bar item and you can then navigate with the
1396arrow keys, select a menu entry with the return key or cancel with the
1397escape key. If FRAME has no menu bar this function does nothing.
1398
1399If FRAME is nil or not given, use the selected frame. */)
1400 (frame)
1401 Lisp_Object frame;
1402{
1403 GtkWidget *menubar;
8630721e
CY
1404 FRAME_PTR f;
1405
718aeeb8
CY
1406 /* gcc 2.95 doesn't accept the FRAME_PTR declaration after
1407 BLOCK_INPUT. */
1408
22badffe 1409 BLOCK_INPUT;
8630721e 1410 f = check_x_frame (frame);
22badffe
JD
1411
1412 if (FRAME_EXTERNAL_MENU_BAR (f))
1413 set_frame_menubar (f, 0, 1);
1414
1415 menubar = FRAME_X_OUTPUT (f)->menubar_widget;
1416 if (menubar)
1417 {
1418 /* Activate the first menu. */
1419 GList *children = gtk_container_get_children (GTK_CONTAINER (menubar));
1420
1421 gtk_menu_shell_select_item (GTK_MENU_SHELL (menubar),
1422 GTK_WIDGET (children->data));
e3135734 1423
22badffe
JD
1424 popup_activated_flag = 1;
1425 g_list_free (children);
1426 }
1427 UNBLOCK_INPUT;
1428
1429 return Qnil;
1430}
1431
488dd4c4
JD
1432/* Loop util popup_activated_flag is set to zero in a callback.
1433 Used for popup menus and dialogs. */
f1d1cd24 1434
488dd4c4 1435static void
f1d1cd24 1436popup_widget_loop (do_timers, widget)
c3438661 1437 int do_timers;
f1d1cd24 1438 GtkWidget *widget;
488dd4c4
JD
1439{
1440 ++popup_activated_flag;
1441
1442 /* Process events in the Gtk event loop until done. */
1443 while (popup_activated_flag)
1444 {
c3438661 1445 if (do_timers) x_menu_wait_for_event (0);
488dd4c4
JD
1446 gtk_main_iteration ();
1447 }
1448}
1449#endif
1450
88766961
RS
1451/* Activate the menu bar of frame F.
1452 This is called from keyboard.c when it gets the
3b8f9651 1453 MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
88766961
RS
1454
1455 To activate the menu bar, we use the X button-press event
ac78b144 1456 that was saved in saved_menu_event.
88766961
RS
1457 That makes the toolkit do its thing.
1458
1459 But first we recompute the menu bar contents (the whole tree).
1460
1461 The reason for saving the button event until here, instead of
1462 passing it to the toolkit right away, is that we can safely
1463 execute Lisp code. */
177c0ea7 1464
dfcf069d 1465void
88766961
RS
1466x_activate_menubar (f)
1467 FRAME_PTR f;
1468{
62af879c
KL
1469 if (! FRAME_X_P (f))
1470 abort ();
1471
ac78b144 1472 if (!f->output_data.x->saved_menu_event->type)
88766961
RS
1473 return;
1474
177c0ea7 1475#ifdef USE_GTK
6b61353c
KH
1476 if (! xg_win_to_widget (FRAME_X_DISPLAY (f),
1477 f->output_data.x->saved_menu_event->xany.window))
488dd4c4
JD
1478 return;
1479#endif
177c0ea7 1480
a9be6839 1481 set_frame_menubar (f, 0, 1);
88766961 1482 BLOCK_INPUT;
488dd4c4
JD
1483#ifdef USE_GTK
1484 XPutBackEvent (f->output_data.x->display_info->display,
1485 f->output_data.x->saved_menu_event);
1486 popup_activated_flag = 1;
1487#else
86fad4ec 1488 XtDispatchEvent (f->output_data.x->saved_menu_event);
488dd4c4 1489#endif
88766961 1490 UNBLOCK_INPUT;
a9be6839 1491#ifdef USE_MOTIF
745c34fb
RS
1492 if (f->output_data.x->saved_menu_event->type == ButtonRelease)
1493 pending_menu_activation = 1;
a9be6839 1494#endif
177c0ea7 1495
88766961 1496 /* Ignore this if we get it a second time. */
ac78b144 1497 f->output_data.x->saved_menu_event->type = 0;
88766961
RS
1498}
1499
4dedbfe0
PR
1500/* This callback is invoked when the user selects a menubar cascade
1501 pushbutton, but before the pulldown menu is posted. */
78589e07 1502
488dd4c4 1503#ifndef USE_GTK
78589e07 1504static void
4dedbfe0 1505popup_activate_callback (widget, id, client_data)
78589e07
RS
1506 Widget widget;
1507 LWLIB_ID id;
1508 XtPointer client_data;
1509{
4dedbfe0 1510 popup_activated_flag = 1;
98a20c65
CY
1511#ifdef USE_X_TOOLKIT
1512 x_activate_timeout_atimer ();
1513#endif
7555d825 1514}
488dd4c4 1515#endif
7555d825
GM
1516
1517/* This callback is invoked when a dialog or menu is finished being
1518 used and has been unposted. */
1519
488dd4c4
JD
1520#ifdef USE_GTK
1521static void
1522popup_deactivate_callback (widget, client_data)
1523 GtkWidget *widget;
1524 gpointer client_data;
1525{
1526 popup_activated_flag = 0;
1527}
1528#else
7555d825
GM
1529static void
1530popup_deactivate_callback (widget, id, client_data)
1531 Widget widget;
1532 LWLIB_ID id;
1533 XtPointer client_data;
1534{
7555d825 1535 popup_activated_flag = 0;
78589e07 1536}
488dd4c4 1537#endif
78589e07 1538
850df50b 1539
488dd4c4
JD
1540/* Function that finds the frame for WIDGET and shows the HELP text
1541 for that widget.
1542 F is the frame if known, or NULL if not known. */
1543static void
1544show_help_event (f, widget, help)
1545 FRAME_PTR f;
1546 xt_or_gtk_widget widget;
1547 Lisp_Object help;
850df50b 1548{
488dd4c4 1549 Lisp_Object frame;
850df50b 1550
850df50b 1551 if (f)
9cd50434
GM
1552 {
1553 XSETFRAME (frame, f);
1554 kbd_buffer_store_help_event (frame, help);
1555 }
850df50b
GM
1556 else
1557 {
6b61353c 1558#if 0 /* This code doesn't do anything useful. ++kfs */
177c0ea7 1559 /* WIDGET is the popup menu. It's parent is the frame's
850df50b 1560 widget. See which frame that is. */
488dd4c4 1561 xt_or_gtk_widget frame_widget = XtParent (widget);
850df50b
GM
1562 Lisp_Object tail;
1563
8e50cc2d 1564 for (tail = Vframe_list; CONSP (tail); tail = XCDR (tail))
850df50b
GM
1565 {
1566 frame = XCAR (tail);
8e50cc2d 1567 if (FRAMEP (frame)
850df50b
GM
1568 && (f = XFRAME (frame),
1569 FRAME_X_P (f) && f->output_data.x->widget == frame_widget))
1570 break;
1571 }
6b61353c 1572#endif
9cd50434
GM
1573 show_help_echo (help, Qnil, Qnil, Qnil, 1);
1574 }
850df50b
GM
1575}
1576
488dd4c4
JD
1577/* Callback called when menu items are highlighted/unhighlighted
1578 while moving the mouse over them. WIDGET is the menu bar or menu
1579 popup widget. ID is its LWLIB_ID. CALL_DATA contains a pointer to
1580 the data structure for the menu item, or null in case of
1581 unhighlighting. */
4dedbfe0 1582
488dd4c4
JD
1583#ifdef USE_GTK
1584void
1585menu_highlight_callback (widget, call_data)
1586 GtkWidget *widget;
1587 gpointer call_data;
1588{
1589 xg_menu_item_cb_data *cb_data;
1590 Lisp_Object help;
177c0ea7 1591
488dd4c4
JD
1592 cb_data = (xg_menu_item_cb_data*) g_object_get_data (G_OBJECT (widget),
1593 XG_ITEM_DATA);
1594 if (! cb_data) return;
1595
1596 help = call_data ? cb_data->help : Qnil;
1597
1598 /* If popup_activated_flag is greater than 1 we are in a popup menu.
1599 Don't show help for them, they won't appear before the
1600 popup is popped down. */
1601 if (popup_activated_flag <= 1)
1602 show_help_event (cb_data->cl_data->f, widget, help);
1603}
1604#else
1605void
1606menu_highlight_callback (widget, id, call_data)
78589e07
RS
1607 Widget widget;
1608 LWLIB_ID id;
488dd4c4
JD
1609 void *call_data;
1610{
1611 struct frame *f;
1612 Lisp_Object help;
1613
1614 widget_value *wv = (widget_value *) call_data;
1615
1616 help = wv ? wv->help : Qnil;
177c0ea7 1617
488dd4c4
JD
1618 /* Determine the frame for the help event. */
1619 f = menubar_id_to_frame (id);
1620
1621 show_help_event (f, widget, help);
1622}
1623#endif
1624
1625/* Find the menu selection and store it in the keyboard buffer.
1626 F is the frame the menu is on.
1627 MENU_BAR_ITEMS_USED is the length of VECTOR.
81f09877
JD
1628 VECTOR is an array of menu events for the whole menu. */
1629
1630static void
488dd4c4
JD
1631find_and_call_menu_selection (f, menu_bar_items_used, vector, client_data)
1632 FRAME_PTR f;
f7fb8e56 1633 EMACS_INT menu_bar_items_used;
488dd4c4
JD
1634 Lisp_Object vector;
1635 void *client_data;
78589e07 1636{
c63f6952 1637 Lisp_Object prefix, entry;
4dedbfe0
PR
1638 Lisp_Object *subprefix_stack;
1639 int submenu_depth = 0;
1640 int i;
1641
6bbd7a29 1642 entry = Qnil;
488dd4c4 1643 subprefix_stack = (Lisp_Object *) alloca (menu_bar_items_used * sizeof (Lisp_Object));
4dedbfe0
PR
1644 prefix = Qnil;
1645 i = 0;
488dd4c4
JD
1646
1647 while (i < menu_bar_items_used)
4dedbfe0 1648 {
4dedbfe0
PR
1649 if (EQ (XVECTOR (vector)->contents[i], Qnil))
1650 {
1651 subprefix_stack[submenu_depth++] = prefix;
1652 prefix = entry;
1653 i++;
1654 }
1655 else if (EQ (XVECTOR (vector)->contents[i], Qlambda))
1656 {
1657 prefix = subprefix_stack[--submenu_depth];
1658 i++;
1659 }
1660 else if (EQ (XVECTOR (vector)->contents[i], Qt))
1661 {
4cb35c39 1662 prefix = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
4dedbfe0
PR
1663 i += MENU_ITEMS_PANE_LENGTH;
1664 }
1665 else
1666 {
4cb35c39 1667 entry = XVECTOR (vector)->contents[i + MENU_ITEMS_ITEM_VALUE];
01d5e892
RS
1668 /* The EMACS_INT cast avoids a warning. There's no problem
1669 as long as pointers have enough bits to hold small integers. */
1670 if ((int) (EMACS_INT) client_data == i)
4dedbfe0
PR
1671 {
1672 int j;
1673 struct input_event buf;
4cb35c39 1674 Lisp_Object frame;
38f7dce3 1675 EVENT_INIT (buf);
4dedbfe0 1676
4cb35c39 1677 XSETFRAME (frame, f);
9979315d
GM
1678 buf.kind = MENU_BAR_EVENT;
1679 buf.frame_or_window = frame;
1680 buf.arg = frame;
4dedbfe0
PR
1681 kbd_buffer_store_event (&buf);
1682
1683 for (j = 0; j < submenu_depth; j++)
1684 if (!NILP (subprefix_stack[j]))
1685 {
9979315d
GM
1686 buf.kind = MENU_BAR_EVENT;
1687 buf.frame_or_window = frame;
1688 buf.arg = subprefix_stack[j];
4dedbfe0
PR
1689 kbd_buffer_store_event (&buf);
1690 }
1691
1692 if (!NILP (prefix))
1693 {
9979315d
GM
1694 buf.kind = MENU_BAR_EVENT;
1695 buf.frame_or_window = frame;
1696 buf.arg = prefix;
4dedbfe0
PR
1697 kbd_buffer_store_event (&buf);
1698 }
1699
9979315d
GM
1700 buf.kind = MENU_BAR_EVENT;
1701 buf.frame_or_window = frame;
1702 buf.arg = entry;
4dedbfe0
PR
1703 kbd_buffer_store_event (&buf);
1704
1705 return;
1706 }
1707 i += MENU_ITEMS_ITEM_LENGTH;
1708 }
1709 }
18686d47
RS
1710}
1711
488dd4c4
JD
1712
1713#ifdef USE_GTK
1714/* Gtk calls callbacks just because we tell it what item should be
1715 selected in a radio group. If this variable is set to a non-zero
1716 value, we are creating menus and don't want callbacks right now.
1717*/
1718static int xg_crazy_callback_abort;
1719
1720/* This callback is called from the menu bar pulldown menu
1721 when the user makes a selection.
1722 Figure out what the user chose
1723 and put the appropriate events into the keyboard buffer. */
1724static void
1725menubar_selection_callback (widget, client_data)
1726 GtkWidget *widget;
1727 gpointer client_data;
1728{
1729 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data*) client_data;
1730
1731 if (xg_crazy_callback_abort)
1732 return;
1733
1734 if (! cb_data || ! cb_data->cl_data || ! cb_data->cl_data->f)
1735 return;
1736
b79c8219
JD
1737 /* For a group of radio buttons, GTK calls the selection callback first
1738 for the item that was active before the selection and then for the one that
1739 is active after the selection. For C-h k this means we get the help on
1740 the deselected item and then the selected item is executed. Prevent that
1741 by ignoring the non-active item. */
1742 if (GTK_IS_RADIO_MENU_ITEM (widget)
1743 && ! gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget)))
1744 return;
1745
81f09877
JD
1746 /* When a menu is popped down, X generates a focus event (i.e. focus
1747 goes back to the frame below the menu). Since GTK buffers events,
1748 we force it out here before the menu selection event. Otherwise
1749 sit-for will exit at once if the focus event follows the menu selection
1750 event. */
1751
1752 BLOCK_INPUT;
1753 while (gtk_events_pending ())
1754 gtk_main_iteration ();
1755 UNBLOCK_INPUT;
1756
488dd4c4
JD
1757 find_and_call_menu_selection (cb_data->cl_data->f,
1758 cb_data->cl_data->menu_bar_items_used,
1759 cb_data->cl_data->menu_bar_vector,
1760 cb_data->call_data);
1761}
1762
1763#else /* not USE_GTK */
1764
1765/* This callback is called from the menu bar pulldown menu
1766 when the user makes a selection.
1767 Figure out what the user chose
1768 and put the appropriate events into the keyboard buffer. */
1769static void
1770menubar_selection_callback (widget, id, client_data)
1771 Widget widget;
1772 LWLIB_ID id;
1773 XtPointer client_data;
1774{
1775 FRAME_PTR f;
1776
1777 f = menubar_id_to_frame (id);
1778 if (!f)
1779 return;
1780 find_and_call_menu_selection (f, f->menu_bar_items_used,
1781 f->menu_bar_vector, client_data);
1782}
1783#endif /* not USE_GTK */
1784
f9655c60
RS
1785/* Allocate a widget_value, blocking input. */
1786
1787widget_value *
1788xmalloc_widget_value ()
1789{
1790 widget_value *value;
1791
1792 BLOCK_INPUT;
1793 value = malloc_widget_value ();
1794 UNBLOCK_INPUT;
1795
1796 return value;
1797}
4dedbfe0
PR
1798
1799/* This recursively calls free_widget_value on the tree of widgets.
18686d47 1800 It must free all data that was malloc'ed for these widget_values.
78589e07
RS
1801 In Emacs, many slots are pointers into the data of Lisp_Strings, and
1802 must be left alone. */
1803
18686d47
RS
1804void
1805free_menubar_widget_value_tree (wv)
1806 widget_value *wv;
1807{
1808 if (! wv) return;
18686d47
RS
1809
1810 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
1811
1812 if (wv->contents && (wv->contents != (widget_value*)1))
1813 {
1814 free_menubar_widget_value_tree (wv->contents);
1815 wv->contents = (widget_value *) 0xDEADBEEF;
1816 }
1817 if (wv->next)
1818 {
1819 free_menubar_widget_value_tree (wv->next);
1820 wv->next = (widget_value *) 0xDEADBEEF;
1821 }
1822 BLOCK_INPUT;
1823 free_widget_value (wv);
1824 UNBLOCK_INPUT;
1825}
4dedbfe0 1826\f
e6c87a0f 1827/* Set up data in menu_items for a menu bar item
4dedbfe0
PR
1828 whose event type is ITEM_KEY (with string ITEM_NAME)
1829 and whose contents come from the list of keymaps MAPS. */
1830
ced89c24
RS
1831static int
1832parse_single_submenu (item_key, item_name, maps)
4dedbfe0
PR
1833 Lisp_Object item_key, item_name, maps;
1834{
4dedbfe0
PR
1835 Lisp_Object length;
1836 int len;
1837 Lisp_Object *mapvec;
ced89c24 1838 int i;
71dca3e3 1839 int top_level_items = 0;
4dedbfe0
PR
1840
1841 length = Flength (maps);
1842 len = XINT (length);
1843
1844 /* Convert the list MAPS into a vector MAPVEC. */
1845 mapvec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
1846 for (i = 0; i < len; i++)
1847 {
1848 mapvec[i] = Fcar (maps);
1849 maps = Fcdr (maps);
1850 }
1851
4dedbfe0
PR
1852 /* Loop over the given keymaps, making a pane for each map.
1853 But don't make a pane that is empty--ignore that map instead. */
1854 for (i = 0; i < len; i++)
71dca3e3 1855 {
e6c87a0f 1856 if (!KEYMAPP (mapvec[i]))
71dca3e3 1857 {
846e8c10
RS
1858 /* Here we have a command at top level in the menu bar
1859 as opposed to a submenu. */
71dca3e3
RS
1860 top_level_items = 1;
1861 push_menu_pane (Qnil, Qnil);
850df50b
GM
1862 push_menu_item (item_name, Qt, item_key, mapvec[i],
1863 Qnil, Qnil, Qnil, Qnil);
71dca3e3
RS
1864 }
1865 else
37dc84ff
RS
1866 {
1867 Lisp_Object prompt;
1868 prompt = Fkeymap_prompt (mapvec[i]);
1869 single_keymap_panes (mapvec[i],
1870 !NILP (prompt) ? prompt : item_name,
1871 item_key, 0, 10);
1872 }
71dca3e3 1873 }
4dedbfe0 1874
ced89c24
RS
1875 return top_level_items;
1876}
1877
1878/* Create a tree of widget_value objects
1879 representing the panes and items
1880 in menu_items starting at index START, up to index END. */
1881
1882static widget_value *
1883digest_single_submenu (start, end, top_level_items)
7448803e 1884 int start, end, top_level_items;
ced89c24
RS
1885{
1886 widget_value *wv, *prev_wv, *save_wv, *first_wv;
1887 int i;
1888 int submenu_depth = 0;
1889 widget_value **submenu_stack;
1fc4d463 1890 int panes_seen = 0;
4dedbfe0
PR
1891
1892 submenu_stack
1893 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
f7fab165 1894 wv = xmalloc_widget_value ();
4dedbfe0
PR
1895 wv->name = "menu";
1896 wv->value = 0;
1897 wv->enabled = 1;
3427a3db 1898 wv->button_type = BUTTON_TYPE_NONE;
0b1e6b54 1899 wv->help = Qnil;
4dedbfe0
PR
1900 first_wv = wv;
1901 save_wv = 0;
71dca3e3 1902 prev_wv = 0;
177c0ea7 1903
37dc84ff
RS
1904 /* Loop over all panes and items made by the preceding call
1905 to parse_single_submenu and construct a tree of widget_value objects.
1906 Ignore the panes and items used by previous calls to
1907 digest_single_submenu, even though those are also in menu_items. */
ced89c24
RS
1908 i = start;
1909 while (i < end)
4dedbfe0
PR
1910 {
1911 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1912 {
1913 submenu_stack[submenu_depth++] = save_wv;
1914 save_wv = prev_wv;
1915 prev_wv = 0;
1916 i++;
1917 }
1918 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1919 {
1920 prev_wv = save_wv;
1921 save_wv = submenu_stack[--submenu_depth];
1922 i++;
1923 }
1924 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1925 && submenu_depth != 0)
1926 i += MENU_ITEMS_PANE_LENGTH;
1927 /* Ignore a nil in the item list.
1928 It's meaningful only for dialog boxes. */
1929 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1930 i += 1;
1931 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1932 {
1933 /* Create a new pane. */
1934 Lisp_Object pane_name, prefix;
1935 char *pane_string;
177c0ea7 1936
1fc4d463
RS
1937 panes_seen++;
1938
4dedbfe0
PR
1939 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1940 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
177c0ea7 1941
703dc2a8
KH
1942#ifndef HAVE_MULTILINGUAL_MENU
1943 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
4c329aa8 1944 {
8af9fa55 1945 pane_name = ENCODE_MENU_STRING (pane_name);
4c329aa8
GM
1946 AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
1947 }
703dc2a8 1948#endif
4dedbfe0 1949 pane_string = (NILP (pane_name)
d5db4077 1950 ? "" : (char *) SDATA (pane_name));
4dedbfe0
PR
1951 /* If there is just one top-level pane, put all its items directly
1952 under the top-level menu. */
1953 if (menu_items_n_panes == 1)
1954 pane_string = "";
1955
1956 /* If the pane has a meaningful name,
1957 make the pane a top-level menu item
1958 with its items as a submenu beneath it. */
1959 if (strcmp (pane_string, ""))
1960 {
f7fab165 1961 wv = xmalloc_widget_value ();
4dedbfe0
PR
1962 if (save_wv)
1963 save_wv->next = wv;
1964 else
1965 first_wv->contents = wv;
2bf436c3
JD
1966 wv->lname = pane_name;
1967 /* Set value to 1 so update_submenu_strings can handle '@' */
1968 wv->value = (char *)1;
4dedbfe0 1969 wv->enabled = 1;
3427a3db 1970 wv->button_type = BUTTON_TYPE_NONE;
0b1e6b54 1971 wv->help = Qnil;
e33c22a5 1972 save_wv = wv;
4dedbfe0 1973 }
e33c22a5
RS
1974 else
1975 save_wv = first_wv;
1976
4dedbfe0
PR
1977 prev_wv = 0;
1978 i += MENU_ITEMS_PANE_LENGTH;
1979 }
1980 else
1981 {
1982 /* Create a new item within current pane. */
3427a3db 1983 Lisp_Object item_name, enable, descrip, def, type, selected;
850df50b 1984 Lisp_Object help;
177c0ea7 1985
1fc4d463
RS
1986 /* All items should be contained in panes. */
1987 if (panes_seen == 0)
1988 abort ();
1989
4c329aa8
GM
1990 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1991 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1992 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1993 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1994 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1995 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1996 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
3427a3db 1997
703dc2a8 1998#ifndef HAVE_MULTILINGUAL_MENU
3427a3db 1999 if (STRING_MULTIBYTE (item_name))
4c329aa8 2000 {
646f98ec 2001 item_name = ENCODE_MENU_STRING (item_name);
4c329aa8
GM
2002 AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
2003 }
177c0ea7 2004
3427a3db 2005 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
4c329aa8 2006 {
646f98ec 2007 descrip = ENCODE_MENU_STRING (descrip);
4c329aa8
GM
2008 AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
2009 }
2010#endif /* not HAVE_MULTILINGUAL_MENU */
3427a3db 2011
f7fab165 2012 wv = xmalloc_widget_value ();
177c0ea7 2013 if (prev_wv)
4dedbfe0 2014 prev_wv->next = wv;
71dca3e3 2015 else
4dedbfe0 2016 save_wv->contents = wv;
71dca3e3 2017
2bf436c3 2018 wv->lname = item_name;
4dedbfe0 2019 if (!NILP (descrip))
2bf436c3 2020 wv->lkey = descrip;
4dedbfe0 2021 wv->value = 0;
01d5e892
RS
2022 /* The EMACS_INT cast avoids a warning. There's no problem
2023 as long as pointers have enough bits to hold small integers. */
2024 wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0);
4dedbfe0 2025 wv->enabled = !NILP (enable);
177c0ea7 2026
3427a3db
GM
2027 if (NILP (type))
2028 wv->button_type = BUTTON_TYPE_NONE;
2029 else if (EQ (type, QCradio))
2030 wv->button_type = BUTTON_TYPE_RADIO;
2031 else if (EQ (type, QCtoggle))
2032 wv->button_type = BUTTON_TYPE_TOGGLE;
2033 else
2034 abort ();
2035
2036 wv->selected = !NILP (selected);
0b1f4572
RS
2037 if (! STRINGP (help))
2038 help = Qnil;
2039
2040 wv->help = help;
cc45fb40 2041
4dedbfe0
PR
2042 prev_wv = wv;
2043
2044 i += MENU_ITEMS_ITEM_LENGTH;
2045 }
2046 }
2047
71dca3e3
RS
2048 /* If we have just one "menu item"
2049 that was originally a button, return it by itself. */
2050 if (top_level_items && first_wv->contents && first_wv->contents->next == 0)
2051 {
2052 wv = first_wv->contents;
2053 free_widget_value (first_wv);
2054 return wv;
2055 }
2056
4dedbfe0
PR
2057 return first_wv;
2058}
2bf436c3 2059
5560d460 2060/* Walk through the widget_value tree starting at FIRST_WV and update
2bf436c3
JD
2061 the char * pointers from the corresponding lisp values.
2062 We do this after building the whole tree, since GC may happen while the
2063 tree is constructed, and small strings are relocated. So we must wait
2064 until no GC can happen before storing pointers into lisp values. */
2065static void
2066update_submenu_strings (first_wv)
2067 widget_value *first_wv;
2068{
2069 widget_value *wv;
2070
2071 for (wv = first_wv; wv; wv = wv->next)
2072 {
1806714a 2073 if (STRINGP (wv->lname))
2bf436c3 2074 {
b79c8219 2075 wv->name = (char *) SDATA (wv->lname);
2bf436c3
JD
2076
2077 /* Ignore the @ that means "separate pane".
2078 This is a kludge, but this isn't worth more time. */
2079 if (wv->value == (char *)1)
2080 {
2081 if (wv->name[0] == '@')
2082 wv->name++;
2083 wv->value = 0;
2084 }
2085 }
2086
1806714a 2087 if (STRINGP (wv->lkey))
b79c8219 2088 wv->key = (char *) SDATA (wv->lkey);
2bf436c3
JD
2089
2090 if (wv->contents)
2091 update_submenu_strings (wv->contents);
2092 }
2093}
2094
f61a541b
GM
2095\f
2096/* Recompute all the widgets of frame F, when the menu bar has been
2097 changed. Value is non-zero if widgets were updated. */
2098
2099static int
6af6cbb5 2100update_frame_menubar (f)
18686d47
RS
2101 FRAME_PTR f;
2102{
488dd4c4
JD
2103#ifdef USE_GTK
2104 return xg_update_frame_menubar (f);
2105#else
62af879c 2106 struct x_output *x;
cffa74ea 2107 int columns, rows;
177c0ea7 2108
62af879c
KL
2109 if (! FRAME_X_P (f))
2110 abort ();
2111
2112 x = f->output_data.x;
2113
f61a541b
GM
2114 if (!x->menubar_widget || XtIsManaged (x->menubar_widget))
2115 return 0;
18686d47
RS
2116
2117 BLOCK_INPUT;
f61a541b
GM
2118 /* Save the size of the frame because the pane widget doesn't accept
2119 to resize itself. So force it. */
9882535b
KS
2120 columns = FRAME_COLS (f);
2121 rows = FRAME_LINES (f);
cffa74ea 2122
f61a541b
GM
2123 /* Do the voodoo which means "I'm changing lots of things, don't try
2124 to refigure sizes until I'm done." */
4dedbfe0 2125 lw_refigure_widget (x->column_widget, False);
cffa74ea 2126
f61a541b
GM
2127 /* The order in which children are managed is the top to bottom
2128 order in which they are displayed in the paned window. First,
2129 remove the text-area widget. */
18686d47
RS
2130 XtUnmanageChild (x->edit_widget);
2131
f61a541b
GM
2132 /* Remove the menubar that is there now, and put up the menubar that
2133 should be there. */
2134 XtManageChild (x->menubar_widget);
2135 XtMapWidget (x->menubar_widget);
2136 XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, NULL);
18686d47 2137
c98fcf4b 2138 /* Re-manage the text-area widget, and then thrash the sizes. */
18686d47 2139 XtManageChild (x->edit_widget);
4dedbfe0 2140 lw_refigure_widget (x->column_widget, True);
cffa74ea
FP
2141
2142 /* Force the pane widget to resize itself with the right values. */
2143 EmacsFrameSetCharSize (x->edit_widget, columns, rows);
18686d47 2144 UNBLOCK_INPUT;
488dd4c4 2145#endif
f61a541b 2146 return 1;
18686d47
RS
2147}
2148
4bcdbab1
KH
2149/* Set the contents of the menubar widgets of frame F.
2150 The argument FIRST_TIME is currently ignored;
2151 it is set the first time this is called, from initialize_frame_menubar. */
2152
18686d47 2153void
88766961 2154set_frame_menubar (f, first_time, deep_p)
18686d47 2155 FRAME_PTR f;
706aa2f2 2156 int first_time;
88766961 2157 int deep_p;
18686d47 2158{
62af879c 2159 xt_or_gtk_widget menubar_widget;
488dd4c4
JD
2160#ifdef USE_X_TOOLKIT
2161 LWLIB_ID id;
2162#endif
faa935b6 2163 Lisp_Object items;
4d19cb8e 2164 widget_value *wv, *first_wv, *prev_wv = 0;
1806714a 2165 int i, last_i = 0;
ced89c24 2166 int *submenu_start, *submenu_end;
37dc84ff 2167 int *submenu_top_level_items, *submenu_n_panes;
cc45fb40 2168
62af879c
KL
2169 if (! FRAME_X_P (f))
2170 abort ();
2171
2172 menubar_widget = f->output_data.x->menubar_widget;
18686d47 2173
bfc524bc
RS
2174 XSETFRAME (Vmenu_updating_frame, f);
2175
488dd4c4 2176#ifdef USE_X_TOOLKIT
7556890b
RS
2177 if (f->output_data.x->id == 0)
2178 f->output_data.x->id = next_menubar_widget_id++;
2179 id = f->output_data.x->id;
488dd4c4 2180#endif
177c0ea7 2181
88766961
RS
2182 if (! menubar_widget)
2183 deep_p = 1;
745c34fb 2184 else if (pending_menu_activation && !deep_p)
a9be6839
RS
2185 deep_p = 1;
2186 /* Make the first call for any given frame always go deep. */
2187 else if (!f->output_data.x->saved_menu_event && !deep_p)
745c34fb
RS
2188 {
2189 deep_p = 1;
a9be6839
RS
2190 f->output_data.x->saved_menu_event = (XEvent*)xmalloc (sizeof (XEvent));
2191 f->output_data.x->saved_menu_event->type = 0;
745c34fb 2192 }
18686d47 2193
6b61353c
KH
2194#ifdef USE_GTK
2195 /* If we have detached menus, we must update deep so detached menus
2196 also gets updated. */
2197 deep_p = deep_p || xg_have_tear_offs ();
2198#endif
2199
88766961 2200 if (deep_p)
18686d47 2201 {
88766961
RS
2202 /* Make a widget-value tree representing the entire menu trees. */
2203
2204 struct buffer *prev = current_buffer;
2205 Lisp_Object buffer;
aed13378 2206 int specpdl_count = SPECPDL_INDEX ();
88766961
RS
2207 int previous_menu_items_used = f->menu_bar_items_used;
2208 Lisp_Object *previous_items
2209 = (Lisp_Object *) alloca (previous_menu_items_used
2210 * sizeof (Lisp_Object));
2211
0b1cf399
RS
2212 /* If we are making a new widget, its contents are empty,
2213 do always reinitialize them. */
2214 if (! menubar_widget)
2215 previous_menu_items_used = 0;
2216
88766961
RS
2217 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
2218 specbind (Qinhibit_quit, Qt);
2219 /* Don't let the debugger step into this code
2220 because it is not reentrant. */
2221 specbind (Qdebug_on_next_call, Qnil);
2222
89f2614d 2223 record_unwind_save_match_data ();
88766961
RS
2224 if (NILP (Voverriding_local_map_menu_flag))
2225 {
2226 specbind (Qoverriding_terminal_local_map, Qnil);
2227 specbind (Qoverriding_local_map, Qnil);
2228 }
18686d47 2229
88766961 2230 set_buffer_internal_1 (XBUFFER (buffer));
18686d47 2231
88766961 2232 /* Run the Lucid hook. */
950eaee7 2233 safe_run_hooks (Qactivate_menubar_hook);
177c0ea7 2234
88766961
RS
2235 /* If it has changed current-menubar from previous value,
2236 really recompute the menubar from the value. */
2237 if (! NILP (Vlucid_menu_bar_dirty_flag))
2238 call0 (Qrecompute_lucid_menubar);
a57634d4 2239 safe_run_hooks (Qmenu_bar_update_hook);
88766961 2240 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
18686d47 2241
88766961 2242 items = FRAME_MENU_BAR_ITEMS (f);
8d8a3494 2243
88766961 2244 /* Save the frame's previous menu bar contents data. */
86c04183
GM
2245 if (previous_menu_items_used)
2246 bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
2247 previous_menu_items_used * sizeof (Lisp_Object));
8d8a3494 2248
ced89c24
RS
2249 /* Fill in menu_items with the current menu bar contents.
2250 This can evaluate Lisp code. */
1fc4d463
RS
2251 save_menu_items ();
2252
88766961 2253 menu_items = f->menu_bar_vector;
86c04183 2254 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
ced89c24
RS
2255 submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
2256 submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
37dc84ff 2257 submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
ced89c24
RS
2258 submenu_top_level_items
2259 = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
88766961 2260 init_menu_items ();
f04366cb 2261 for (i = 0; i < XVECTOR (items)->size; i += 4)
88766961
RS
2262 {
2263 Lisp_Object key, string, maps;
2264
ced89c24
RS
2265 last_i = i;
2266
88766961
RS
2267 key = XVECTOR (items)->contents[i];
2268 string = XVECTOR (items)->contents[i + 1];
2269 maps = XVECTOR (items)->contents[i + 2];
2270 if (NILP (string))
2271 break;
2272
ced89c24
RS
2273 submenu_start[i] = menu_items_used;
2274
2275 menu_items_n_panes = 0;
2276 submenu_top_level_items[i]
2277 = parse_single_submenu (key, string, maps);
37dc84ff 2278 submenu_n_panes[i] = menu_items_n_panes;
ced89c24
RS
2279
2280 submenu_end[i] = menu_items_used;
2281 }
2282
2283 finish_menu_items ();
2284
2285 /* Convert menu_items into widget_value trees
2286 to display the menu. This cannot evaluate Lisp code. */
2287
2288 wv = xmalloc_widget_value ();
2289 wv->name = "menubar";
2290 wv->value = 0;
2291 wv->enabled = 1;
2292 wv->button_type = BUTTON_TYPE_NONE;
2293 wv->help = Qnil;
2294 first_wv = wv;
2295
2296 for (i = 0; i < last_i; i += 4)
2297 {
37dc84ff 2298 menu_items_n_panes = submenu_n_panes[i];
ced89c24
RS
2299 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
2300 submenu_top_level_items[i]);
177c0ea7 2301 if (prev_wv)
88766961
RS
2302 prev_wv->next = wv;
2303 else
2304 first_wv->contents = wv;
2305 /* Don't set wv->name here; GC during the loop might relocate it. */
2306 wv->enabled = 1;
3427a3db 2307 wv->button_type = BUTTON_TYPE_NONE;
88766961
RS
2308 prev_wv = wv;
2309 }
2310
88766961 2311 set_buffer_internal_1 (prev);
8d8a3494 2312
88766961
RS
2313 /* If there has been no change in the Lisp-level contents
2314 of the menu bar, skip redisplaying it. Just exit. */
2315
510d5bf6 2316 /* Compare the new menu items with the ones computed last time. */
88766961
RS
2317 for (i = 0; i < previous_menu_items_used; i++)
2318 if (menu_items_used == i
99d3fac7 2319 || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
88766961 2320 break;
62555c22 2321 if (i == menu_items_used && i == previous_menu_items_used && i != 0)
88766961 2322 {
510d5bf6
RS
2323 /* The menu items have not changed. Don't bother updating
2324 the menus in any form, since it would be a no-op. */
88766961 2325 free_menubar_widget_value_tree (first_wv);
86fad4ec 2326 discard_menu_items ();
1fc4d463 2327 unbind_to (specpdl_count, Qnil);
88766961
RS
2328 return;
2329 }
2330
510d5bf6 2331 /* The menu items are different, so store them in the frame. */
1fc4d463
RS
2332 f->menu_bar_vector = menu_items;
2333 f->menu_bar_items_used = menu_items_used;
2334
510d5bf6
RS
2335 /* This calls restore_menu_items to restore menu_items, etc.,
2336 as they were outside. */
1fc4d463
RS
2337 unbind_to (specpdl_count, Qnil);
2338
88766961
RS
2339 /* Now GC cannot happen during the lifetime of the widget_value,
2340 so it's safe to store data from a Lisp_String. */
2341 wv = first_wv->contents;
f04366cb 2342 for (i = 0; i < XVECTOR (items)->size; i += 4)
88766961
RS
2343 {
2344 Lisp_Object string;
2345 string = XVECTOR (items)->contents[i + 1];
2346 if (NILP (string))
2bf436c3 2347 break;
ec867797 2348 wv->name = (char *) SDATA (string);
2bf436c3
JD
2349 update_submenu_strings (wv->contents);
2350 wv = wv->next;
88766961
RS
2351 }
2352
4d19cb8e 2353 }
88766961
RS
2354 else
2355 {
2356 /* Make a widget-value tree containing
2357 just the top level menu bar strings. */
4d19cb8e 2358
ced89c24
RS
2359 wv = xmalloc_widget_value ();
2360 wv->name = "menubar";
2361 wv->value = 0;
2362 wv->enabled = 1;
2363 wv->button_type = BUTTON_TYPE_NONE;
2364 wv->help = Qnil;
2365 first_wv = wv;
2366
88766961 2367 items = FRAME_MENU_BAR_ITEMS (f);
f04366cb 2368 for (i = 0; i < XVECTOR (items)->size; i += 4)
88766961
RS
2369 {
2370 Lisp_Object string;
2371
2372 string = XVECTOR (items)->contents[i + 1];
2373 if (NILP (string))
2374 break;
2375
f7fab165 2376 wv = xmalloc_widget_value ();
d5db4077 2377 wv->name = (char *) SDATA (string);
88766961
RS
2378 wv->value = 0;
2379 wv->enabled = 1;
3427a3db 2380 wv->button_type = BUTTON_TYPE_NONE;
27ad7b52 2381 wv->help = Qnil;
fe8fa62f
RS
2382 /* This prevents lwlib from assuming this
2383 menu item is really supposed to be empty. */
2384 /* The EMACS_INT cast avoids a warning.
2385 This value just has to be different from small integers. */
2386 wv->call_data = (void *) (EMACS_INT) (-1);
88766961 2387
177c0ea7 2388 if (prev_wv)
88766961
RS
2389 prev_wv->next = wv;
2390 else
2391 first_wv->contents = wv;
2392 prev_wv = wv;
2393 }
62555c22
RS
2394
2395 /* Forget what we thought we knew about what is in the
2396 detailed contents of the menu bar menus.
2397 Changing the top level always destroys the contents. */
2398 f->menu_bar_items_used = 0;
88766961 2399 }
4dedbfe0 2400
88766961 2401 /* Create or update the menu bar widget. */
aa669def
RS
2402
2403 BLOCK_INPUT;
2404
488dd4c4
JD
2405#ifdef USE_GTK
2406 xg_crazy_callback_abort = 1;
2407 if (menubar_widget)
2408 {
6b61353c 2409 /* The fourth arg is DEEP_P, which says to consider the entire
488dd4c4
JD
2410 menu trees we supply, rather than just the menu bar item names. */
2411 xg_modify_menubar_widgets (menubar_widget,
2412 f,
2413 first_wv,
2414 deep_p,
2415 G_CALLBACK (menubar_selection_callback),
2416 G_CALLBACK (popup_deactivate_callback),
2417 G_CALLBACK (menu_highlight_callback));
2418 }
2419 else
2420 {
2421 GtkWidget *wvbox = f->output_data.x->vbox_widget;
177c0ea7 2422
488dd4c4 2423 menubar_widget
177c0ea7 2424 = xg_create_widget ("menubar", "menubar", f, first_wv,
488dd4c4
JD
2425 G_CALLBACK (menubar_selection_callback),
2426 G_CALLBACK (popup_deactivate_callback),
2427 G_CALLBACK (menu_highlight_callback));
2428
2429 f->output_data.x->menubar_widget = menubar_widget;
2430 }
2431
177c0ea7 2432
488dd4c4 2433#else /* not USE_GTK */
18686d47 2434 if (menubar_widget)
4dedbfe0
PR
2435 {
2436 /* Disable resizing (done for Motif!) */
7556890b 2437 lw_allow_resizing (f->output_data.x->widget, False);
4dedbfe0
PR
2438
2439 /* The third arg is DEEP_P, which says to consider the entire
2440 menu trees we supply, rather than just the menu bar item names. */
88766961 2441 lw_modify_all_widgets (id, first_wv, deep_p);
4dedbfe0 2442
c98fcf4b 2443 /* Re-enable the edit widget to resize. */
7556890b 2444 lw_allow_resizing (f->output_data.x->widget, True);
4dedbfe0 2445 }
18686d47
RS
2446 else
2447 {
9f6fcdc5
JD
2448 char menuOverride[] = "Ctrl<KeyPress>g: MenuGadgetEscape()";
2449 XtTranslations override = XtParseTranslationTable (menuOverride);
2450
177c0ea7 2451 menubar_widget = lw_create_widget ("menubar", "menubar", id, first_wv,
7556890b 2452 f->output_data.x->column_widget,
4dedbfe0
PR
2453 0,
2454 popup_activate_callback,
2455 menubar_selection_callback,
850df50b
GM
2456 popup_deactivate_callback,
2457 menu_highlight_callback);
7556890b 2458 f->output_data.x->menubar_widget = menubar_widget;
9f6fcdc5
JD
2459
2460 /* Make menu pop down on C-g. */
2461 XtOverrideTranslations (menubar_widget, override);
18686d47 2462 }
1d1c1567
KH
2463
2464 {
177c0ea7 2465 int menubar_size
7556890b
RS
2466 = (f->output_data.x->menubar_widget
2467 ? (f->output_data.x->menubar_widget->core.height
2468 + f->output_data.x->menubar_widget->core.border_width)
1d1c1567
KH
2469 : 0);
2470
f42aa681
RS
2471#if 0 /* Experimentally, we now get the right results
2472 for -geometry -0-0 without this. 24 Aug 96, rms. */
5ee80bd3 2473#ifdef USE_LUCID
1d1c1567
KH
2474 if (FRAME_EXTERNAL_MENU_BAR (f))
2475 {
2476 Dimension ibw = 0;
7556890b 2477 XtVaGetValues (f->output_data.x->column_widget,
1d1c1567
KH
2478 XtNinternalBorderWidth, &ibw, NULL);
2479 menubar_size += ibw;
2480 }
5ee80bd3 2481#endif /* USE_LUCID */
f42aa681 2482#endif /* 0 */
1d1c1567 2483
7556890b 2484 f->output_data.x->menubar_height = menubar_size;
1d1c1567 2485 }
488dd4c4 2486#endif /* not USE_GTK */
177c0ea7 2487
18686d47 2488 free_menubar_widget_value_tree (first_wv);
364cd450 2489 update_frame_menubar (f);
18686d47 2490
488dd4c4
JD
2491#ifdef USE_GTK
2492 xg_crazy_callback_abort = 0;
2493#endif
2494
18686d47
RS
2495 UNBLOCK_INPUT;
2496}
85f487d1 2497
8e6208c5 2498/* Called from Fx_create_frame to create the initial menubar of a frame
4dedbfe0
PR
2499 before it is mapped, so that the window is mapped with the menubar already
2500 there instead of us tacking it on later and thrashing the window after it
2501 is visible. */
2502
2503void
2504initialize_frame_menubar (f)
2505 FRAME_PTR f;
2506{
2507 /* This function is called before the first chance to redisplay
2508 the frame. It has to be, so the frame will have the right size. */
2509 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
88766961 2510 set_frame_menubar (f, 1, 1);
4dedbfe0
PR
2511}
2512
aba25348 2513
4dedbfe0 2514/* Get rid of the menu bar of frame F, and free its storage.
488dd4c4
JD
2515 This is used when deleting a frame, and when turning off the menu bar.
2516 For GTK this function is in gtkutil.c. */
4dedbfe0 2517
488dd4c4 2518#ifndef USE_GTK
85f487d1
FP
2519void
2520free_frame_menubar (f)
2521 FRAME_PTR f;
2522{
2523 Widget menubar_widget;
85f487d1 2524
62af879c
KL
2525 if (! FRAME_X_P (f))
2526 abort ();
2527
7556890b 2528 menubar_widget = f->output_data.x->menubar_widget;
a45bad2a
RS
2529
2530 f->output_data.x->menubar_height = 0;
177c0ea7 2531
85f487d1
FP
2532 if (menubar_widget)
2533 {
aba25348
GM
2534#ifdef USE_MOTIF
2535 /* Removing the menu bar magically changes the shell widget's x
2536 and y position of (0, 0) which, when the menu bar is turned
2537 on again, leads to pull-down menuss appearing in strange
2538 positions near the upper-left corner of the display. This
2539 happens only with some window managers like twm and ctwm,
2540 but not with other like Motif's mwm or kwm, because the
2541 latter generate ConfigureNotify events when the menu bar
2542 is switched off, which fixes the shell position. */
2543 Position x0, y0, x1, y1;
2544#endif
177c0ea7 2545
85f487d1 2546 BLOCK_INPUT;
aba25348
GM
2547
2548#ifdef USE_MOTIF
ae556422
GM
2549 if (f->output_data.x->widget)
2550 XtVaGetValues (f->output_data.x->widget, XtNx, &x0, XtNy, &y0, NULL);
aba25348 2551#endif
177c0ea7 2552
7556890b 2553 lw_destroy_all_widgets ((LWLIB_ID) f->output_data.x->id);
0c05dcd3 2554 f->output_data.x->menubar_widget = NULL;
aba25348
GM
2555
2556#ifdef USE_MOTIF
ae556422
GM
2557 if (f->output_data.x->widget)
2558 {
2559 XtVaGetValues (f->output_data.x->widget, XtNx, &x1, XtNy, &y1, NULL);
2560 if (x1 == 0 && y1 == 0)
2561 XtVaSetValues (f->output_data.x->widget, XtNx, x0, XtNy, y0, NULL);
2562 }
aba25348 2563#endif
177c0ea7 2564
85f487d1
FP
2565 UNBLOCK_INPUT;
2566 }
2567}
488dd4c4 2568#endif /* not USE_GTK */
78589e07 2569
488dd4c4 2570#endif /* USE_X_TOOLKIT || USE_GTK */
78589e07
RS
2571\f
2572/* xmenu_show actually displays a menu using the panes and items in menu_items
2573 and returns the value selected from it.
2574 There are two versions of xmenu_show, one for Xt and one for Xlib.
2575 Both assume input is blocked by the caller. */
2576
2577/* F is the frame the menu is for.
2578 X and Y are the frame-relative specified position,
2579 relative to the inside upper left corner of the frame F.
c8b5aa3d 2580 FOR_CLICK is nonzero if this menu was invoked for a mouse click.
78589e07
RS
2581 KEYMAPS is 1 if this menu was specified with keymaps;
2582 in that case, we return a list containing the chosen item's value
2583 and perhaps also the pane's prefix.
2584 TITLE is the specified menu title.
2585 ERROR is a place to store an error message string in case of failure.
2586 (We return nil on failure, but the value doesn't actually matter.) */
18686d47 2587
488dd4c4
JD
2588#if defined (USE_X_TOOLKIT) || defined (USE_GTK)
2589
2590/* The item selected in the popup menu. */
2591static Lisp_Object *volatile menu_item_selection;
2592
2593#ifdef USE_GTK
2594
2595/* Used when position a popup menu. See menu_position_func and
2596 create_and_show_popup_menu below. */
2597struct next_popup_x_y
2598{
7b76ca1c 2599 FRAME_PTR f;
488dd4c4
JD
2600 int x;
2601 int y;
2602};
2603
2604/* The menu position function to use if we are not putting a popup
2605 menu where the pointer is.
2606 MENU is the menu to pop up.
2607 X and Y shall on exit contain x/y where the menu shall pop up.
2608 PUSH_IN is not documented in the GTK manual.
2609 USER_DATA is any data passed in when calling gtk_menu_popup.
2610 Here it points to a struct next_popup_x_y where the coordinates
7b76ca1c 2611 to store in *X and *Y are as well as the frame for the popup.
488dd4c4
JD
2612
2613 Here only X and Y are used. */
2614static void
2615menu_position_func (menu, x, y, push_in, user_data)
2616 GtkMenu *menu;
2617 gint *x;
2618 gint *y;
2619 gboolean *push_in;
2620 gpointer user_data;
2621{
7b76ca1c
JD
2622 struct next_popup_x_y* data = (struct next_popup_x_y*)user_data;
2623 GtkRequisition req;
2624 int disp_width = FRAME_X_DISPLAY_INFO (data->f)->width;
2625 int disp_height = FRAME_X_DISPLAY_INFO (data->f)->height;
b9de078a 2626
7b76ca1c
JD
2627 *x = data->x;
2628 *y = data->y;
2629
2630 /* Check if there is room for the menu. If not, adjust x/y so that
2631 the menu is fully visible. */
2632 gtk_widget_size_request (GTK_WIDGET (menu), &req);
2633 if (data->x + req.width > disp_width)
2634 *x -= data->x + req.width - disp_width;
2635 if (data->y + req.height > disp_height)
2636 *y -= data->y + req.height - disp_height;
488dd4c4
JD
2637}
2638
2639static void
2640popup_selection_callback (widget, client_data)
2641 GtkWidget *widget;
2642 gpointer client_data;
2643{
2644 xg_menu_item_cb_data *cb_data = (xg_menu_item_cb_data*) client_data;
2645
2646 if (xg_crazy_callback_abort) return;
2647 if (cb_data) menu_item_selection = (Lisp_Object *) cb_data->call_data;
2648}
2649
af89e871 2650static Lisp_Object
a130b901
JD
2651pop_down_menu (arg)
2652 Lisp_Object arg;
af89e871 2653{
a130b901
JD
2654 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
2655
af89e871 2656 popup_activated_flag = 0;
a130b901
JD
2657 BLOCK_INPUT;
2658 gtk_widget_destroy (GTK_WIDGET (p->pointer));
2659 UNBLOCK_INPUT;
af89e871
JD
2660 return Qnil;
2661}
2662
488dd4c4
JD
2663/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
2664 menu pops down.
2665 menu_item_selection will be set to the selection. */
2666static void
2667create_and_show_popup_menu (f, first_wv, x, y, for_click)
2668 FRAME_PTR f;
2669 widget_value *first_wv;
2670 int x;
2671 int y;
2672 int for_click;
2673{
2674 int i;
2675 GtkWidget *menu;
2676 GtkMenuPositionFunc pos_func = 0; /* Pop up at pointer. */
2677 struct next_popup_x_y popup_x_y;
af89e871 2678 int specpdl_count = SPECPDL_INDEX ();
488dd4c4 2679
62af879c
KL
2680 if (! FRAME_X_P (f))
2681 abort ();
2682
488dd4c4
JD
2683 xg_crazy_callback_abort = 1;
2684 menu = xg_create_widget ("popup", first_wv->name, f, first_wv,
2685 G_CALLBACK (popup_selection_callback),
2686 G_CALLBACK (popup_deactivate_callback),
2687 G_CALLBACK (menu_highlight_callback));
2688 xg_crazy_callback_abort = 0;
177c0ea7 2689
488dd4c4
JD
2690 if (! for_click)
2691 {
2692 /* Not invoked by a click. pop up at x/y. */
2693 pos_func = menu_position_func;
2694
2695 /* Adjust coordinates to be root-window-relative. */
9882535b
KS
2696 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
2697 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
488dd4c4
JD
2698
2699 popup_x_y.x = x;
2700 popup_x_y.y = y;
7b76ca1c 2701 popup_x_y.f = f;
488dd4c4 2702
9b85e63d
JD
2703 i = 0; /* gtk_menu_popup needs this to be 0 for a non-button popup. */
2704 }
2705 else
2706 {
2707 for (i = 0; i < 5; i++)
2708 if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
2709 break;
2710 }
59cfb104 2711
488dd4c4
JD
2712 /* Display the menu. */
2713 gtk_widget_show_all (menu);
2714 gtk_menu_popup (GTK_MENU (menu), 0, 0, pos_func, &popup_x_y, i, 0);
177c0ea7 2715
a130b901 2716 record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
af89e871 2717
ff18668f
JD
2718 if (GTK_WIDGET_MAPPED (menu))
2719 {
2720 /* Set this to one. popup_widget_loop increases it by one, so it becomes
2721 two. show_help_echo uses this to detect popup menus. */
2722 popup_activated_flag = 1;
2723 /* Process events that apply to the menu. */
2724 popup_widget_loop (1, menu);
2725 }
488dd4c4 2726
af89e871 2727 unbind_to (specpdl_count, Qnil);
177c0ea7 2728
488dd4c4
JD
2729 /* Must reset this manually because the button release event is not passed
2730 to Emacs event loop. */
2731 FRAME_X_DISPLAY_INFO (f)->grabbed = 0;
2732}
2733
2734#else /* not USE_GTK */
18686d47 2735
8ed87156 2736/* We need a unique id for each widget handled by the Lucid Widget
cc17e9bf
KH
2737 library.
2738
2739 For the main windows, and popup menus, we use this counter,
88766961 2740 which we increment each time after use. This starts from 1<<16.
cc17e9bf 2741
88766961
RS
2742 For menu bars, we use numbers starting at 0, counted in
2743 next_menubar_widget_id. */
8ed87156 2744LWLIB_ID widget_id_tick;
165e1749 2745
4dedbfe0
PR
2746static void
2747popup_selection_callback (widget, id, client_data)
2748 Widget widget;
2749 LWLIB_ID id;
2750 XtPointer client_data;
2751{
2752 menu_item_selection = (Lisp_Object *) client_data;
2753}
2754
af89e871
JD
2755/* ARG is the LWLIB ID of the dialog box, represented
2756 as a Lisp object as (HIGHPART . LOWPART). */
2757
2758static Lisp_Object
2759pop_down_menu (arg)
2760 Lisp_Object arg;
2761{
2762 LWLIB_ID id = (XINT (XCAR (arg)) << 4 * sizeof (LWLIB_ID)
2763 | XINT (XCDR (arg)));
2764
2765 BLOCK_INPUT;
2766 lw_destroy_all_widgets (id);
2767 UNBLOCK_INPUT;
2768 popup_activated_flag = 0;
2769
2770 return Qnil;
2771}
2772
488dd4c4
JD
2773/* Pop up the menu for frame F defined by FIRST_WV at X/Y and loop until the
2774 menu pops down.
2775 menu_item_selection will be set to the selection. */
2776static void
2777create_and_show_popup_menu (f, first_wv, x, y, for_click)
2778 FRAME_PTR f;
2779 widget_value *first_wv;
2780 int x;
2781 int y;
2782 int for_click;
2783{
2784 int i;
2785 Arg av[2];
2786 int ac = 0;
2787 XButtonPressedEvent dummy;
2788 LWLIB_ID menu_id;
2789 Widget menu;
488dd4c4 2790
62af879c
KL
2791 if (! FRAME_X_P (f))
2792 abort ();
2793
488dd4c4
JD
2794 menu_id = widget_id_tick++;
2795 menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
2796 f->output_data.x->widget, 1, 0,
2797 popup_selection_callback,
2798 popup_deactivate_callback,
2799 menu_highlight_callback);
2800
2801 dummy.type = ButtonPress;
2802 dummy.serial = 0;
2803 dummy.send_event = 0;
2804 dummy.display = FRAME_X_DISPLAY (f);
2805 dummy.time = CurrentTime;
2806 dummy.root = FRAME_X_DISPLAY_INFO (f)->root_window;
2807 dummy.window = dummy.root;
2808 dummy.subwindow = dummy.root;
2809 dummy.x = x;
2810 dummy.y = y;
2811
2812 /* Adjust coordinates to be root-window-relative. */
9882535b
KS
2813 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
2814 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
488dd4c4
JD
2815
2816 dummy.x_root = x;
2817 dummy.y_root = y;
2818
2819 dummy.state = 0;
2820 dummy.button = 0;
2821 for (i = 0; i < 5; i++)
2822 if (FRAME_X_DISPLAY_INFO (f)->grabbed & (1 << i))
2823 dummy.button = i;
2824
2825 /* Don't allow any geometry request from the user. */
2826 XtSetArg (av[ac], XtNgeometry, 0); ac++;
2827 XtSetValues (menu, av, ac);
2828
2829 /* Display the menu. */
2830 lw_popup_menu (menu, (XEvent *) &dummy);
2831 popup_activated_flag = 1;
98a20c65 2832 x_activate_timeout_atimer ();
59cfb104 2833
af89e871
JD
2834 {
2835 int fact = 4 * sizeof (LWLIB_ID);
2836 int specpdl_count = SPECPDL_INDEX ();
2837 record_unwind_protect (pop_down_menu,
2838 Fcons (make_number (menu_id >> (fact)),
2839 make_number (menu_id & ~(-1 << (fact)))));
488dd4c4 2840
af89e871 2841 /* Process events that apply to the menu. */
95bdef2e 2842 popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f), menu_id, 1);
488dd4c4 2843
af89e871
JD
2844 unbind_to (specpdl_count, Qnil);
2845 }
488dd4c4
JD
2846}
2847
2848#endif /* not USE_GTK */
2849
78589e07 2850static Lisp_Object
673a6211 2851xmenu_show (f, x, y, for_click, keymaps, title, error)
18686d47 2852 FRAME_PTR f;
18686d47
RS
2853 int x;
2854 int y;
9685a93f 2855 int for_click;
78589e07
RS
2856 int keymaps;
2857 Lisp_Object title;
2858 char **error;
18686d47 2859{
78589e07 2860 int i;
78589e07 2861 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
101bb4a5
RS
2862 widget_value **submenu_stack
2863 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
2864 Lisp_Object *subprefix_stack
2865 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
2866 int submenu_depth = 0;
4e8d3549 2867
78c8278d
RS
2868 int first_pane;
2869
62af879c
KL
2870 if (! FRAME_X_P (f))
2871 abort ();
2872
78589e07
RS
2873 *error = NULL;
2874
742f715d
KH
2875 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
2876 {
2877 *error = "Empty menu";
2878 return Qnil;
2879 }
63c414df 2880
78589e07
RS
2881 /* Create a tree of widget_value objects
2882 representing the panes and their items. */
f7fab165 2883 wv = xmalloc_widget_value ();
78589e07
RS
2884 wv->name = "menu";
2885 wv->value = 0;
2886 wv->enabled = 1;
3427a3db 2887 wv->button_type = BUTTON_TYPE_NONE;
27ad7b52 2888 wv->help =Qnil;
78589e07 2889 first_wv = wv;
78c8278d 2890 first_pane = 1;
177c0ea7 2891
78589e07
RS
2892 /* Loop over all panes and items, filling in the tree. */
2893 i = 0;
2894 while (i < menu_items_used)
2895 {
101bb4a5
RS
2896 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
2897 {
2898 submenu_stack[submenu_depth++] = save_wv;
2899 save_wv = prev_wv;
2900 prev_wv = 0;
78c8278d 2901 first_pane = 1;
101bb4a5
RS
2902 i++;
2903 }
2904 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
2905 {
2906 prev_wv = save_wv;
2907 save_wv = submenu_stack[--submenu_depth];
78c8278d 2908 first_pane = 0;
101bb4a5
RS
2909 i++;
2910 }
2911 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
2912 && submenu_depth != 0)
2913 i += MENU_ITEMS_PANE_LENGTH;
fcaa7665
RS
2914 /* Ignore a nil in the item list.
2915 It's meaningful only for dialog boxes. */
2916 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2917 i += 1;
101bb4a5 2918 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
78589e07
RS
2919 {
2920 /* Create a new pane. */
2921 Lisp_Object pane_name, prefix;
2922 char *pane_string;
177c0ea7 2923
4c329aa8
GM
2924 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
2925 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
177c0ea7 2926
703dc2a8 2927#ifndef HAVE_MULTILINGUAL_MENU
4c329aa8
GM
2928 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
2929 {
8af9fa55 2930 pane_name = ENCODE_MENU_STRING (pane_name);
4c329aa8
GM
2931 AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
2932 }
703dc2a8 2933#endif
78589e07 2934 pane_string = (NILP (pane_name)
d5db4077 2935 ? "" : (char *) SDATA (pane_name));
101bb4a5 2936 /* If there is just one top-level pane, put all its items directly
78589e07
RS
2937 under the top-level menu. */
2938 if (menu_items_n_panes == 1)
2939 pane_string = "";
2940
2941 /* If the pane has a meaningful name,
2942 make the pane a top-level menu item
2943 with its items as a submenu beneath it. */
78c8278d 2944 if (!keymaps && strcmp (pane_string, ""))
78589e07 2945 {
f7fab165 2946 wv = xmalloc_widget_value ();
78589e07
RS
2947 if (save_wv)
2948 save_wv->next = wv;
2949 else
2950 first_wv->contents = wv;
2951 wv->name = pane_string;
2952 if (keymaps && !NILP (prefix))
2953 wv->name++;
2954 wv->value = 0;
2955 wv->enabled = 1;
3427a3db 2956 wv->button_type = BUTTON_TYPE_NONE;
27ad7b52 2957 wv->help = Qnil;
78c8278d
RS
2958 save_wv = wv;
2959 prev_wv = 0;
78589e07 2960 }
78c8278d
RS
2961 else if (first_pane)
2962 {
2963 save_wv = wv;
2964 prev_wv = 0;
2965 }
2966 first_pane = 0;
78589e07
RS
2967 i += MENU_ITEMS_PANE_LENGTH;
2968 }
2969 else
2970 {
2971 /* Create a new item within current pane. */
9cd50434 2972 Lisp_Object item_name, enable, descrip, def, type, selected, help;
4c329aa8
GM
2973 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
2974 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
2975 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
2976 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
2977 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
2978 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
2979 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
3427a3db 2980
703dc2a8 2981#ifndef HAVE_MULTILINGUAL_MENU
3427a3db 2982 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
4c329aa8 2983 {
646f98ec 2984 item_name = ENCODE_MENU_STRING (item_name);
4c329aa8
GM
2985 AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
2986 }
177c0ea7 2987
3427a3db 2988 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
4c329aa8 2989 {
646f98ec 2990 descrip = ENCODE_MENU_STRING (descrip);
4c329aa8
GM
2991 AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
2992 }
2993#endif /* not HAVE_MULTILINGUAL_MENU */
177c0ea7 2994
f7fab165 2995 wv = xmalloc_widget_value ();
177c0ea7 2996 if (prev_wv)
78589e07 2997 prev_wv->next = wv;
177c0ea7 2998 else
78589e07 2999 save_wv->contents = wv;
d5db4077 3000 wv->name = (char *) SDATA (item_name);
78589e07 3001 if (!NILP (descrip))
d5db4077 3002 wv->key = (char *) SDATA (descrip);
78589e07 3003 wv->value = 0;
a352a815
RS
3004 /* If this item has a null value,
3005 make the call_data null so that it won't display a box
3006 when the mouse is on it. */
3007 wv->call_data
3008 = (!NILP (def) ? (void *) &XVECTOR (menu_items)->contents[i] : 0);
78589e07 3009 wv->enabled = !NILP (enable);
3427a3db
GM
3010
3011 if (NILP (type))
3012 wv->button_type = BUTTON_TYPE_NONE;
3013 else if (EQ (type, QCtoggle))
3014 wv->button_type = BUTTON_TYPE_TOGGLE;
3015 else if (EQ (type, QCradio))
3016 wv->button_type = BUTTON_TYPE_RADIO;
3017 else
3018 abort ();
3019
3020 wv->selected = !NILP (selected);
cc45fb40 3021
0b1f4572
RS
3022 if (! STRINGP (help))
3023 help = Qnil;
3024
3025 wv->help = help;
cc45fb40 3026
78589e07
RS
3027 prev_wv = wv;
3028
3029 i += MENU_ITEMS_ITEM_LENGTH;
3030 }
3031 }
3032
c98fcf4b 3033 /* Deal with the title, if it is non-nil. */
4dedbfe0
PR
3034 if (!NILP (title))
3035 {
f7fab165
RS
3036 widget_value *wv_title = xmalloc_widget_value ();
3037 widget_value *wv_sep1 = xmalloc_widget_value ();
3038 widget_value *wv_sep2 = xmalloc_widget_value ();
4dedbfe0
PR
3039
3040 wv_sep2->name = "--";
3041 wv_sep2->next = first_wv->contents;
27ad7b52 3042 wv_sep2->help = Qnil;
4dedbfe0
PR
3043
3044 wv_sep1->name = "--";
3045 wv_sep1->next = wv_sep2;
27ad7b52 3046 wv_sep1->help = Qnil;
4dedbfe0 3047
703dc2a8
KH
3048#ifndef HAVE_MULTILINGUAL_MENU
3049 if (STRING_MULTIBYTE (title))
646f98ec 3050 title = ENCODE_MENU_STRING (title);
703dc2a8 3051#endif
177c0ea7 3052
d5db4077 3053 wv_title->name = (char *) SDATA (title);
cc45fb40 3054 wv_title->enabled = TRUE;
3427a3db 3055 wv_title->button_type = BUTTON_TYPE_NONE;
4dedbfe0 3056 wv_title->next = wv_sep1;
27ad7b52 3057 wv_title->help = Qnil;
4dedbfe0
PR
3058 first_wv->contents = wv_title;
3059 }
3060
78589e07
RS
3061 /* No selection has been chosen yet. */
3062 menu_item_selection = 0;
3063
488dd4c4
JD
3064 /* Actually create and show the menu until popped down. */
3065 create_and_show_popup_menu (f, first_wv, x, y, for_click);
18686d47 3066
488dd4c4
JD
3067 /* Free the widget_value objects we used to specify the contents. */
3068 free_menubar_widget_value_tree (first_wv);
18686d47 3069
78589e07
RS
3070 /* Find the selected item, and its pane, to return
3071 the proper value. */
3072 if (menu_item_selection != 0)
3073 {
c63f6952 3074 Lisp_Object prefix, entry;
78589e07 3075
6bbd7a29 3076 prefix = entry = Qnil;
78589e07
RS
3077 i = 0;
3078 while (i < menu_items_used)
3079 {
101bb4a5
RS
3080 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
3081 {
3082 subprefix_stack[submenu_depth++] = prefix;
3083 prefix = entry;
3084 i++;
3085 }
3086 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
3087 {
3088 prefix = subprefix_stack[--submenu_depth];
3089 i++;
3090 }
3091 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
78589e07
RS
3092 {
3093 prefix
3094 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
3095 i += MENU_ITEMS_PANE_LENGTH;
3096 }
d31d42cc
RS
3097 /* Ignore a nil in the item list.
3098 It's meaningful only for dialog boxes. */
3099 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
3100 i += 1;
78589e07
RS
3101 else
3102 {
3103 entry
3104 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
3105 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
3106 {
3107 if (keymaps != 0)
3108 {
101bb4a5
RS
3109 int j;
3110
78589e07
RS
3111 entry = Fcons (entry, Qnil);
3112 if (!NILP (prefix))
3113 entry = Fcons (prefix, entry);
101bb4a5 3114 for (j = submenu_depth - 1; j >= 0; j--)
e48087b7 3115 if (!NILP (subprefix_stack[j]))
5964e450 3116 entry = Fcons (subprefix_stack[j], entry);
78589e07
RS
3117 }
3118 return entry;
3119 }
3120 i += MENU_ITEMS_ITEM_LENGTH;
3121 }
3122 }
3123 }
be6ed24a
RS
3124 else if (!for_click)
3125 /* Make "Cancel" equivalent to C-g. */
3126 Fsignal (Qquit, Qnil);
78589e07
RS
3127
3128 return Qnil;
18686d47 3129}
4dedbfe0 3130\f
488dd4c4
JD
3131#ifdef USE_GTK
3132static void
3133dialog_selection_callback (widget, client_data)
3134 GtkWidget *widget;
3135 gpointer client_data;
3136{
3137 /* The EMACS_INT cast avoids a warning. There's no problem
3138 as long as pointers have enough bits to hold small integers. */
3139 if ((int) (EMACS_INT) client_data != -1)
3140 menu_item_selection = (Lisp_Object *) client_data;
3141
3142 popup_activated_flag = 0;
3143}
3144
3145/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
3146 dialog pops down.
3147 menu_item_selection will be set to the selection. */
3148static void
3149create_and_show_dialog (f, first_wv)
3150 FRAME_PTR f;
3151 widget_value *first_wv;
3152{
3153 GtkWidget *menu;
3154
62af879c
KL
3155 if (! FRAME_X_P (f))
3156 abort ();
3157
488dd4c4
JD
3158 menu = xg_create_widget ("dialog", first_wv->name, f, first_wv,
3159 G_CALLBACK (dialog_selection_callback),
3160 G_CALLBACK (popup_deactivate_callback),
3161 0);
3162
3163 if (menu)
3164 {
af89e871 3165 int specpdl_count = SPECPDL_INDEX ();
a130b901 3166 record_unwind_protect (pop_down_menu, make_save_value (menu, 0));
af89e871 3167
488dd4c4
JD
3168 /* Display the menu. */
3169 gtk_widget_show_all (menu);
3170
3171 /* Process events that apply to the menu. */
f1d1cd24 3172 popup_widget_loop (1, menu);
177c0ea7 3173
af89e871 3174 unbind_to (specpdl_count, Qnil);
488dd4c4
JD
3175 }
3176}
3177
3178#else /* not USE_GTK */
4dedbfe0
PR
3179static void
3180dialog_selection_callback (widget, id, client_data)
3181 Widget widget;
3182 LWLIB_ID id;
3183 XtPointer client_data;
3184{
01d5e892
RS
3185 /* The EMACS_INT cast avoids a warning. There's no problem
3186 as long as pointers have enough bits to hold small integers. */
3187 if ((int) (EMACS_INT) client_data != -1)
4dedbfe0 3188 menu_item_selection = (Lisp_Object *) client_data;
488dd4c4 3189
4dedbfe0
PR
3190 BLOCK_INPUT;
3191 lw_destroy_all_widgets (id);
3192 UNBLOCK_INPUT;
9572375b 3193 popup_activated_flag = 0;
4dedbfe0 3194}
18686d47 3195
488dd4c4 3196
488dd4c4
JD
3197/* Pop up the dialog for frame F defined by FIRST_WV and loop until the
3198 dialog pops down.
3199 menu_item_selection will be set to the selection. */
3200static void
3201create_and_show_dialog (f, first_wv)
3202 FRAME_PTR f;
3203 widget_value *first_wv;
3204{
3205 LWLIB_ID dialog_id;
3206
62af879c
KL
3207 if (!FRAME_X_P (f))
3208 abort();
3209
488dd4c4
JD
3210 dialog_id = widget_id_tick++;
3211 lw_create_widget (first_wv->name, "dialog", dialog_id, first_wv,
3212 f->output_data.x->widget, 1, 0,
3213 dialog_selection_callback, 0, 0);
3214 lw_modify_all_widgets (dialog_id, first_wv->contents, True);
3215
3216 /* Display the dialog box. */
3217 lw_pop_up_all_widgets (dialog_id);
3218 popup_activated_flag = 1;
98a20c65 3219 x_activate_timeout_atimer ();
488dd4c4
JD
3220
3221 /* Process events that apply to the dialog box.
3222 Also handle timers. */
3223 {
3224 int count = SPECPDL_INDEX ();
3225 int fact = 4 * sizeof (LWLIB_ID);
177c0ea7 3226
488dd4c4 3227 /* xdialog_show_unwind is responsible for popping the dialog box down. */
af89e871 3228 record_unwind_protect (pop_down_menu,
488dd4c4
JD
3229 Fcons (make_number (dialog_id >> (fact)),
3230 make_number (dialog_id & ~(-1 << (fact)))));
3231
df470e3b 3232 popup_get_selection ((XEvent *) 0, FRAME_X_DISPLAY_INFO (f),
95bdef2e 3233 dialog_id, 1);
488dd4c4
JD
3234
3235 unbind_to (count, Qnil);
3236 }
3237}
3238
3239#endif /* not USE_GTK */
3240
165e1749
FP
3241static char * button_names [] = {
3242 "button1", "button2", "button3", "button4", "button5",
3243 "button6", "button7", "button8", "button9", "button10" };
3244
3245static Lisp_Object
6a040d6a 3246xdialog_show (f, keymaps, title, header, error_name)
165e1749 3247 FRAME_PTR f;
165e1749 3248 int keymaps;
6a040d6a
NR
3249 Lisp_Object title, header;
3250 char **error_name;
165e1749
FP
3251{
3252 int i, nb_buttons=0;
80670155 3253 char dialog_name[6];
165e1749 3254
faa935b6 3255 widget_value *wv, *first_wv = 0, *prev_wv = 0;
165e1749 3256
fcaa7665
RS
3257 /* Number of elements seen so far, before boundary. */
3258 int left_count = 0;
3259 /* 1 means we've seen the boundary between left-hand elts and right-hand. */
3260 int boundary_seen = 0;
3261
62af879c
KL
3262 if (! FRAME_X_P (f))
3263 abort ();
3264
6a040d6a 3265 *error_name = NULL;
165e1749 3266
80670155
RS
3267 if (menu_items_n_panes > 1)
3268 {
6a040d6a 3269 *error_name = "Multiple panes in dialog box";
80670155
RS
3270 return Qnil;
3271 }
3272
165e1749
FP
3273 /* Create a tree of widget_value objects
3274 representing the text label and buttons. */
3275 {
3276 Lisp_Object pane_name, prefix;
3277 char *pane_string;
3278 pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
3279 prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
3280 pane_string = (NILP (pane_name)
177c0ea7 3281 ? "" : (char *) SDATA (pane_name));
f7fab165 3282 prev_wv = xmalloc_widget_value ();
165e1749
FP
3283 prev_wv->value = pane_string;
3284 if (keymaps && !NILP (prefix))
3285 prev_wv->name++;
3286 prev_wv->enabled = 1;
3287 prev_wv->name = "message";
27ad7b52 3288 prev_wv->help = Qnil;
165e1749 3289 first_wv = prev_wv;
177c0ea7 3290
165e1749
FP
3291 /* Loop over all panes and items, filling in the tree. */
3292 i = MENU_ITEMS_PANE_LENGTH;
3293 while (i < menu_items_used)
3294 {
177c0ea7 3295
165e1749
FP
3296 /* Create a new item within current pane. */
3297 Lisp_Object item_name, enable, descrip;
3298 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
3299 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
3300 descrip
3301 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
177c0ea7 3302
80670155
RS
3303 if (NILP (item_name))
3304 {
3305 free_menubar_widget_value_tree (first_wv);
6a040d6a 3306 *error_name = "Submenu in dialog items";
80670155
RS
3307 return Qnil;
3308 }
fcaa7665
RS
3309 if (EQ (item_name, Qquote))
3310 {
3311 /* This is the boundary between left-side elts
3312 and right-side elts. Stop incrementing right_count. */
3313 boundary_seen = 1;
3314 i++;
3315 continue;
3316 }
86e71abf 3317 if (nb_buttons >= 9)
80670155
RS
3318 {
3319 free_menubar_widget_value_tree (first_wv);
6a040d6a 3320 *error_name = "Too many dialog items";
80670155
RS
3321 return Qnil;
3322 }
3323
f7fab165 3324 wv = xmalloc_widget_value ();
165e1749 3325 prev_wv->next = wv;
80670155 3326 wv->name = (char *) button_names[nb_buttons];
165e1749 3327 if (!NILP (descrip))
d5db4077
KR
3328 wv->key = (char *) SDATA (descrip);
3329 wv->value = (char *) SDATA (item_name);
165e1749
FP
3330 wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
3331 wv->enabled = !NILP (enable);
27ad7b52 3332 wv->help = Qnil;
165e1749
FP
3333 prev_wv = wv;
3334
fcaa7665
RS
3335 if (! boundary_seen)
3336 left_count++;
3337
165e1749
FP
3338 nb_buttons++;
3339 i += MENU_ITEMS_ITEM_LENGTH;
3340 }
3341
fcaa7665
RS
3342 /* If the boundary was not specified,
3343 by default put half on the left and half on the right. */
3344 if (! boundary_seen)
3345 left_count = nb_buttons - nb_buttons / 2;
3346
f7fab165 3347 wv = xmalloc_widget_value ();
80670155 3348 wv->name = dialog_name;
27ad7b52 3349 wv->help = Qnil;
6a040d6a
NR
3350
3351 /* Frame title: 'Q' = Question, 'I' = Information.
3352 Can also have 'E' = Error if, one day, we want
3353 a popup for errors. */
3354 if (NILP(header))
3355 dialog_name[0] = 'Q';
3356 else
3357 dialog_name[0] = 'I';
3358
80670155
RS
3359 /* Dialog boxes use a really stupid name encoding
3360 which specifies how many buttons to use
6a040d6a 3361 and how many buttons are on the right. */
80670155
RS
3362 dialog_name[1] = '0' + nb_buttons;
3363 dialog_name[2] = 'B';
3364 dialog_name[3] = 'R';
fcaa7665
RS
3365 /* Number of buttons to put on the right. */
3366 dialog_name[4] = '0' + nb_buttons - left_count;
80670155 3367 dialog_name[5] = 0;
165e1749
FP
3368 wv->contents = first_wv;
3369 first_wv = wv;
165e1749
FP
3370 }
3371
165e1749
FP
3372 /* No selection has been chosen yet. */
3373 menu_item_selection = 0;
3374
f1f6b769
JD
3375 /* Force a redisplay before showing the dialog. If a frame is created
3376 just before showing the dialog, its contents may not have been fully
3377 drawn, as this depends on timing of events from the X server. Redisplay
3378 is not done when a dialog is shown. If redisplay could be done in the
3379 X event loop (i.e. the X event loop does not run in a signal handler)
3380 this would not be needed. */
3381 Fredisplay (Qt);
3382
488dd4c4
JD
3383 /* Actually create and show the dialog. */
3384 create_and_show_dialog (f, first_wv);
f02cac82 3385
488dd4c4
JD
3386 /* Free the widget_value objects we used to specify the contents. */
3387 free_menubar_widget_value_tree (first_wv);
177c0ea7 3388
488dd4c4
JD
3389 /* Find the selected item, and its pane, to return
3390 the proper value. */
165e1749
FP
3391 if (menu_item_selection != 0)
3392 {
3393 Lisp_Object prefix;
3394
3395 prefix = Qnil;
3396 i = 0;
3397 while (i < menu_items_used)
3398 {
3399 Lisp_Object entry;
3400
3401 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
3402 {
3403 prefix
3404 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
3405 i += MENU_ITEMS_PANE_LENGTH;
3406 }
85996cfb
GM
3407 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
3408 {
3409 /* This is the boundary between left-side elts and
3410 right-side elts. */
3411 ++i;
3412 }
165e1749
FP
3413 else
3414 {
3415 entry
3416 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
3417 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
3418 {
3419 if (keymaps != 0)
3420 {
3421 entry = Fcons (entry, Qnil);
3422 if (!NILP (prefix))
3423 entry = Fcons (prefix, entry);
3424 }
3425 return entry;
3426 }
3427 i += MENU_ITEMS_ITEM_LENGTH;
3428 }
3429 }
3430 }
9f6fcdc5
JD
3431 else
3432 /* Make "Cancel" equivalent to C-g. */
3433 Fsignal (Qquit, Qnil);
165e1749
FP
3434
3435 return Qnil;
3436}
ba461919 3437
488dd4c4 3438#else /* not USE_X_TOOLKIT && not USE_GTK */
78589e07 3439
3e703b25
GM
3440/* The frame of the last activated non-toolkit menu bar.
3441 Used to generate menu help events. */
3442
3443static struct frame *menu_help_frame;
3444
3445
62145073
GM
3446/* Show help HELP_STRING, or clear help if HELP_STRING is null.
3447
3448 PANE is the pane number, and ITEM is the menu item number in
3449 the menu (currently not used).
177c0ea7 3450
62145073
GM
3451 This cannot be done with generating a HELP_EVENT because
3452 XMenuActivate contains a loop that doesn't let Emacs process
3453 keyboard events. */
3e703b25
GM
3454
3455static void
62145073 3456menu_help_callback (help_string, pane, item)
3e703b25 3457 char *help_string;
62145073 3458 int pane, item;
3e703b25 3459{
62145073
GM
3460 extern Lisp_Object Qmenu_item;
3461 Lisp_Object *first_item;
3462 Lisp_Object pane_name;
3463 Lisp_Object menu_object;
177c0ea7 3464
62145073
GM
3465 first_item = XVECTOR (menu_items)->contents;
3466 if (EQ (first_item[0], Qt))
3467 pane_name = first_item[MENU_ITEMS_PANE_NAME];
3468 else if (EQ (first_item[0], Qquote))
3469 /* This shouldn't happen, see xmenu_show. */
ce33e8eb 3470 pane_name = empty_unibyte_string;
62145073
GM
3471 else
3472 pane_name = first_item[MENU_ITEMS_ITEM_NAME];
177c0ea7 3473
62145073
GM
3474 /* (menu-item MENU-NAME PANE-NUMBER) */
3475 menu_object = Fcons (Qmenu_item,
3476 Fcons (pane_name,
3477 Fcons (make_number (pane), Qnil)));
ba461919 3478 show_help_echo (help_string ? build_string (help_string) : Qnil,
62145073 3479 Qnil, menu_object, make_number (item), 1);
3e703b25 3480}
177c0ea7 3481
af89e871 3482static Lisp_Object
a130b901
JD
3483pop_down_menu (arg)
3484 Lisp_Object arg;
af89e871 3485{
a130b901
JD
3486 struct Lisp_Save_Value *p1 = XSAVE_VALUE (Fcar (arg));
3487 struct Lisp_Save_Value *p2 = XSAVE_VALUE (Fcdr (arg));
59cfb104 3488
a130b901
JD
3489 FRAME_PTR f = p1->pointer;
3490 XMenu *menu = p2->pointer;
af89e871
JD
3491
3492 BLOCK_INPUT;
af89e871 3493#ifndef MSDOS
a130b901
JD
3494 XUngrabPointer (FRAME_X_DISPLAY (f), CurrentTime);
3495 XUngrabKeyboard (FRAME_X_DISPLAY (f), CurrentTime);
af89e871 3496#endif
a130b901 3497 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
af89e871
JD
3498
3499#ifdef HAVE_X_WINDOWS
3500 /* Assume the mouse has moved out of the X window.
3501 If it has actually moved in, we will get an EnterNotify. */
3502 x_mouse_leave (FRAME_X_DISPLAY_INFO (f));
3503
3504 /* State that no mouse buttons are now held.
3505 (The oldXMenu code doesn't track this info for us.)
3506 That is not necessarily true, but the fiction leads to reasonable
3507 results, and it is a pain to ask which are actually held now. */
3508 FRAME_X_DISPLAY_INFO (f)->grabbed = 0;
3509
3510#endif /* HAVE_X_WINDOWS */
3511
3512 UNBLOCK_INPUT;
3513
3514 return Qnil;
3515}
3516
3e703b25 3517
78589e07 3518static Lisp_Object
673a6211 3519xmenu_show (f, x, y, for_click, keymaps, title, error)
78589e07
RS
3520 FRAME_PTR f;
3521 int x, y;
9685a93f
RS
3522 int for_click;
3523 int keymaps;
78589e07
RS
3524 Lisp_Object title;
3525 char **error;
dcfdbac7 3526{
177c0ea7 3527 Window root;
78589e07
RS
3528 XMenu *menu;
3529 int pane, selidx, lpane, status;
3530 Lisp_Object entry, pane_prefix;
dcfdbac7
JB
3531 char *datap;
3532 int ulx, uly, width, height;
3533 int dispwidth, dispheight;
453a4f1b 3534 int i, j, lines, maxlines;
4e8d3549 3535 int maxwidth;
78589e07
RS
3536 int dummy_int;
3537 unsigned int dummy_uint;
af89e871 3538 int specpdl_count = SPECPDL_INDEX ();
088831f6 3539
62af879c
KL
3540 if (! FRAME_X_P (f))
3541 abort ();
3542
07a675b7 3543 *error = 0;
78589e07
RS
3544 if (menu_items_n_panes == 0)
3545 return Qnil;
088831f6 3546
742f715d
KH
3547 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
3548 {
3549 *error = "Empty menu";
3550 return Qnil;
3551 }
3552
78589e07 3553 /* Figure out which root window F is on. */
92280f67 3554 XGetGeometry (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), &root,
78589e07
RS
3555 &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
3556 &dummy_uint, &dummy_uint);
18686d47 3557
78589e07 3558 /* Make the menu on that window. */
92280f67 3559 menu = XMenuCreate (FRAME_X_DISPLAY (f), root, "emacs");
78589e07 3560 if (menu == NULL)
dcfdbac7
JB
3561 {
3562 *error = "Can't create menu";
78589e07 3563 return Qnil;
dcfdbac7 3564 }
78589e07 3565
d130d129
RS
3566 /* Don't GC while we prepare and show the menu,
3567 because we give the oldxmenu library pointers to the
3568 contents of strings. */
3569 inhibit_garbage_collection ();
3570
87485d6f 3571#ifdef HAVE_X_WINDOWS
78589e07 3572 /* Adjust coordinates to relative to the outer (window manager) window. */
453a4f1b
JD
3573 x += FRAME_OUTER_TO_INNER_DIFF_X (f);
3574 y += FRAME_OUTER_TO_INNER_DIFF_Y (f);
87485d6f 3575#endif /* HAVE_X_WINDOWS */
78589e07
RS
3576
3577 /* Adjust coordinates to be root-window-relative. */
9882535b
KS
3578 x += f->left_pos;
3579 y += f->top_pos;
177c0ea7 3580
78589e07 3581 /* Create all the necessary panes and their items. */
453a4f1b 3582 maxlines = lines = i = 0;
78589e07 3583 while (i < menu_items_used)
dcfdbac7 3584 {
78589e07 3585 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
dcfdbac7 3586 {
78589e07
RS
3587 /* Create a new pane. */
3588 Lisp_Object pane_name, prefix;
3589 char *pane_string;
3590
453a4f1b
JD
3591 maxlines = max (maxlines, lines);
3592 lines = 0;
78589e07
RS
3593 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
3594 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
3595 pane_string = (NILP (pane_name)
d5db4077 3596 ? "" : (char *) SDATA (pane_name));
78589e07
RS
3597 if (keymaps && !NILP (prefix))
3598 pane_string++;
3599
92280f67 3600 lpane = XMenuAddPane (FRAME_X_DISPLAY (f), menu, pane_string, TRUE);
78589e07
RS
3601 if (lpane == XM_FAILURE)
3602 {
92280f67 3603 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
78589e07
RS
3604 *error = "Can't create pane";
3605 return Qnil;
3606 }
3607 i += MENU_ITEMS_PANE_LENGTH;
4e8d3549
RS
3608
3609 /* Find the width of the widest item in this pane. */
3610 maxwidth = 0;
3611 j = i;
3612 while (j < menu_items_used)
3613 {
3614 Lisp_Object item;
3615 item = XVECTOR (menu_items)->contents[j];
3616 if (EQ (item, Qt))
3617 break;
3618 if (NILP (item))
3619 {
3620 j++;
3621 continue;
3622 }
d5db4077 3623 width = SBYTES (item);
4e8d3549
RS
3624 if (width > maxwidth)
3625 maxwidth = width;
3626
3627 j += MENU_ITEMS_ITEM_LENGTH;
3628 }
dcfdbac7 3629 }
fcaa7665
RS
3630 /* Ignore a nil in the item list.
3631 It's meaningful only for dialog boxes. */
3632 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
3633 i += 1;
78589e07 3634 else
dcfdbac7 3635 {
78589e07 3636 /* Create a new item within current pane. */
3e703b25 3637 Lisp_Object item_name, enable, descrip, help;
4e8d3549 3638 unsigned char *item_data;
3e703b25 3639 char *help_string;
78589e07
RS
3640
3641 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
3642 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
3643 descrip
3644 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
3e703b25 3645 help = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_HELP];
d5db4077 3646 help_string = STRINGP (help) ? SDATA (help) : NULL;
177c0ea7 3647
78589e07 3648 if (!NILP (descrip))
4e8d3549 3649 {
d5db4077 3650 int gap = maxwidth - SBYTES (item_name);
4e8d3549
RS
3651 /* if alloca is fast, use that to make the space,
3652 to reduce gc needs. */
3653 item_data
3654 = (unsigned char *) alloca (maxwidth
d5db4077
KR
3655 + SBYTES (descrip) + 1);
3656 bcopy (SDATA (item_name), item_data,
3657 SBYTES (item_name));
3658 for (j = SCHARS (item_name); j < maxwidth; j++)
4e8d3549 3659 item_data[j] = ' ';
d5db4077
KR
3660 bcopy (SDATA (descrip), item_data + j,
3661 SBYTES (descrip));
3662 item_data[j + SBYTES (descrip)] = 0;
4e8d3549
RS
3663 }
3664 else
d5db4077 3665 item_data = SDATA (item_name);
78589e07 3666
92280f67
RS
3667 if (XMenuAddSelection (FRAME_X_DISPLAY (f),
3668 menu, lpane, 0, item_data,
3e703b25 3669 !NILP (enable), help_string)
dcfdbac7
JB
3670 == XM_FAILURE)
3671 {
92280f67 3672 XMenuDestroy (FRAME_X_DISPLAY (f), menu);
dcfdbac7 3673 *error = "Can't add selection to menu";
78589e07 3674 return Qnil;
dcfdbac7 3675 }
78589e07 3676 i += MENU_ITEMS_ITEM_LENGTH;
453a4f1b 3677 lines++;
dcfdbac7
JB
3678 }
3679 }
4e8d3549 3680
453a4f1b
JD
3681 maxlines = max (maxlines, lines);
3682
78589e07 3683 /* All set and ready to fly. */
92280f67 3684 XMenuRecompute (FRAME_X_DISPLAY (f), menu);
63685b9d
GM
3685 dispwidth = DisplayWidth (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
3686 dispheight = DisplayHeight (FRAME_X_DISPLAY (f), FRAME_X_SCREEN_NUMBER (f));
78589e07
RS
3687 x = min (x, dispwidth);
3688 y = min (y, dispheight);
3689 x = max (x, 1);
3690 y = max (y, 1);
92280f67 3691 XMenuLocate (FRAME_X_DISPLAY (f), menu, 0, 0, x, y,
dcfdbac7
JB
3692 &ulx, &uly, &width, &height);
3693 if (ulx+width > dispwidth)
3694 {
78589e07 3695 x -= (ulx + width) - dispwidth;
dcfdbac7
JB
3696 ulx = dispwidth - width;
3697 }
3698 if (uly+height > dispheight)
3699 {
78589e07 3700 y -= (uly + height) - dispheight;
dcfdbac7
JB
3701 uly = dispheight - height;
3702 }
78589e07
RS
3703 if (ulx < 0) x -= ulx;
3704 if (uly < 0) y -= uly;
121e4555 3705
453a4f1b
JD
3706 if (! for_click)
3707 {
3708 /* If position was not given by a mouse click, adjust so upper left
3709 corner of the menu as a whole ends up at given coordinates. This
3710 is what x-popup-menu says in its documentation. */
3711 x += width/2;
3712 y += 1.5*height/(maxlines+2);
3713 }
3714
121e4555 3715 XMenuSetAEQ (menu, TRUE);
78589e07
RS
3716 XMenuSetFreeze (menu, TRUE);
3717 pane = selidx = 0;
3e703b25 3718
c3438661
JD
3719#ifndef MSDOS
3720 XMenuActivateSetWaitFunction (x_menu_wait_for_event, FRAME_X_DISPLAY (f));
3721#endif
59cfb104 3722
a130b901
JD
3723 record_unwind_protect (pop_down_menu,
3724 Fcons (make_save_value (f, 0),
3725 make_save_value (menu, 0)));
c3438661 3726
3e703b25
GM
3727 /* Help display under X won't work because XMenuActivate contains
3728 a loop that doesn't give Emacs a chance to process it. */
3729 menu_help_frame = f;
92280f67 3730 status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
af89e871
JD
3731 x, y, ButtonReleaseMask, &datap,
3732 menu_help_callback);
a352a815 3733
dcfdbac7
JB
3734 switch (status)
3735 {
3736 case XM_SUCCESS:
3737#ifdef XDEBUG
3738 fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
3739#endif
fa6d54d9 3740
78589e07
RS
3741 /* Find the item number SELIDX in pane number PANE. */
3742 i = 0;
3743 while (i < menu_items_used)
fa6d54d9 3744 {
78589e07 3745 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
088831f6 3746 {
78589e07
RS
3747 if (pane == 0)
3748 pane_prefix
3749 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
3750 pane--;
3751 i += MENU_ITEMS_PANE_LENGTH;
088831f6 3752 }
78589e07 3753 else
ab6ee1a0 3754 {
78589e07 3755 if (pane == -1)
ab6ee1a0 3756 {
78589e07 3757 if (selidx == 0)
ab6ee1a0 3758 {
78589e07
RS
3759 entry
3760 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
3761 if (keymaps != 0)
ab6ee1a0 3762 {
78589e07
RS
3763 entry = Fcons (entry, Qnil);
3764 if (!NILP (pane_prefix))
3765 entry = Fcons (pane_prefix, entry);
ab6ee1a0 3766 }
78589e07 3767 break;
ab6ee1a0 3768 }
78589e07 3769 selidx--;
ab6ee1a0 3770 }
78589e07 3771 i += MENU_ITEMS_ITEM_LENGTH;
ab6ee1a0
RS
3772 }
3773 }
78589e07 3774 break;
dcfdbac7 3775
78589e07 3776 case XM_FAILURE:
78589e07
RS
3777 *error = "Can't activate menu";
3778 case XM_IA_SELECT:
9f6fcdc5
JD
3779 entry = Qnil;
3780 break;
78589e07 3781 case XM_NO_SELECT:
be6ed24a
RS
3782 /* Make "Cancel" equivalent to C-g unless FOR_CLICK (which means
3783 the menu was invoked with a mouse event as POSITION). */
9f6fcdc5
JD
3784 if (! for_click)
3785 Fsignal (Qquit, Qnil);
78589e07
RS
3786 entry = Qnil;
3787 break;
dcfdbac7 3788 }
a5285df3 3789
af89e871 3790 unbind_to (specpdl_count, Qnil);
a5285df3 3791
78589e07 3792 return entry;
dcfdbac7 3793}
4dedbfe0 3794
78589e07 3795#endif /* not USE_X_TOOLKIT */
1e659e4c
RS
3796
3797#endif /* HAVE_MENUS */
e3135734 3798
b79b8a1c
CY
3799/* Detect if a dialog or menu has been posted. */
3800
3801int
3802popup_activated ()
3803{
3804 return popup_activated_flag;
3805}
e3135734
CY
3806
3807/* The following is used by delayed window autoselection. */
3808
3809DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
3810 doc: /* Return t if a menu or popup dialog is active. */)
3811 ()
3812{
3813#ifdef HAVE_MENUS
3814 return (popup_activated ()) ? Qt : Qnil;
3815#else
3816 return Qnil;
3817#endif /* HAVE_MENUS */
3818}
088831f6 3819\f
dfcf069d 3820void
78589e07 3821syms_of_xmenu ()
dcfdbac7 3822{
78589e07
RS
3823 staticpro (&menu_items);
3824 menu_items = Qnil;
86fad4ec 3825 menu_items_inuse = Qnil;
dcfdbac7 3826
0314aacb
RS
3827 Qdebug_on_next_call = intern ("debug-on-next-call");
3828 staticpro (&Qdebug_on_next_call);
3829
8ed87156 3830#ifdef USE_X_TOOLKIT
177c0ea7 3831 widget_id_tick = (1<<16);
88766961 3832 next_menubar_widget_id = 1;
8ed87156
RS
3833#endif
3834
78589e07 3835 defsubr (&Sx_popup_menu);
508fb067 3836 defsubr (&Smenu_or_popup_active_p);
22badffe
JD
3837
3838#if defined (USE_GTK) || defined (USE_X_TOOLKIT)
12b6af5c
KL
3839 defsubr (&Sx_menu_bar_open_internal);
3840 Ffset (intern ("accelerate-menu"),
3841 intern (Sx_menu_bar_open_internal.symbol_name));
22badffe
JD
3842#endif
3843
1e659e4c 3844#ifdef HAVE_MENUS
165e1749 3845 defsubr (&Sx_popup_dialog);
1e659e4c 3846#endif
dcfdbac7 3847}
6b61353c
KH
3848
3849/* arch-tag: 92ea573c-398e-496e-ac73-2436f7d63242
3850 (do not change this comment) */