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