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