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