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