Comment change.
[bpt/emacs.git] / src / xmenu.c
CommitLineData
dcfdbac7 1/* X Communication module for terminals which understand the X protocol.
b5b4d636 2 Copyright (C) 1986, 1988, 1993, 1994 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
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20/* X pop-up deck-of-cards menu facility for gnuemacs.
21 *
22 * Written by Jon Arnold and Roman Budzianowski
23 * Mods and rewrite by Robert Krawitz
24 *
25 */
26
18686d47
RS
27/* Modified by Fred Pierresteguy on December 93
28 to make the popup menus and menubar use the Xt. */
29
78589e07
RS
30/* Rewritten for clarity and GC protection by rms in Feb 94. */
31
dcfdbac7 32#include <stdio.h>
dcfdbac7
JB
33
34/* On 4.3 this loses if it comes after xterm.h. */
35#include <signal.h>
18160b98 36#include <config.h>
dcfdbac7 37#include "lisp.h"
18686d47 38#include "termhooks.h"
7708e9bd 39#include "frame.h"
dcfdbac7 40#include "window.h"
031b0e31 41#include "keyboard.h"
9ac0d9e0 42#include "blockinput.h"
dcfdbac7
JB
43
44/* This may include sys/types.h, and that somehow loses
45 if this is not done before the other system files. */
46#include "xterm.h"
47
48/* Load sys/types.h if not already loaded.
49 In some systems loading it twice is suicidal. */
50#ifndef makedev
51#include <sys/types.h>
52#endif
53
54#include "dispextern.h"
55
56#ifdef HAVE_X11
57#include "../oldXMenu/XMenu.h"
58#else
59#include <X/XMenu.h>
60#endif
61
18686d47
RS
62#ifdef USE_X_TOOLKIT
63#include <X11/Xlib.h>
64#include <X11/IntrinsicP.h>
65#include <X11/CoreP.h>
66#include <X11/StringDefs.h>
67#include <X11/Xaw/Paned.h>
68#include "../lwlib/lwlib.h"
69#include "../lwlib/xlwmenuP.h"
70#endif /* USE_X_TOOLKIT */
71
dcfdbac7
JB
72#define min(x,y) (((x) < (y)) ? (x) : (y))
73#define max(x,y) (((x) > (y)) ? (x) : (y))
74
dcfdbac7
JB
75#ifndef TRUE
76#define TRUE 1
77#define FALSE 0
78589e07 78#endif /* no TRUE */
dcfdbac7
JB
79
80#ifdef HAVE_X11
81extern Display *x_current_display;
82#else
83#define ButtonReleaseMask ButtonReleased
84#endif /* not HAVE_X11 */
85
78589e07
RS
86/* We need a unique id for each popup menu and dialog box. */
87static unsigned int popup_id_tick;
88
6904bdcd 89extern Lisp_Object Qmenu_enable;
18686d47 90extern Lisp_Object Qmenu_bar;
78589e07 91
18686d47 92#ifdef USE_X_TOOLKIT
78589e07
RS
93extern void process_expose_from_menu ();
94extern XtAppContext Xt_app_con;
95
18686d47 96static int string_width ();
18686d47
RS
97#endif
98
78589e07
RS
99static Lisp_Object xmenu_show ();
100static void keymap_panes ();
101static void single_keymap_panes ();
102static void list_of_panes ();
103static void list_of_items ();
104\f
105/* This holds a Lisp vector that holds the results of decoding
106 the keymaps or alist-of-alists that specify a menu.
dcfdbac7 107
78589e07 108 It describes the panes and items within the panes.
dcfdbac7 109
78589e07
RS
110 Each pane is described by 3 elements in the vector:
111 t, the pane name, the pane's prefix key.
112 Then follow the pane's items, with 4 elements per item:
113 the item string, the enable flag, the item's value,
114 and the equivalent keyboard key's description string.
dcfdbac7 115
78589e07
RS
116 Using a Lisp vector to hold this information while we decode it
117 takes care of protecting all the data from GC. */
dcfdbac7 118
78589e07
RS
119#define MENU_ITEMS_PANE_NAME 1
120#define MENU_ITEMS_PANE_PREFIX 2
121#define MENU_ITEMS_PANE_LENGTH 3
088831f6 122
78589e07
RS
123#define MENU_ITEMS_ITEM_NAME 0
124#define MENU_ITEMS_ITEM_ENABLE 1
125#define MENU_ITEMS_ITEM_VALUE 2
126#define MENU_ITEMS_ITEM_EQUIV_KEY 3
127#define MENU_ITEMS_ITEM_LENGTH 4
7da99777 128
78589e07 129static Lisp_Object menu_items;
18686d47 130
78589e07
RS
131/* Number of slots currently allocated in menu_items. */
132static int menu_items_allocated;
18686d47 133
78589e07
RS
134/* This is the index in menu_items of the first empty slot. */
135static int menu_items_used;
18686d47 136
78589e07
RS
137/* The number of panes currently recorded in menu_items. */
138static int menu_items_n_panes;
18686d47 139
78589e07
RS
140/* Initialize the menu_items structure if we haven't already done so.
141 Also mark it as currently empty. */
142
143static void
144init_menu_items ()
145{
146 if (NILP (menu_items))
147 {
148 menu_items_allocated = 60;
149 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
18686d47
RS
150 }
151
78589e07
RS
152 menu_items_used = 0;
153 menu_items_n_panes = 0;
154}
18686d47 155
78589e07
RS
156/* Call at the end of generating the data in menu_items.
157 This fills in the number of items in the last pane. */
1658603c 158
78589e07
RS
159static void
160finish_menu_items ()
161{
162}
1658603c 163
78589e07
RS
164/* Call when finished using the data for the current menu
165 in menu_items. */
1658603c 166
78589e07
RS
167static void
168discard_menu_items ()
169{
170 /* Free the structure if it is especially large.
171 Otherwise, hold on to it, to save time. */
172 if (menu_items_allocated > 200)
173 {
174 menu_items = Qnil;
175 menu_items_allocated = 0;
176 }
177}
1658603c 178
78589e07
RS
179/* Start a new menu pane in menu_items..
180 NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
1658603c 181
78589e07
RS
182static void
183push_menu_pane (name, prefix_vec)
184 Lisp_Object name, prefix_vec;
185{
186 if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
187 {
188 Lisp_Object old;
189 int old_size = menu_items_allocated;
190 old = menu_items;
191
192 menu_items_allocated *= 2;
193 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
194 bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
195 old_size * sizeof (Lisp_Object));
196 }
dcfdbac7 197
78589e07
RS
198 menu_items_n_panes++;
199 XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
200 XVECTOR (menu_items)->contents[menu_items_used++] = name;
201 XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
202}
dcfdbac7 203
78589e07
RS
204/* Push one menu item into the current pane.
205 NAME is the string to display. ENABLE if non-nil means
206 this item can be selected. KEY is the key generated by
207 choosing this item. EQUIV is the textual description
208 of the keyboard equivalent for this item (or nil if none). */
18686d47 209
78589e07
RS
210static void
211push_menu_item (name, enable, key, equiv)
212 Lisp_Object name, enable, key, equiv;
213{
214 if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
088831f6 215 {
78589e07
RS
216 Lisp_Object old;
217 int old_size = menu_items_allocated;
218 old = menu_items;
219
220 menu_items_allocated *= 2;
221 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
222 bcopy (XVECTOR (old)->contents, XVECTOR (menu_items)->contents,
223 old_size * sizeof (Lisp_Object));
224 }
088831f6 225
78589e07
RS
226 XVECTOR (menu_items)->contents[menu_items_used++] = name;
227 XVECTOR (menu_items)->contents[menu_items_used++] = enable;
228 XVECTOR (menu_items)->contents[menu_items_used++] = key;
229 XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
230}
231\f
232/* Figure out the current keyboard equivalent of a menu item ITEM1.
233 The item string for menu display should be ITEM_STRING.
234 Store the equivalent keyboard key sequence's
235 textual description into *DESCRIP_PTR.
236 Also cache them in the item itself.
237 Return the real definition to execute. */
088831f6 238
78589e07
RS
239static Lisp_Object
240menu_item_equiv_key (item_string, item1, descrip_ptr)
241 Lisp_Object item_string;
242 Lisp_Object item1;
243 Lisp_Object *descrip_ptr;
244{
245 /* This is the real definition--the function to run. */
246 Lisp_Object def;
247 /* This is the sublist that records cached equiv key data
248 so we can save time. */
249 Lisp_Object cachelist;
250 /* These are the saved equivalent keyboard key sequence
251 and its key-description. */
252 Lisp_Object savedkey, descrip;
253 Lisp_Object def1;
254 int changed = 0;
088831f6 255
78589e07
RS
256 /* If a help string follows the item string, skip it. */
257 if (CONSP (XCONS (item1)->cdr)
258 && STRINGP (XCONS (XCONS (item1)->cdr)->car))
259 item1 = XCONS (item1)->cdr;
088831f6 260
78589e07 261 def = Fcdr (item1);
088831f6 262
78589e07
RS
263 /* Get out the saved equivalent-keyboard-key info. */
264 cachelist = savedkey = descrip = Qnil;
265 if (CONSP (def) && CONSP (XCONS (def)->car)
266 && (NILP (XCONS (XCONS (def)->car)->car)
267 || VECTORP (XCONS (XCONS (def)->car)->car)))
088831f6 268 {
78589e07
RS
269 cachelist = XCONS (def)->car;
270 def = XCONS (def)->cdr;
271 savedkey = XCONS (cachelist)->car;
272 descrip = XCONS (cachelist)->cdr;
088831f6 273 }
78589e07
RS
274
275 /* Is it still valid? */
276 def1 = Qnil;
277 if (!NILP (savedkey))
278 def1 = Fkey_binding (savedkey, Qnil);
279 /* If not, update it. */
280 if (! EQ (def1, def)
281 /* If something had no key binding before, don't recheck it--
282 doing that takes too much time and makes menus too slow. */
283 && !(!NILP (cachelist) && NILP (savedkey)))
088831f6 284 {
78589e07
RS
285 changed = 1;
286 descrip = Qnil;
287 savedkey = Fwhere_is_internal (def, Qnil, Qt, Qnil);
288 if (VECTORP (savedkey)
289 && EQ (XVECTOR (savedkey)->contents[0], Qmenu_bar))
290 savedkey = Qnil;
291 if (!NILP (savedkey))
292 {
293 descrip = Fkey_description (savedkey);
294 descrip = concat2 (make_string (" (", 3), descrip);
295 descrip = concat2 (descrip, make_string (")", 1));
296 }
dcfdbac7 297 }
18686d47 298
78589e07
RS
299 /* Cache the data we just got in a sublist of the menu binding. */
300 if (NILP (cachelist))
301 XCONS (item1)->cdr = Fcons (Fcons (savedkey, descrip), def);
302 else if (changed)
dcfdbac7 303 {
78589e07
RS
304 XCONS (cachelist)->car = savedkey;
305 XCONS (cachelist)->cdr = descrip;
dcfdbac7 306 }
18686d47 307
78589e07
RS
308 *descrip_ptr = descrip;
309 return def;
18686d47
RS
310}
311
78589e07
RS
312/* This is used as the handler when calling internal_condition_case_1. */
313
314static Lisp_Object
315menu_item_enabled_p_1 (arg)
316 Lisp_Object arg;
18686d47 317{
78589e07 318 return Qnil;
dcfdbac7
JB
319}
320
78589e07
RS
321/* Return non-nil if the command DEF is enabled when used as a menu item.
322 This is based on looking for a menu-enable property. */
323
324static Lisp_Object
325menu_item_enabled_p (def)
326 Lisp_Object def;
18686d47 327{
78589e07 328 Lisp_Object enabled, tem;
18686d47 329
78589e07
RS
330 enabled = Qt;
331 if (XTYPE (def) == Lisp_Symbol)
332 {
333 /* No property, or nil, means enable.
334 Otherwise, enable if value is not nil. */
335 tem = Fget (def, Qmenu_enable);
336 if (!NILP (tem))
337 /* (condition-case nil (eval tem)
338 (error nil)) */
339 enabled = internal_condition_case_1 (Feval, tem, Qerror,
340 menu_item_enabled_p_1);
341 }
342 return enabled;
343}
344\f
345/* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
346 and generate menu panes for them in menu_items. */
18686d47 347
78589e07
RS
348static void
349keymap_panes (keymaps, nmaps)
350 Lisp_Object *keymaps;
351 int nmaps;
18686d47 352{
78589e07 353 int mapno;
18686d47 354
78589e07 355 init_menu_items ();
18686d47 356
78589e07
RS
357 /* Loop over the given keymaps, making a pane for each map.
358 But don't make a pane that is empty--ignore that map instead.
359 P is the number of panes we have made so far. */
360 for (mapno = 0; mapno < nmaps; mapno++)
361 single_keymap_panes (keymaps[mapno], Qnil, Qnil);
362
363 finish_menu_items ();
364}
365
366/* This is a recursive subroutine of keymap_panes.
367 It handles one keymap, KEYMAP.
368 The other arguments are passed along
369 or point to local variables of the previous function. */
370
371static void
372single_keymap_panes (keymap, pane_name, prefix)
373 Lisp_Object keymap;
374 Lisp_Object pane_name;
375 Lisp_Object prefix;
376{
377 Lisp_Object pending_maps;
378 Lisp_Object tail, item, item1, item_string, table;
379 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
380
381 pending_maps = Qnil;
382
383 push_menu_pane (pane_name, prefix);
384
385 for (tail = keymap; XTYPE (tail) == Lisp_Cons; tail = XCONS (tail)->cdr)
18686d47 386 {
78589e07
RS
387 /* Look at each key binding, and if it has a menu string,
388 make a menu item from it. */
389 item = XCONS (tail)->car;
390 if (XTYPE (item) == Lisp_Cons)
18686d47 391 {
78589e07
RS
392 item1 = XCONS (item)->cdr;
393 if (XTYPE (item1) == Lisp_Cons)
394 {
395 item_string = XCONS (item1)->car;
396 if (XTYPE (item_string) == Lisp_String)
397 {
398 /* This is the real definition--the function to run. */
399 Lisp_Object def;
400 /* These are the saved equivalent keyboard key sequence
401 and its key-description. */
402 Lisp_Object descrip;
403 Lisp_Object tem, enabled;
404
405 def = menu_item_equiv_key (item_string, item1, &descrip);
406
407 /* GCPRO because we will call eval.
408 Protecting KEYMAP preserves everything we use;
409 aside from that, must protect whatever might be
410 a string. Since there's no GCPRO5, we refetch
411 item_string instead of protecting it. */
412 GCPRO4 (keymap, pending_maps, def, descrip);
413 enabled = menu_item_enabled_p (def);
414 UNGCPRO;
415
416 item_string = XCONS (item1)->car;
417
418 tem = Fkeymapp (def);
419 if (XSTRING (item_string)->data[0] == '@' && !NILP (tem))
420 pending_maps = Fcons (Fcons (def, Fcons (item_string, XCONS (item)->car)),
421 pending_maps);
422 else
423 push_menu_item (item_string, enabled, XCONS (item)->car,
424 descrip);
425 }
426 }
427 }
428 else if (XTYPE (item) == Lisp_Vector)
429 {
430 /* Loop over the char values represented in the vector. */
431 int len = XVECTOR (item)->size;
432 int c;
433 for (c = 0; c < len; c++)
434 {
435 Lisp_Object character;
436 XFASTINT (character) = c;
437 item1 = XVECTOR (item)->contents[c];
438 if (XTYPE (item1) == Lisp_Cons)
439 {
440 item_string = XCONS (item1)->car;
441 if (XTYPE (item_string) == Lisp_String)
442 {
443 Lisp_Object def;
444
445 /* These are the saved equivalent keyboard key sequence
446 and its key-description. */
447 Lisp_Object descrip;
448 Lisp_Object tem, enabled;
449
450 def = menu_item_equiv_key (item_string, item1, &descrip);
451
452 /* GCPRO because we will call eval.
453 Protecting KEYMAP preserves everything we use;
454 aside from that, must protect whatever might be
455 a string. Since there's no GCPRO5, we refetch
456 item_string instead of protecting it. */
457 GCPRO4 (keymap, pending_maps, def, descrip);
458 enabled = menu_item_enabled_p (def);
459 UNGCPRO;
460
461 item_string = XCONS (item1)->car;
462
463 tem = Fkeymapp (def);
464 if (XSTRING (item_string)->data[0] == '@' && !NILP (tem))
465 pending_maps = Fcons (Fcons (def, Fcons (item_string, character)),
466 pending_maps);
467 else
468 push_menu_item (item_string, enabled,
469 character, descrip);
470 }
471 }
472 }
18686d47
RS
473 }
474 }
78589e07
RS
475
476 /* Process now any submenus which want to be panes at this level. */
477 while (!NILP (pending_maps))
478 {
479 Lisp_Object elt, eltcdr;
480 elt = Fcar (pending_maps);
481 eltcdr = XCONS (elt)->cdr;
482 single_keymap_panes (Fcar (elt),
483 /* Fails to discard the @. */
484 XCONS (eltcdr)->car, XCONS (eltcdr)->cdr);
485 pending_maps = Fcdr (pending_maps);
486 }
18686d47 487}
78589e07
RS
488\f
489/* Push all the panes and items of a menu decsribed by the
490 alist-of-alists MENU.
491 This handles old-fashioned calls to x-popup-menu. */
18686d47 492
78589e07
RS
493static void
494list_of_panes (menu)
18686d47 495 Lisp_Object menu;
18686d47 496{
78589e07
RS
497 Lisp_Object tail;
498
499 init_menu_items ();
500
501 for (tail = menu; !NILP (tail); tail = Fcdr (tail))
502 {
503 Lisp_Object elt, pane_name, pane_data;
504 elt = Fcar (tail);
505 pane_name = Fcar (elt);
506 CHECK_STRING (pane_name, 0);
507 push_menu_pane (pane_name, Qnil);
508 pane_data = Fcdr (elt);
509 CHECK_CONS (pane_data, 0);
510 list_of_items (pane_data);
511 }
512
513 finish_menu_items ();
514}
515
516/* Push the items in a single pane defined by the alist PANE. */
517
518static void
519list_of_items (pane)
520 Lisp_Object pane;
521{
522 Lisp_Object tail, item, item1;
523
524 for (tail = pane; !NILP (tail); tail = Fcdr (tail))
525 {
526 item = Fcar (tail);
527 if (STRINGP (item))
528 push_menu_item (item, Qnil, Qnil);
529 else
530 {
531 CHECK_CONS (item, 0);
532 item1 = Fcar (item);
533 CHECK_STRING (item1, 1);
534 push_menu_item (item1, Qt, Fcdr (item), Qnil);
535 }
536 }
537}
538\f
539DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 1, 2, 0,
540 "Pop up a deck-of-cards menu and return user's selection.\n\
541POSITION is a position specification. This is either a mouse button event\n\
542or a list ((XOFFSET YOFFSET) WINDOW)\n\
543where XOFFSET and YOFFSET are positions in characters from the top left\n\
544corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.)\n\
545This controls the position of the center of the first line\n\
546in the first pane of the menu, not the top left of the menu as a whole.\n\
547If POSITION is t, it means to use the current mouse position.\n\
548\n\
549MENU is a specifier for a menu. For the simplest case, MENU is a keymap.\n\
550The menu items come from key bindings that have a menu string as well as\n\
551a definition; actually, the \"definition\" in such a key binding looks like\n\
552\(STRING . REAL-DEFINITION). To give the menu a title, put a string into\n\
553the keymap as a top-level element.\n\n\
554You can also use a list of keymaps as MENU.\n\
555 Then each keymap makes a separate pane.\n\
556When MENU is a keymap or a list of keymaps, the return value\n\
557is a list of events.\n\n\
558Alternatively, you can specify a menu of multiple panes\n\
559 with a list of the form (TITLE PANE1 PANE2...),\n\
560where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
561Each ITEM is normally a cons cell (STRING . VALUE);\n\
562but a string can appear as an item--that makes a nonselectable line\n\
563in the menu.\n\
564With this form of menu, the return value is VALUE from the chosen item.\n\
565\n\
566If POSITION is nil, don't display the menu at all, just precalculate the\n\
567cached information about equivalent key sequences.")
568 (position, menu)
569 Lisp_Object position, menu;
570{
571 int number_of_panes, panes;
18686d47 572 Lisp_Object keymap, tem;
78589e07
RS
573 int xpos, ypos;
574 Lisp_Object title;
575 char *error_name;
576 Lisp_Object selection;
18686d47 577 int i, j;
78589e07
RS
578 FRAME_PTR f;
579 Lisp_Object x, y, window;
580 int keymaps = 0;
581 int menubarp = 0;
582 struct gcpro gcpro1;
583
584 check_x ();
585
586 if (! NILP (position))
587 {
588 /* Decode the first argument: find the window and the coordinates. */
589 if (EQ (position, Qt))
590 {
591 /* Use the mouse's current position. */
592 FRAME_PTR new_f;
593 Lisp_Object bar_window;
594 int part;
595 unsigned long time;
596
597 (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
598 XSET (window, Lisp_Frame, new_f);
599 }
600 else
601 {
602 tem = Fcar (position);
603 if (XTYPE (tem) == Lisp_Cons)
604 {
605 window = Fcar (Fcdr (position));
606 x = Fcar (tem);
607 y = Fcar (Fcdr (tem));
608 }
609 else
610 {
611 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
612 window = Fcar (tem); /* POSN_WINDOW (tem) */
613 tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
614 x = Fcar (tem);
615 y = Fcdr (tem);
616
617 /* Determine whether this menu is handling a menu bar click. */
618 tem = Fcar (Fcdr (Fcar (Fcdr (position))));
619 if (XTYPE (Fcar (position)) != Lisp_Cons
620 && CONSP (tem)
621 && EQ (Fcar (tem), Qmenu_bar))
622 menubarp = 1;
623 }
624 }
625
626 CHECK_NUMBER (x, 0);
627 CHECK_NUMBER (y, 0);
628
629 /* Decode where to put the menu. */
630
631 if (XTYPE (window) == Lisp_Frame)
632 {
633 f = XFRAME (window);
634
635 xpos = 0;
636 ypos = 0;
637 }
638 else if (XTYPE (window) == Lisp_Window)
639 {
640 CHECK_LIVE_WINDOW (window, 0);
641 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
642
643 xpos = (FONT_WIDTH (f->display.x->font) * XWINDOW (window)->left);
644 ypos = (FONT_HEIGHT (f->display.x->font) * XWINDOW (window)->top);
645 }
646 else
647 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
648 but I don't want to make one now. */
649 CHECK_WINDOW (window, 0);
650
651 xpos += XINT (x);
652 ypos += XINT (y);
653 }
654
655 title = Qnil;
656 GCPRO1 (title);
657
658 /* Decode the menu items from what was specified. */
18686d47
RS
659
660 keymap = Fkeymapp (menu);
661 tem = Qnil;
18686d47
RS
662 if (XTYPE (menu) == Lisp_Cons)
663 tem = Fkeymapp (Fcar (menu));
664 if (!NILP (keymap))
665 {
666 /* We were given a keymap. Extract menu info from the keymap. */
667 Lisp_Object prompt;
668 keymap = get_keymap (menu);
669
78589e07
RS
670 /* Extract the detailed info to make one pane. */
671 keymap_panes (&menu, 1);
672
18686d47
RS
673 /* Search for a string appearing directly as an element of the keymap.
674 That string is the title of the menu. */
675 prompt = map_prompt (keymap);
18686d47 676
78589e07
RS
677 /* Make that be the pane title of the first pane. */
678 if (!NILP (prompt) && menu_items_n_panes >= 0)
679 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
680
681 keymaps = 1;
18686d47
RS
682 }
683 else if (!NILP (tem))
684 {
685 /* We were given a list of keymaps. */
18686d47
RS
686 int nmaps = XFASTINT (Flength (menu));
687 Lisp_Object *maps
688 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
689 int i;
78589e07
RS
690
691 title = Qnil;
18686d47
RS
692
693 /* The first keymap that has a prompt string
694 supplies the menu title. */
695 for (tem = menu, i = 0; XTYPE (tem) == Lisp_Cons; tem = Fcdr (tem))
696 {
78589e07
RS
697 Lisp_Object prompt;
698
18686d47
RS
699 maps[i++] = keymap = get_keymap (Fcar (tem));
700
701 prompt = map_prompt (keymap);
78589e07
RS
702 if (NILP (title) && !NILP (prompt))
703 title = prompt;
18686d47
RS
704 }
705
706 /* Extract the detailed info to make one pane. */
78589e07
RS
707 keymap_panes (maps, nmaps);
708
709 /* Make the title be the pane title of the first pane. */
710 if (!NILP (title) && menu_items_n_panes >= 0)
711 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
712
713 keymaps = 1;
18686d47
RS
714 }
715 else
716 {
717 /* We were given an old-fashioned menu. */
78589e07
RS
718 title = Fcar (menu);
719 CHECK_STRING (title, 1);
18686d47 720
78589e07 721 list_of_panes (Fcdr (menu));
18686d47 722
78589e07
RS
723 keymaps = 0;
724 }
18686d47 725
78589e07 726 if (NILP (position))
18686d47 727 {
78589e07
RS
728 discard_menu_items ();
729 UNGCPRO;
730 return Qnil;
18686d47
RS
731 }
732
78589e07
RS
733 /* Display them in a menu. */
734 BLOCK_INPUT;
18686d47 735
78589e07
RS
736 selection = xmenu_show (f, xpos, ypos, menubarp,
737 keymaps, title, &error_name);
738 UNBLOCK_INPUT;
18686d47 739
78589e07 740 discard_menu_items ();
18686d47 741
78589e07 742 UNGCPRO;
18686d47 743
78589e07
RS
744 if (error_name) error (error_name);
745 return selection;
18686d47 746}
78589e07
RS
747\f
748#ifdef USE_X_TOOLKIT
18686d47
RS
749
750static void
78589e07
RS
751dispatch_dummy_expose (w, x, y)
752 Widget w;
753 int x;
754 int y;
755{
756 XExposeEvent dummy;
757
758 dummy.type = Expose;
759 dummy.window = XtWindow (w);
760 dummy.count = 0;
761 dummy.serial = 0;
762 dummy.send_event = 0;
763 dummy.display = XtDisplay (w);
764 dummy.x = x;
765 dummy.y = y;
766
767 XtDispatchEvent (&dummy);
768}
769
770static int
771string_width (mw, s)
772 XlwMenuWidget mw;
773 char* s;
774{
775 XCharStruct xcs;
776 int drop;
777
778 XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs);
779 return xcs.width;
780}
781
782static int
783event_is_in_menu_item (mw, event, name, string_w)
784 XlwMenuWidget mw;
785 struct input_event *event;
786 char *name;
787 int *string_w;
788{
789 *string_w += (string_width (mw, name)
790 + 2 * (mw->menu.horizontal_spacing
791 + mw->menu.shadow_thickness));
792 return XINT (event->x) < *string_w;
793}
794
795
796Lisp_Object
797map_event_to_object (event, f)
798 struct input_event *event;
799 FRAME_PTR f;
800{
801 int i,j, string_w;
802 window_state* ws;
803 XlwMenuWidget mw = (XlwMenuWidget) f->display.x->menubar_widget;
804 widget_value *val;
805
806
807 string_w = 0;
808 /* Find the window */
809 for (val = mw->menu.old_stack [0]->contents; val; val = val->next)
810 {
811 ws = &mw->menu.windows [0];
812 if (ws && event_is_in_menu_item (mw, event, val->name, &string_w))
813 {
814 Lisp_Object items;
815 items = FRAME_MENU_BAR_ITEMS (f);
816 for (; CONSP (items); items = XCONS (items)->cdr)
817 if (!strcmp (val->name,
818 XSTRING (Fcar (Fcdr (Fcar (items))))->data))
819 return items;
820 }
821 }
822 return Qnil;
823}
824
825static Lisp_Object *menu_item_selection;
826
827static void
828popup_selection_callback (widget, id, client_data)
829 Widget widget;
830 LWLIB_ID id;
831 XtPointer client_data;
832{
833 menu_item_selection = (Lisp_Object *) client_data;
834}
835
836static void
837popup_down_callback (widget, id, client_data)
838 Widget widget;
839 LWLIB_ID id;
840 XtPointer client_data;
841{
18686d47
RS
842 BLOCK_INPUT;
843 lw_destroy_all_widgets (id);
844 UNBLOCK_INPUT;
18686d47
RS
845}
846
847/* This recursively calls free_widget_value() on the tree of widgets.
848 It must free all data that was malloc'ed for these widget_values.
78589e07
RS
849 In Emacs, many slots are pointers into the data of Lisp_Strings, and
850 must be left alone. */
851
18686d47
RS
852void
853free_menubar_widget_value_tree (wv)
854 widget_value *wv;
855{
856 if (! wv) return;
18686d47
RS
857
858 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
859
860 if (wv->contents && (wv->contents != (widget_value*)1))
861 {
862 free_menubar_widget_value_tree (wv->contents);
863 wv->contents = (widget_value *) 0xDEADBEEF;
864 }
865 if (wv->next)
866 {
867 free_menubar_widget_value_tree (wv->next);
868 wv->next = (widget_value *) 0xDEADBEEF;
869 }
870 BLOCK_INPUT;
871 free_widget_value (wv);
872 UNBLOCK_INPUT;
873}
874
875static void
876update_one_frame_psheets (f)
877 FRAME_PTR f;
878{
879 struct x_display *x = f->display.x;
880
881 int menubar_changed;
882
883 menubar_changed = (x->menubar_widget
884 && !XtIsManaged (x->menubar_widget));
885
886 if (! (menubar_changed))
887 return;
888
889 BLOCK_INPUT;
890 XawPanedSetRefigureMode (x->column_widget, 0);
891
892 /* the order in which children are managed is the top to
893 bottom order in which they are displayed in the paned window.
894 First, remove the text-area widget.
895 */
896 XtUnmanageChild (x->edit_widget);
897
898 /* remove the menubar that is there now, and put up the menubar that
899 should be there.
900 */
901 if (menubar_changed)
902 {
903 XtManageChild (x->menubar_widget);
904 XtMapWidget (x->menubar_widget);
905 XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0);
906 }
907
908
909 /* Re-manage the text-area widget */
910 XtManageChild (x->edit_widget);
911
912 /* and now thrash the sizes */
913 XawPanedSetRefigureMode (x->column_widget, 1);
914 UNBLOCK_INPUT;
915}
916
917void
918set_frame_menubar (f)
919 FRAME_PTR f;
920{
921 Widget menubar_widget = f->display.x->menubar_widget;
922 int id = (int) f;
923 Lisp_Object tail;
924 widget_value *wv, *save_wv, *first_wv, *prev_wv = 0;
925
926 BLOCK_INPUT;
927
928 wv = malloc_widget_value ();
929 wv->name = "menubar";
930 wv->value = 0;
931 wv->enabled = 1;
932 save_wv = first_wv = wv;
933
934
935 for (tail = FRAME_MENU_BAR_ITEMS (f); CONSP (tail); tail = XCONS (tail)->cdr)
936 {
937 Lisp_Object string;
938
939 string = Fcar (Fcdr (Fcar (tail)));
940
941 wv = malloc_widget_value ();
942 if (prev_wv)
943 prev_wv->next = wv;
944 else
945 save_wv->contents = wv;
946 wv->name = XSTRING (string)->data;
947 wv->value = 0;
948 wv->enabled = 1;
949 prev_wv = wv;
950 }
951
952 if (menubar_widget)
953 lw_modify_all_widgets (id, first_wv, False);
954 else
955 {
956 menubar_widget = lw_create_widget ("menubar", "menubar",
957 id, first_wv,
958 f->display.x->column_widget,
959 0, 0,
960 0, 0);
961 f->display.x->menubar_widget = menubar_widget;
962 XtVaSetValues (menubar_widget,
963 XtNshowGrip, 0,
964 XtNresizeToPreferred, 1,
965 XtNallowResize, 1,
966 0);
967 }
968
969 free_menubar_widget_value_tree (first_wv);
970
971 update_one_frame_psheets (f);
972
973 UNBLOCK_INPUT;
974}
85f487d1
FP
975
976void
977free_frame_menubar (f)
978 FRAME_PTR f;
979{
980 Widget menubar_widget;
981 int id;
982
983 menubar_widget = f->display.x->menubar_widget;
984 id = (int) f;
985
986 if (menubar_widget)
987 {
988 BLOCK_INPUT;
989 lw_destroy_all_widgets (id);
990 UNBLOCK_INPUT;
991 }
992}
18686d47 993\f
78589e07
RS
994/* Nonzero if position X, Y relative to inside of frame F
995 is in some other menu bar item. */
dcfdbac7 996
78589e07
RS
997static int this_menu_bar_item_beg;
998static int this_menu_bar_item_end;
999
1000static int
1001other_menu_bar_item_p (f, x, y)
1002 FRAME_PTR f;
1003 int x, y;
1004{
1005 return (y >= 0
1006 && y < f->display.x->menubar_widget->core.height
1007 && x >= 0
1008 && x < f->display.x->menubar_widget->core.width
1009 && (x >= this_menu_bar_item_end
1010 || x < this_menu_bar_item_beg));
1011}
1012
1013/* Unread a button-press event in the menu bar of frame F
1014 at x position XPOS relative to the inside of the frame. */
1015
1016static void
1017unread_menu_bar_button (f, xpos)
1018 FRAME_PTR f;
1019 int xpos;
1020{
1021 XEvent event;
1022
1023 event.type = ButtonPress;
1024 event.xbutton.display = x_current_display;
1025 event.xbutton.serial = 0;
1026 event.xbutton.send_event = 0;
1027 event.xbutton.time = CurrentTime;
1028 event.xbutton.button = Button1;
1029 event.xbutton.window = XtWindow (f->display.x->menubar_widget);
1030 event.xbutton.x = xpos;
1031 XPutBackEvent (XDISPLAY &event);
1032}
1033
1034/* If the mouse has moved to another menu bar item,
1035 return 1 and unread a button press event for that item.
1036 Otherwise return 0. */
1037
1038static int
1039check_mouse_other_menu_bar (f)
1040 FRAME_PTR f;
1041{
1042 FRAME_PTR new_f;
1043 Lisp_Object bar_window;
1044 int part;
1045 Lisp_Object x, y;
1046 unsigned long time;
1047
1048 (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
1049
1050 if (f == new_f && other_menu_bar_item_p (f, x, y))
1051 {
1052 unread_menu_bar_button (f, x);
1053 return 1;
1054 }
1055
1056 return 0;
1057}
1058#endif /* USE_X_TOOLKIT */
1059\f
1060/* xmenu_show actually displays a menu using the panes and items in menu_items
1061 and returns the value selected from it.
1062 There are two versions of xmenu_show, one for Xt and one for Xlib.
1063 Both assume input is blocked by the caller. */
1064
1065/* F is the frame the menu is for.
1066 X and Y are the frame-relative specified position,
1067 relative to the inside upper left corner of the frame F.
1068 MENUBARP is 1 if the click that asked for this menu came from the menu bar.
1069 KEYMAPS is 1 if this menu was specified with keymaps;
1070 in that case, we return a list containing the chosen item's value
1071 and perhaps also the pane's prefix.
1072 TITLE is the specified menu title.
1073 ERROR is a place to store an error message string in case of failure.
1074 (We return nil on failure, but the value doesn't actually matter.) */
18686d47
RS
1075
1076#ifdef USE_X_TOOLKIT
18686d47 1077
78589e07
RS
1078static Lisp_Object
1079xmenu_show (f, x, y, menubarp, keymaps, title, error)
18686d47 1080 FRAME_PTR f;
18686d47
RS
1081 int x;
1082 int y;
1083 int menubarp;
78589e07
RS
1084 int keymaps;
1085 Lisp_Object title;
1086 char **error;
18686d47 1087{
78589e07
RS
1088 int i;
1089 int menu_id;
18686d47 1090 Widget menu;
78589e07 1091 XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
18686d47 1092
78589e07
RS
1093 /* This is the menu bar item (if any) that led to this menu. */
1094 widget_value *menubar_item = 0;
1095
1096 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
1097
1098 /* Define a queue to save up for later unreading
1099 all X events that don't pertain to the menu. */
1100 struct event_queue
18686d47
RS
1101 {
1102 XEvent event;
78589e07
RS
1103 struct event_queue *next;
1104 };
18686d47 1105
78589e07
RS
1106 struct event_queue *queue = NULL;
1107 struct event_queue *queue_tmp;
1108
1109 *error = NULL;
1110
1111 this_menu_bar_item_beg = -1;
1112 this_menu_bar_item_end = -1;
1113
1114 /* Figure out which menu bar item, if any, this menu is for. */
1115 if (menubarp)
1116 {
1117 int xbeg;
1118 int xend = 0;
1119
1120 for (menubar_item = menubar->menu.old_stack[0]->contents;
1121 menubar_item;
1122 menubar_item = menubar_item->next)
1123 {
1124 xbeg = xend;
1125 xend += (string_width (menubar, menubar_item->name)
1126 + 2 * (menubar->menu.horizontal_spacing
1127 + menubar->menu.shadow_thickness));
1128 if (x < xend)
1129 {
1130 x = xbeg + 4;
1131 y = 0;
1132 /* Arrange to show a different menu if we move in the menu bar
1133 to a different item. */
1134 this_menu_bar_item_beg = xbeg;
1135 this_menu_bar_item_end = xend;
1136 break;
1137 }
1138 }
1139 }
1140 if (menubar_item == 0)
1141 menubarp = 0;
1142
1143 /* Offset the coordinates to root-relative. */
1144 x += (f->display.x->widget->core.x
1145 + f->display.x->widget->core.border_width);
1146 y += (f->display.x->widget->core.y
1147 + f->display.x->widget->core.border_width
1148 + f->display.x->menubar_widget->core.height);
63c414df 1149
78589e07
RS
1150 /* Create a tree of widget_value objects
1151 representing the panes and their items. */
1152 wv = malloc_widget_value ();
1153 wv->name = "menu";
1154 wv->value = 0;
1155 wv->enabled = 1;
1156 first_wv = wv;
1157
1158 /* Loop over all panes and items, filling in the tree. */
1159 i = 0;
1160 while (i < menu_items_used)
1161 {
1162 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1163 {
1164 /* Create a new pane. */
1165 Lisp_Object pane_name, prefix;
1166 char *pane_string;
1167 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1168 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1169 pane_string = (NILP (pane_name)
1170 ? "" : (char *) XSTRING (pane_name)->data);
1171 /* If there is just one pane, put all its items directly
1172 under the top-level menu. */
1173 if (menu_items_n_panes == 1)
1174 pane_string = "";
1175
1176 /* If the pane has a meaningful name,
1177 make the pane a top-level menu item
1178 with its items as a submenu beneath it. */
1179 if (strcmp (pane_string, ""))
1180 {
1181 wv = malloc_widget_value ();
1182 if (save_wv)
1183 save_wv->next = wv;
1184 else
1185 first_wv->contents = wv;
1186 wv->name = pane_string;
1187 if (keymaps && !NILP (prefix))
1188 wv->name++;
1189 wv->value = 0;
1190 wv->enabled = 1;
1191 }
1192 save_wv = wv;
1193 prev_wv = 0;
1194 i += MENU_ITEMS_PANE_LENGTH;
1195 }
1196 else
1197 {
1198 /* Create a new item within current pane. */
1199 Lisp_Object item_name, enable, descrip;
1200 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
1201 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
1202 descrip
1203 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
1204
1205 wv = malloc_widget_value ();
1206 if (prev_wv)
1207 prev_wv->next = wv;
1208 else
1209 save_wv->contents = wv;
1210 wv->name = XSTRING (item_name)->data;
1211 if (!NILP (descrip))
1212 wv->key = XSTRING (descrip)->data;
1213 wv->value = 0;
1214 wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
1215 wv->enabled = !NILP (enable);
1216 prev_wv = wv;
1217
1218 i += MENU_ITEMS_ITEM_LENGTH;
1219 }
1220 }
1221
1222 /* Actually create the menu. */
18686d47 1223 menu_id = ++popup_id_tick;
78589e07 1224 menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
18686d47
RS
1225 f->display.x->widget, 1, 0,
1226 popup_selection_callback, popup_down_callback);
78589e07
RS
1227 /* Free the widget_value objects we used to specify the contents. */
1228 free_menubar_widget_value_tree (first_wv);
1229
1230 /* No selection has been chosen yet. */
1231 menu_item_selection = 0;
1232
1233 /* If the mouse moves out of the menu before we show the menu,
1234 don't show it at all. */
1235 if (check_mouse_other_menu_bar (f))
1236 {
1237 lw_destroy_all_widgets (menu_id);
1238 return Qnil;
1239 }
1240
18686d47 1241
78589e07
RS
1242 /* Highlight the menu bar item (if any) that led to this menu. */
1243 if (menubarp)
1244 {
1245 menubar_item->call_data = (XtPointer) 1;
1246 dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
1247 }
18686d47 1248
78589e07 1249 /* Display the menu. */
18686d47
RS
1250 {
1251 XButtonPressedEvent dummy;
1252 XlwMenuWidget mw;
1253
78589e07 1254 mw = (XlwMenuWidget) ((CompositeWidget)menu)->composite.children[0];
18686d47
RS
1255
1256 dummy.type = ButtonPress;
1257 dummy.serial = 0;
1258 dummy.send_event = 0;
1259 dummy.display = XtDisplay (menu);
1260 dummy.window = XtWindow (XtParent (menu));
1261 dummy.time = CurrentTime;
1262 dummy.button = 0;
1263 dummy.x_root = x;
1264 dummy.y_root = y;
1265
78589e07 1266 /* We activate directly the lucid implementation. */
18686d47
RS
1267 pop_up_menu (mw, &dummy);
1268 }
1269
78589e07
RS
1270 /* Check again whether the mouse has moved to another menu bar item. */
1271 if (check_mouse_other_menu_bar (f))
47e8f9a3 1272 {
78589e07
RS
1273 /* The mouse moved into a different menu bar item.
1274 We should bring up that item's menu instead.
1275 First pop down this menu. */
1276 XtUngrabPointer ((Widget)
1277 ((XlwMenuWidget)
1278 ((CompositeWidget)menu)->composite.children[0]),
1279 CurrentTime);
1280 lw_destroy_all_widgets (menu_id);
1281 goto pop_down;
47e8f9a3
FP
1282 }
1283
78589e07 1284 /* Process events that apply to the menu. */
18686d47
RS
1285 while (1)
1286 {
18686d47 1287 XEvent event;
78589e07 1288
18686d47
RS
1289 XtAppNextEvent (Xt_app_con, &event);
1290 if (event.type == ButtonRelease)
1291 {
1292 XtDispatchEvent (&event);
1293 break;
1294 }
78589e07
RS
1295 else if (event.type == Expose)
1296 process_expose_from_menu (event);
1297 else if (event.type == MotionNotify)
1298 {
1299 int event_x = (event.xmotion.x_root
1300 - (f->display.x->widget->core.x
1301 + f->display.x->widget->core.border_width));
1302 int event_y = (event.xmotion.y_root
1303 - (f->display.x->widget->core.y
1304 + f->display.x->widget->core.border_width));
1305
1306 if (other_menu_bar_item_p (f, event_x, event_y))
1307 {
1308 /* The mouse moved into a different menu bar item.
1309 We should bring up that item's menu instead.
1310 First pop down this menu. */
1311 XtUngrabPointer ((Widget)
1312 ((XlwMenuWidget)
1313 ((CompositeWidget)menu)->composite.children[0]),
1314 event.xbutton.time);
1315 lw_destroy_all_widgets (menu_id);
1316
1317 /* Put back an event that will bring up the other item's menu. */
1318 unread_menu_bar_button (f, event_x);
1319 /* Don't let us select anything in this case. */
1320 menu_item_selection = 0;
1321 break;
1322 }
1323 }
47e8f9a3 1324
18686d47 1325 XtDispatchEvent (&event);
0f7159e3 1326 if (XtWindowToWidget(event.xany.window) != menu)
78589e07 1327 {
0f7159e3
FP
1328 queue_tmp = (struct event_queue *) malloc (sizeof (struct event_queue));
1329
1330 if (queue_tmp != NULL)
1331 {
1332 queue_tmp->event = event;
1333 queue_tmp->next = queue;
1334 queue = queue_tmp;
1335 }
78589e07 1336 }
18686d47 1337 }
78589e07
RS
1338
1339 pop_down:
1340 /* Unhighlight the menu bar item (if any) that led to this menu. */
18686d47
RS
1341 if (menubarp)
1342 {
78589e07 1343 menubar_item->call_data = (XtPointer) 0;
399703f1 1344 dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
18686d47
RS
1345 }
1346
78589e07
RS
1347 /* Make sure the menu disappears. */
1348 lw_destroy_all_widgets (menu_id);
1349
1350 /* Unread any events that we got but did not handle. */
1351 while (queue != NULL)
18686d47 1352 {
78589e07
RS
1353 queue_tmp = queue;
1354 XPutBackEvent (XDISPLAY &queue_tmp->event);
1355 queue = queue_tmp->next;
1356 free ((char *)queue_tmp);
18686d47
RS
1357 }
1358
78589e07
RS
1359 /* Find the selected item, and its pane, to return
1360 the proper value. */
1361 if (menu_item_selection != 0)
1362 {
1363 Lisp_Object prefix;
1364
1365 prefix = Qnil;
1366 i = 0;
1367 while (i < menu_items_used)
1368 {
1369 Lisp_Object entry;
18686d47 1370
78589e07
RS
1371 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1372 {
1373 prefix
1374 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1375 i += MENU_ITEMS_PANE_LENGTH;
1376 }
1377 else
1378 {
1379 entry
1380 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
1381 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
1382 {
1383 if (keymaps != 0)
1384 {
1385 entry = Fcons (entry, Qnil);
1386 if (!NILP (prefix))
1387 entry = Fcons (prefix, entry);
1388 }
1389 return entry;
1390 }
1391 i += MENU_ITEMS_ITEM_LENGTH;
1392 }
1393 }
1394 }
1395
1396 return Qnil;
18686d47
RS
1397}
1398
1399#else /* not USE_X_TOOLKIT */
78589e07
RS
1400
1401static Lisp_Object
1402xmenu_show (f, x, y, menubarp, keymaps, title, error)
1403 FRAME_PTR f;
1404 int x, y;
1405 int keymaps;
1406 int menubarp;
1407 Lisp_Object title;
1408 char **error;
dcfdbac7 1409{
78589e07
RS
1410 Window root;
1411 XMenu *menu;
1412 int pane, selidx, lpane, status;
1413 Lisp_Object entry, pane_prefix;
dcfdbac7
JB
1414 char *datap;
1415 int ulx, uly, width, height;
1416 int dispwidth, dispheight;
78589e07
RS
1417 int i;
1418 int dummy_int;
1419 unsigned int dummy_uint;
088831f6 1420
07a675b7 1421 *error = 0;
78589e07
RS
1422 if (menu_items_n_panes == 0)
1423 return Qnil;
088831f6 1424
78589e07
RS
1425 /* Figure out which root window F is on. */
1426 XGetGeometry (x_current_display, FRAME_X_WINDOW (f), &root,
1427 &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
1428 &dummy_uint, &dummy_uint);
18686d47 1429
78589e07
RS
1430 /* Make the menu on that window. */
1431 menu = XMenuCreate (XDISPLAY root, "emacs");
1432 if (menu == NULL)
dcfdbac7
JB
1433 {
1434 *error = "Can't create menu";
78589e07 1435 return Qnil;
dcfdbac7 1436 }
78589e07
RS
1437
1438 /* Adjust coordinates to relative to the outer (window manager) window. */
1439#ifdef HAVE_X11
1440 {
1441 Window child;
1442 int win_x = 0, win_y = 0;
1443
1444 /* Find the position of the outside upper-left corner of
1445 the inner window, with respect to the outer window. */
1446 if (f->display.x->parent_desc != ROOT_WINDOW)
1447 {
1448 BLOCK_INPUT;
1449 XTranslateCoordinates (x_current_display,
1450
1451 /* From-window, to-window. */
1452 f->display.x->window_desc,
1453 f->display.x->parent_desc,
1454
1455 /* From-position, to-position. */
1456 0, 0, &win_x, &win_y,
1457
1458 /* Child of window. */
1459 &child);
1460 UNBLOCK_INPUT;
1461 x += win_x;
1462 y += win_y;
1463 }
1464 }
1465#endif /* HAVE_X11 */
1466
1467 /* Adjust coordinates to be root-window-relative. */
1468 x += f->display.x->left_pos;
1469 y += f->display.x->top_pos;
18686d47 1470
78589e07
RS
1471 /* Create all the necessary panes and their items. */
1472 i = 0;
1473 while (i < menu_items_used)
dcfdbac7 1474 {
78589e07 1475 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
dcfdbac7 1476 {
78589e07
RS
1477 /* Create a new pane. */
1478 Lisp_Object pane_name, prefix;
1479 char *pane_string;
1480
1481 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1482 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1483 pane_string = (NILP (pane_name)
1484 ? "" : (char *) XSTRING (pane_name)->data);
1485 if (keymaps && !NILP (prefix))
1486 pane_string++;
1487
1488 lpane = XMenuAddPane (XDISPLAY menu, pane_string, TRUE);
1489 if (lpane == XM_FAILURE)
1490 {
1491 XMenuDestroy (XDISPLAY menu);
1492 *error = "Can't create pane";
1493 return Qnil;
1494 }
1495 i += MENU_ITEMS_PANE_LENGTH;
dcfdbac7 1496 }
78589e07 1497 else
dcfdbac7 1498 {
78589e07
RS
1499 /* Create a new item within current pane. */
1500 Lisp_Object item_name, enable, descrip;
1501
1502 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
1503 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
1504 descrip
1505 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
1506 if (!NILP (descrip))
1507 item_name = concat2 (item_name, descrip);
1508
1509 if (XMenuAddSelection (XDISPLAY menu, lpane, 0,
1510 XSTRING (item_name)->data,
1511 !NILP (enable))
dcfdbac7
JB
1512 == XM_FAILURE)
1513 {
78589e07 1514 XMenuDestroy (XDISPLAY menu);
dcfdbac7 1515 *error = "Can't add selection to menu";
78589e07 1516 return Qnil;
dcfdbac7 1517 }
78589e07 1518 i += MENU_ITEMS_ITEM_LENGTH;
dcfdbac7
JB
1519 }
1520 }
78589e07
RS
1521
1522 /* All set and ready to fly. */
1523 XMenuRecompute (XDISPLAY menu);
dcfdbac7
JB
1524 dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
1525 dispheight = DisplayHeight (x_current_display, XDefaultScreen (x_current_display));
78589e07
RS
1526 x = min (x, dispwidth);
1527 y = min (y, dispheight);
1528 x = max (x, 1);
1529 y = max (y, 1);
1530 XMenuLocate (XDISPLAY menu, 0, 0, x, y,
dcfdbac7
JB
1531 &ulx, &uly, &width, &height);
1532 if (ulx+width > dispwidth)
1533 {
78589e07 1534 x -= (ulx + width) - dispwidth;
dcfdbac7
JB
1535 ulx = dispwidth - width;
1536 }
1537 if (uly+height > dispheight)
1538 {
78589e07 1539 y -= (uly + height) - dispheight;
dcfdbac7
JB
1540 uly = dispheight - height;
1541 }
78589e07
RS
1542 if (ulx < 0) x -= ulx;
1543 if (uly < 0) y -= uly;
dcfdbac7 1544
78589e07
RS
1545 XMenuSetFreeze (menu, TRUE);
1546 pane = selidx = 0;
dcfdbac7 1547
78589e07
RS
1548 status = XMenuActivate (XDISPLAY menu, &pane, &selidx,
1549 x, y, ButtonReleaseMask, &datap);
dcfdbac7
JB
1550 switch (status)
1551 {
1552 case XM_SUCCESS:
1553#ifdef XDEBUG
1554 fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
1555#endif
fa6d54d9 1556
78589e07
RS
1557 /* Find the item number SELIDX in pane number PANE. */
1558 i = 0;
1559 while (i < menu_items_used)
fa6d54d9 1560 {
78589e07 1561 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
088831f6 1562 {
78589e07
RS
1563 if (pane == 0)
1564 pane_prefix
1565 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1566 pane--;
1567 i += MENU_ITEMS_PANE_LENGTH;
088831f6 1568 }
78589e07 1569 else
ab6ee1a0 1570 {
78589e07 1571 if (pane == -1)
ab6ee1a0 1572 {
78589e07 1573 if (selidx == 0)
ab6ee1a0 1574 {
78589e07
RS
1575 entry
1576 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
1577 if (keymaps != 0)
ab6ee1a0 1578 {
78589e07
RS
1579 entry = Fcons (entry, Qnil);
1580 if (!NILP (pane_prefix))
1581 entry = Fcons (pane_prefix, entry);
ab6ee1a0 1582 }
78589e07 1583 break;
ab6ee1a0 1584 }
78589e07 1585 selidx--;
ab6ee1a0 1586 }
78589e07 1587 i += MENU_ITEMS_ITEM_LENGTH;
ab6ee1a0
RS
1588 }
1589 }
78589e07 1590 break;
dcfdbac7 1591
78589e07
RS
1592 case XM_FAILURE:
1593 XMenuDestroy (XDISPLAY menu);
1594 *error = "Can't activate menu";
1595 case XM_IA_SELECT:
1596 case XM_NO_SELECT:
1597 entry = Qnil;
1598 break;
dcfdbac7 1599 }
78589e07
RS
1600 XMenuDestroy (XDISPLAY menu);
1601 return entry;
dcfdbac7 1602}
78589e07 1603#endif /* not USE_X_TOOLKIT */
088831f6 1604\f
78589e07 1605syms_of_xmenu ()
dcfdbac7 1606{
78589e07
RS
1607 staticpro (&menu_items);
1608 menu_items = Qnil;
dcfdbac7 1609
78589e07
RS
1610 popup_id_tick = (1<<16);
1611 defsubr (&Sx_popup_menu);
dcfdbac7 1612}