(update_one_frame_psheets): Call EmacsFrameSetCharSize to
[bpt/emacs.git] / src / xmenu.c
1 /* X Communication module for terminals which understand the X protocol.
2 Copyright (C) 1986, 1988, 1993, 1994 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the 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
27 /* Modified by Fred Pierresteguy on December 93
28 to make the popup menus and menubar use the Xt. */
29
30 /* Rewritten for clarity and GC protection by rms in Feb 94. */
31
32 #include <stdio.h>
33
34 /* On 4.3 this loses if it comes after xterm.h. */
35 #include <signal.h>
36 #include <config.h>
37 #include "lisp.h"
38 #include "termhooks.h"
39 #include "frame.h"
40 #include "window.h"
41 #include "keyboard.h"
42 #include "blockinput.h"
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
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
72 #define min(x,y) (((x) < (y)) ? (x) : (y))
73 #define max(x,y) (((x) > (y)) ? (x) : (y))
74
75 #ifndef TRUE
76 #define TRUE 1
77 #define FALSE 0
78 #endif /* no TRUE */
79
80 #ifdef HAVE_X11
81 extern Display *x_current_display;
82 #else
83 #define ButtonReleaseMask ButtonReleased
84 #endif /* not HAVE_X11 */
85
86 /* We need a unique id for each popup menu and dialog box. */
87 static unsigned int popup_id_tick;
88
89 extern Lisp_Object Qmenu_enable;
90 extern Lisp_Object Qmenu_bar;
91
92 #ifdef USE_X_TOOLKIT
93 extern void process_expose_from_menu ();
94 extern XtAppContext Xt_app_con;
95
96 static int string_width ();
97 #endif
98
99 static Lisp_Object xmenu_show ();
100 static void keymap_panes ();
101 static void single_keymap_panes ();
102 static void list_of_panes ();
103 static 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.
107
108 It describes the panes and items within the panes.
109
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.
115
116 Using a Lisp vector to hold this information while we decode it
117 takes care of protecting all the data from GC. */
118
119 #define MENU_ITEMS_PANE_NAME 1
120 #define MENU_ITEMS_PANE_PREFIX 2
121 #define MENU_ITEMS_PANE_LENGTH 3
122
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
128
129 static Lisp_Object menu_items;
130
131 /* Number of slots currently allocated in menu_items. */
132 static int menu_items_allocated;
133
134 /* This is the index in menu_items of the first empty slot. */
135 static int menu_items_used;
136
137 /* The number of panes currently recorded in menu_items. */
138 static int menu_items_n_panes;
139
140 /* Initialize the menu_items structure if we haven't already done so.
141 Also mark it as currently empty. */
142
143 static void
144 init_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);
150 }
151
152 menu_items_used = 0;
153 menu_items_n_panes = 0;
154 }
155
156 /* Call at the end of generating the data in menu_items.
157 This fills in the number of items in the last pane. */
158
159 static void
160 finish_menu_items ()
161 {
162 }
163
164 /* Call when finished using the data for the current menu
165 in menu_items. */
166
167 static void
168 discard_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 }
178
179 /* Start a new menu pane in menu_items..
180 NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
181
182 static void
183 push_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 }
197
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 }
203
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). */
209
210 static void
211 push_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)
215 {
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 }
225
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. */
238
239 static Lisp_Object
240 menu_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;
255
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;
260
261 def = Fcdr (item1);
262
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)))
268 {
269 cachelist = XCONS (def)->car;
270 def = XCONS (def)->cdr;
271 savedkey = XCONS (cachelist)->car;
272 descrip = XCONS (cachelist)->cdr;
273 }
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)))
284 {
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 }
297 }
298
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)
303 {
304 XCONS (cachelist)->car = savedkey;
305 XCONS (cachelist)->cdr = descrip;
306 }
307
308 *descrip_ptr = descrip;
309 return def;
310 }
311
312 /* This is used as the handler when calling internal_condition_case_1. */
313
314 static Lisp_Object
315 menu_item_enabled_p_1 (arg)
316 Lisp_Object arg;
317 {
318 return Qnil;
319 }
320
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
324 static Lisp_Object
325 menu_item_enabled_p (def)
326 Lisp_Object def;
327 {
328 Lisp_Object enabled, tem;
329
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. */
347
348 static void
349 keymap_panes (keymaps, nmaps)
350 Lisp_Object *keymaps;
351 int nmaps;
352 {
353 int mapno;
354
355 init_menu_items ();
356
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
371 static void
372 single_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)
386 {
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)
391 {
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 }
473 }
474 }
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 }
487 }
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. */
492
493 static void
494 list_of_panes (menu)
495 Lisp_Object menu;
496 {
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
518 static void
519 list_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
539 DEFUN ("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\
541 POSITION is a position specification. This is either a mouse button event\n\
542 or a list ((XOFFSET YOFFSET) WINDOW)\n\
543 where XOFFSET and YOFFSET are positions in characters from the top left\n\
544 corner of WINDOW's frame. (WINDOW may be a frame object instead of a window.)\n\
545 This controls the position of the center of the first line\n\
546 in the first pane of the menu, not the top left of the menu as a whole.\n\
547 If POSITION is t, it means to use the current mouse position.\n\
548 \n\
549 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.\n\
550 The menu items come from key bindings that have a menu string as well as\n\
551 a 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\
553 the keymap as a top-level element.\n\n\
554 You can also use a list of keymaps as MENU.\n\
555 Then each keymap makes a separate pane.\n\
556 When MENU is a keymap or a list of keymaps, the return value\n\
557 is a list of events.\n\n\
558 Alternatively, you can specify a menu of multiple panes\n\
559 with a list of the form (TITLE PANE1 PANE2...),\n\
560 where each pane is a list of form (TITLE ITEM1 ITEM2...).\n\
561 Each ITEM is normally a cons cell (STRING . VALUE);\n\
562 but a string can appear as an item--that makes a nonselectable line\n\
563 in the menu.\n\
564 With this form of menu, the return value is VALUE from the chosen item.\n\
565 \n\
566 If POSITION is nil, don't display the menu at all, just precalculate the\n\
567 cached information about equivalent key sequences.")
568 (position, menu)
569 Lisp_Object position, menu;
570 {
571 int number_of_panes, panes;
572 Lisp_Object keymap, tem;
573 int xpos, ypos;
574 Lisp_Object title;
575 char *error_name;
576 Lisp_Object selection;
577 int i, j;
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. */
659
660 keymap = Fkeymapp (menu);
661 tem = Qnil;
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
670 /* Extract the detailed info to make one pane. */
671 keymap_panes (&menu, 1);
672
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);
676
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;
682 }
683 else if (!NILP (tem))
684 {
685 /* We were given a list of keymaps. */
686 int nmaps = XFASTINT (Flength (menu));
687 Lisp_Object *maps
688 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
689 int i;
690
691 title = Qnil;
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 {
697 Lisp_Object prompt;
698
699 maps[i++] = keymap = get_keymap (Fcar (tem));
700
701 prompt = map_prompt (keymap);
702 if (NILP (title) && !NILP (prompt))
703 title = prompt;
704 }
705
706 /* Extract the detailed info to make one pane. */
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;
714 }
715 else
716 {
717 /* We were given an old-fashioned menu. */
718 title = Fcar (menu);
719 CHECK_STRING (title, 1);
720
721 list_of_panes (Fcdr (menu));
722
723 keymaps = 0;
724 }
725
726 if (NILP (position))
727 {
728 discard_menu_items ();
729 UNGCPRO;
730 return Qnil;
731 }
732
733 /* Display them in a menu. */
734 BLOCK_INPUT;
735
736 selection = xmenu_show (f, xpos, ypos, menubarp,
737 keymaps, title, &error_name);
738 UNBLOCK_INPUT;
739
740 discard_menu_items ();
741
742 UNGCPRO;
743
744 if (error_name) error (error_name);
745 return selection;
746 }
747 \f
748 #ifdef USE_X_TOOLKIT
749
750 static void
751 dispatch_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
770 static int
771 string_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
782 static int
783 event_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
796 /* Return the menu bar key which corresponds to event EVENT in frame F. */
797
798 Lisp_Object
799 map_event_to_object (event, f)
800 struct input_event *event;
801 FRAME_PTR f;
802 {
803 int i,j, string_w;
804 window_state* ws;
805 XlwMenuWidget mw = (XlwMenuWidget) f->display.x->menubar_widget;
806 widget_value *val;
807
808
809 string_w = 0;
810 /* Find the window */
811 for (val = mw->menu.old_stack [0]->contents; val; val = val->next)
812 {
813 ws = &mw->menu.windows [0];
814 if (ws && event_is_in_menu_item (mw, event, val->name, &string_w))
815 {
816 Lisp_Object items;
817 int i;
818
819 items = FRAME_MENU_BAR_ITEMS (f);
820
821 for (i = 0; i < XVECTOR (items)->size; i += 3)
822 {
823 Lisp_Object pos, string, item;
824 item = XVECTOR (items)->contents[i];
825 string = XVECTOR (items)->contents[i + 1];
826 pos = XVECTOR (items)->contents[i + 2];
827 if (NILP (string))
828 break;
829
830 if (!strcmp (val->name, XSTRING (string)->data))
831 return item;
832 }
833 }
834 }
835 return Qnil;
836 }
837
838 static Lisp_Object *menu_item_selection;
839
840 static void
841 popup_selection_callback (widget, id, client_data)
842 Widget widget;
843 LWLIB_ID id;
844 XtPointer client_data;
845 {
846 menu_item_selection = (Lisp_Object *) client_data;
847 }
848
849 static void
850 popup_down_callback (widget, id, client_data)
851 Widget widget;
852 LWLIB_ID id;
853 XtPointer client_data;
854 {
855 BLOCK_INPUT;
856 lw_destroy_all_widgets (id);
857 UNBLOCK_INPUT;
858 }
859
860 /* This recursively calls free_widget_value() on the tree of widgets.
861 It must free all data that was malloc'ed for these widget_values.
862 In Emacs, many slots are pointers into the data of Lisp_Strings, and
863 must be left alone. */
864
865 void
866 free_menubar_widget_value_tree (wv)
867 widget_value *wv;
868 {
869 if (! wv) return;
870
871 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
872
873 if (wv->contents && (wv->contents != (widget_value*)1))
874 {
875 free_menubar_widget_value_tree (wv->contents);
876 wv->contents = (widget_value *) 0xDEADBEEF;
877 }
878 if (wv->next)
879 {
880 free_menubar_widget_value_tree (wv->next);
881 wv->next = (widget_value *) 0xDEADBEEF;
882 }
883 BLOCK_INPUT;
884 free_widget_value (wv);
885 UNBLOCK_INPUT;
886 }
887
888 extern void EmacsFrameSetCharSize ();
889
890 static void
891 update_one_frame_psheets (f)
892 FRAME_PTR f;
893 {
894 struct x_display *x = f->display.x;
895 int columns, rows;
896 int menubar_changed;
897
898 menubar_changed = (x->menubar_widget
899 && !XtIsManaged (x->menubar_widget));
900
901 if (! (menubar_changed))
902 return;
903
904 BLOCK_INPUT;
905 /* Save the size of the frame because the pane widget doesn't accept to
906 resize itself. So force it. */
907 columns = f->width;
908 rows = f->height;
909
910
911 XawPanedSetRefigureMode (x->column_widget, 0);
912
913 /* the order in which children are managed is the top to
914 bottom order in which they are displayed in the paned window.
915 First, remove the text-area widget.
916 */
917 XtUnmanageChild (x->edit_widget);
918
919 /* remove the menubar that is there now, and put up the menubar that
920 should be there.
921 */
922 if (menubar_changed)
923 {
924 XtManageChild (x->menubar_widget);
925 XtMapWidget (x->menubar_widget);
926 XtVaSetValues (x->menubar_widget, XtNmappedWhenManaged, 1, 0);
927 }
928
929
930 /* Re-manage the text-area widget */
931 XtManageChild (x->edit_widget);
932
933 /* and now thrash the sizes */
934 XawPanedSetRefigureMode (x->column_widget, 1);
935
936 /* Force the pane widget to resize itself with the right values. */
937 EmacsFrameSetCharSize (x->edit_widget, columns, rows);
938
939 UNBLOCK_INPUT;
940 }
941
942 void
943 set_frame_menubar (f)
944 FRAME_PTR f;
945 {
946 Widget menubar_widget = f->display.x->menubar_widget;
947 int id = (int) f;
948 Lisp_Object tail, items;
949 widget_value *wv, *save_wv, *first_wv, *prev_wv = 0;
950 int i;
951
952 BLOCK_INPUT;
953
954 wv = malloc_widget_value ();
955 wv->name = "menubar";
956 wv->value = 0;
957 wv->enabled = 1;
958 save_wv = first_wv = wv;
959
960 items = FRAME_MENU_BAR_ITEMS (f);
961
962 for (i = 0; i < XVECTOR (items)->size; i += 3)
963 {
964 Lisp_Object string;
965
966 string = XVECTOR (items)->contents[i + 1];
967 if (NILP (string))
968 break;
969
970 wv = malloc_widget_value ();
971 if (prev_wv)
972 prev_wv->next = wv;
973 else
974 save_wv->contents = wv;
975 wv->name = XSTRING (string)->data;
976 wv->value = 0;
977 wv->enabled = 1;
978 prev_wv = wv;
979 }
980
981 if (menubar_widget)
982 lw_modify_all_widgets (id, first_wv, False);
983 else
984 {
985 menubar_widget = lw_create_widget ("menubar", "menubar",
986 id, first_wv,
987 f->display.x->column_widget,
988 0, 0,
989 0, 0);
990 f->display.x->menubar_widget = menubar_widget;
991 XtVaSetValues (menubar_widget,
992 XtNshowGrip, 0,
993 XtNresizeToPreferred, 1,
994 XtNallowResize, 1,
995 0);
996 }
997
998 free_menubar_widget_value_tree (first_wv);
999
1000 update_one_frame_psheets (f);
1001
1002 UNBLOCK_INPUT;
1003 }
1004
1005 void
1006 free_frame_menubar (f)
1007 FRAME_PTR f;
1008 {
1009 Widget menubar_widget;
1010 int id;
1011
1012 menubar_widget = f->display.x->menubar_widget;
1013 id = (int) f;
1014
1015 if (menubar_widget)
1016 {
1017 BLOCK_INPUT;
1018 lw_destroy_all_widgets (id);
1019 UNBLOCK_INPUT;
1020 }
1021 }
1022 \f
1023 /* Nonzero if position X, Y relative to inside of frame F
1024 is in some other menu bar item. */
1025
1026 static int this_menu_bar_item_beg;
1027 static int this_menu_bar_item_end;
1028
1029 static int
1030 other_menu_bar_item_p (f, x, y)
1031 FRAME_PTR f;
1032 int x, y;
1033 {
1034 return (y >= 0
1035 && y < f->display.x->menubar_widget->core.height
1036 && x >= 0
1037 && x < f->display.x->menubar_widget->core.width
1038 && (x >= this_menu_bar_item_end
1039 || x < this_menu_bar_item_beg));
1040 }
1041
1042 /* Unread a button-press event in the menu bar of frame F
1043 at x position XPOS relative to the inside of the frame. */
1044
1045 static void
1046 unread_menu_bar_button (f, xpos)
1047 FRAME_PTR f;
1048 int xpos;
1049 {
1050 XEvent event;
1051
1052 event.type = ButtonPress;
1053 event.xbutton.display = x_current_display;
1054 event.xbutton.serial = 0;
1055 event.xbutton.send_event = 0;
1056 event.xbutton.time = CurrentTime;
1057 event.xbutton.button = Button1;
1058 event.xbutton.window = XtWindow (f->display.x->menubar_widget);
1059 event.xbutton.x = xpos;
1060 XPutBackEvent (XDISPLAY &event);
1061 }
1062
1063 /* If the mouse has moved to another menu bar item,
1064 return 1 and unread a button press event for that item.
1065 Otherwise return 0. */
1066
1067 static int
1068 check_mouse_other_menu_bar (f)
1069 FRAME_PTR f;
1070 {
1071 FRAME_PTR new_f;
1072 Lisp_Object bar_window;
1073 int part;
1074 Lisp_Object x, y;
1075 unsigned long time;
1076
1077 (*mouse_position_hook) (&new_f, &bar_window, &part, &x, &y, &time);
1078
1079 if (f == new_f && other_menu_bar_item_p (f, x, y))
1080 {
1081 unread_menu_bar_button (f, x);
1082 return 1;
1083 }
1084
1085 return 0;
1086 }
1087 #endif /* USE_X_TOOLKIT */
1088 \f
1089 /* xmenu_show actually displays a menu using the panes and items in menu_items
1090 and returns the value selected from it.
1091 There are two versions of xmenu_show, one for Xt and one for Xlib.
1092 Both assume input is blocked by the caller. */
1093
1094 /* F is the frame the menu is for.
1095 X and Y are the frame-relative specified position,
1096 relative to the inside upper left corner of the frame F.
1097 MENUBARP is 1 if the click that asked for this menu came from the menu bar.
1098 KEYMAPS is 1 if this menu was specified with keymaps;
1099 in that case, we return a list containing the chosen item's value
1100 and perhaps also the pane's prefix.
1101 TITLE is the specified menu title.
1102 ERROR is a place to store an error message string in case of failure.
1103 (We return nil on failure, but the value doesn't actually matter.) */
1104
1105 #ifdef USE_X_TOOLKIT
1106
1107 static Lisp_Object
1108 xmenu_show (f, x, y, menubarp, keymaps, title, error)
1109 FRAME_PTR f;
1110 int x;
1111 int y;
1112 int menubarp;
1113 int keymaps;
1114 Lisp_Object title;
1115 char **error;
1116 {
1117 int i;
1118 int menu_id;
1119 Widget menu;
1120 XlwMenuWidget menubar = (XlwMenuWidget) f->display.x->menubar_widget;
1121
1122 /* This is the menu bar item (if any) that led to this menu. */
1123 widget_value *menubar_item = 0;
1124
1125 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
1126
1127 /* Define a queue to save up for later unreading
1128 all X events that don't pertain to the menu. */
1129 struct event_queue
1130 {
1131 XEvent event;
1132 struct event_queue *next;
1133 };
1134
1135 struct event_queue *queue = NULL;
1136 struct event_queue *queue_tmp;
1137
1138 *error = NULL;
1139
1140 this_menu_bar_item_beg = -1;
1141 this_menu_bar_item_end = -1;
1142
1143 /* Figure out which menu bar item, if any, this menu is for. */
1144 if (menubarp)
1145 {
1146 int xbeg;
1147 int xend = 0;
1148
1149 for (menubar_item = menubar->menu.old_stack[0]->contents;
1150 menubar_item;
1151 menubar_item = menubar_item->next)
1152 {
1153 xbeg = xend;
1154 xend += (string_width (menubar, menubar_item->name)
1155 + 2 * (menubar->menu.horizontal_spacing
1156 + menubar->menu.shadow_thickness));
1157 if (x < xend)
1158 {
1159 x = xbeg + 4;
1160 y = 0;
1161 /* Arrange to show a different menu if we move in the menu bar
1162 to a different item. */
1163 this_menu_bar_item_beg = xbeg;
1164 this_menu_bar_item_end = xend;
1165 break;
1166 }
1167 }
1168 }
1169 if (menubar_item == 0)
1170 menubarp = 0;
1171
1172 /* Offset the coordinates to root-relative. */
1173 x += (f->display.x->widget->core.x
1174 + f->display.x->widget->core.border_width);
1175 y += (f->display.x->widget->core.y
1176 + f->display.x->widget->core.border_width
1177 + f->display.x->menubar_widget->core.height);
1178
1179 /* Create a tree of widget_value objects
1180 representing the panes and their items. */
1181 wv = malloc_widget_value ();
1182 wv->name = "menu";
1183 wv->value = 0;
1184 wv->enabled = 1;
1185 first_wv = wv;
1186
1187 /* Loop over all panes and items, filling in the tree. */
1188 i = 0;
1189 while (i < menu_items_used)
1190 {
1191 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1192 {
1193 /* Create a new pane. */
1194 Lisp_Object pane_name, prefix;
1195 char *pane_string;
1196 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1197 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1198 pane_string = (NILP (pane_name)
1199 ? "" : (char *) XSTRING (pane_name)->data);
1200 /* If there is just one pane, put all its items directly
1201 under the top-level menu. */
1202 if (menu_items_n_panes == 1)
1203 pane_string = "";
1204
1205 /* If the pane has a meaningful name,
1206 make the pane a top-level menu item
1207 with its items as a submenu beneath it. */
1208 if (strcmp (pane_string, ""))
1209 {
1210 wv = malloc_widget_value ();
1211 if (save_wv)
1212 save_wv->next = wv;
1213 else
1214 first_wv->contents = wv;
1215 wv->name = pane_string;
1216 if (keymaps && !NILP (prefix))
1217 wv->name++;
1218 wv->value = 0;
1219 wv->enabled = 1;
1220 }
1221 save_wv = wv;
1222 prev_wv = 0;
1223 i += MENU_ITEMS_PANE_LENGTH;
1224 }
1225 else
1226 {
1227 /* Create a new item within current pane. */
1228 Lisp_Object item_name, enable, descrip;
1229 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
1230 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
1231 descrip
1232 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
1233
1234 wv = malloc_widget_value ();
1235 if (prev_wv)
1236 prev_wv->next = wv;
1237 else
1238 save_wv->contents = wv;
1239 wv->name = XSTRING (item_name)->data;
1240 if (!NILP (descrip))
1241 wv->key = XSTRING (descrip)->data;
1242 wv->value = 0;
1243 wv->call_data = (void *) &XVECTOR (menu_items)->contents[i];
1244 wv->enabled = !NILP (enable);
1245 prev_wv = wv;
1246
1247 i += MENU_ITEMS_ITEM_LENGTH;
1248 }
1249 }
1250
1251 /* Actually create the menu. */
1252 menu_id = ++popup_id_tick;
1253 menu = lw_create_widget ("popup", first_wv->name, menu_id, first_wv,
1254 f->display.x->widget, 1, 0,
1255 popup_selection_callback, popup_down_callback);
1256 /* Free the widget_value objects we used to specify the contents. */
1257 free_menubar_widget_value_tree (first_wv);
1258
1259 /* No selection has been chosen yet. */
1260 menu_item_selection = 0;
1261
1262 /* If the mouse moves out of the menu before we show the menu,
1263 don't show it at all. */
1264 if (check_mouse_other_menu_bar (f))
1265 {
1266 lw_destroy_all_widgets (menu_id);
1267 return Qnil;
1268 }
1269
1270
1271 /* Highlight the menu bar item (if any) that led to this menu. */
1272 if (menubarp)
1273 {
1274 menubar_item->call_data = (XtPointer) 1;
1275 dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
1276 }
1277
1278 /* Display the menu. */
1279 {
1280 XButtonPressedEvent dummy;
1281 XlwMenuWidget mw;
1282
1283 mw = (XlwMenuWidget) ((CompositeWidget)menu)->composite.children[0];
1284
1285 dummy.type = ButtonPress;
1286 dummy.serial = 0;
1287 dummy.send_event = 0;
1288 dummy.display = XtDisplay (menu);
1289 dummy.window = XtWindow (XtParent (menu));
1290 dummy.time = CurrentTime;
1291 dummy.button = 0;
1292 dummy.x_root = x;
1293 dummy.y_root = y;
1294
1295 /* We activate directly the lucid implementation. */
1296 pop_up_menu (mw, &dummy);
1297 }
1298
1299 /* No need to check a second time since this is done in the XEvent loop.
1300 This slows done the execution. */
1301 #if 0
1302 /* Check again whether the mouse has moved to another menu bar item. */
1303 if (check_mouse_other_menu_bar (f))
1304 {
1305 /* The mouse moved into a different menu bar item.
1306 We should bring up that item's menu instead.
1307 First pop down this menu. */
1308 XtUngrabPointer ((Widget)
1309 ((XlwMenuWidget)
1310 ((CompositeWidget)menu)->composite.children[0]),
1311 CurrentTime);
1312 lw_destroy_all_widgets (menu_id);
1313 goto pop_down;
1314 }
1315 #endif
1316
1317 /* Process events that apply to the menu. */
1318 while (1)
1319 {
1320 XEvent event;
1321
1322 XtAppNextEvent (Xt_app_con, &event);
1323 if (event.type == ButtonRelease)
1324 {
1325 XtDispatchEvent (&event);
1326 break;
1327 }
1328 else if (event.type == Expose)
1329 process_expose_from_menu (event);
1330 else if (event.type == MotionNotify)
1331 {
1332 int event_x = (event.xmotion.x_root
1333 - (f->display.x->widget->core.x
1334 + f->display.x->widget->core.border_width));
1335 int event_y = (event.xmotion.y_root
1336 - (f->display.x->widget->core.y
1337 + f->display.x->widget->core.border_width));
1338
1339 if (other_menu_bar_item_p (f, event_x, event_y))
1340 {
1341 /* The mouse moved into a different menu bar item.
1342 We should bring up that item's menu instead.
1343 First pop down this menu. */
1344 XtUngrabPointer ((Widget)
1345 ((XlwMenuWidget)
1346 ((CompositeWidget)menu)->composite.children[0]),
1347 event.xbutton.time);
1348 lw_destroy_all_widgets (menu_id);
1349
1350 /* Put back an event that will bring up the other item's menu. */
1351 unread_menu_bar_button (f, event_x);
1352 /* Don't let us select anything in this case. */
1353 menu_item_selection = 0;
1354 break;
1355 }
1356 }
1357
1358 XtDispatchEvent (&event);
1359 if (XtWindowToWidget(XDISPLAY event.xany.window) != menu)
1360 {
1361 queue_tmp
1362 = (struct event_queue *) malloc (sizeof (struct event_queue));
1363
1364 if (queue_tmp != NULL)
1365 {
1366 queue_tmp->event = event;
1367 queue_tmp->next = queue;
1368 queue = queue_tmp;
1369 }
1370 }
1371 }
1372
1373 pop_down:
1374 /* Unhighlight the menu bar item (if any) that led to this menu. */
1375 if (menubarp)
1376 {
1377 menubar_item->call_data = (XtPointer) 0;
1378 dispatch_dummy_expose (f->display.x->menubar_widget, x, y);
1379 }
1380
1381 /* Make sure the menu disappears. */
1382 lw_destroy_all_widgets (menu_id);
1383
1384 /* Unread any events that we got but did not handle. */
1385 while (queue != NULL)
1386 {
1387 queue_tmp = queue;
1388 XPutBackEvent (XDISPLAY &queue_tmp->event);
1389 queue = queue_tmp->next;
1390 free ((char *)queue_tmp);
1391 }
1392
1393 /* Find the selected item, and its pane, to return
1394 the proper value. */
1395 if (menu_item_selection != 0)
1396 {
1397 Lisp_Object prefix;
1398
1399 prefix = Qnil;
1400 i = 0;
1401 while (i < menu_items_used)
1402 {
1403 Lisp_Object entry;
1404
1405 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1406 {
1407 prefix
1408 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1409 i += MENU_ITEMS_PANE_LENGTH;
1410 }
1411 else
1412 {
1413 entry
1414 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
1415 if (menu_item_selection == &XVECTOR (menu_items)->contents[i])
1416 {
1417 if (keymaps != 0)
1418 {
1419 entry = Fcons (entry, Qnil);
1420 if (!NILP (prefix))
1421 entry = Fcons (prefix, entry);
1422 }
1423 return entry;
1424 }
1425 i += MENU_ITEMS_ITEM_LENGTH;
1426 }
1427 }
1428 }
1429
1430 return Qnil;
1431 }
1432
1433 #else /* not USE_X_TOOLKIT */
1434
1435 static Lisp_Object
1436 xmenu_show (f, x, y, menubarp, keymaps, title, error)
1437 FRAME_PTR f;
1438 int x, y;
1439 int keymaps;
1440 int menubarp;
1441 Lisp_Object title;
1442 char **error;
1443 {
1444 Window root;
1445 XMenu *menu;
1446 int pane, selidx, lpane, status;
1447 Lisp_Object entry, pane_prefix;
1448 char *datap;
1449 int ulx, uly, width, height;
1450 int dispwidth, dispheight;
1451 int i;
1452 int dummy_int;
1453 unsigned int dummy_uint;
1454
1455 *error = 0;
1456 if (menu_items_n_panes == 0)
1457 return Qnil;
1458
1459 /* Figure out which root window F is on. */
1460 XGetGeometry (x_current_display, FRAME_X_WINDOW (f), &root,
1461 &dummy_int, &dummy_int, &dummy_uint, &dummy_uint,
1462 &dummy_uint, &dummy_uint);
1463
1464 /* Make the menu on that window. */
1465 menu = XMenuCreate (XDISPLAY root, "emacs");
1466 if (menu == NULL)
1467 {
1468 *error = "Can't create menu";
1469 return Qnil;
1470 }
1471
1472 /* Adjust coordinates to relative to the outer (window manager) window. */
1473 #ifdef HAVE_X11
1474 {
1475 Window child;
1476 int win_x = 0, win_y = 0;
1477
1478 /* Find the position of the outside upper-left corner of
1479 the inner window, with respect to the outer window. */
1480 if (f->display.x->parent_desc != ROOT_WINDOW)
1481 {
1482 BLOCK_INPUT;
1483 XTranslateCoordinates (x_current_display,
1484
1485 /* From-window, to-window. */
1486 f->display.x->window_desc,
1487 f->display.x->parent_desc,
1488
1489 /* From-position, to-position. */
1490 0, 0, &win_x, &win_y,
1491
1492 /* Child of window. */
1493 &child);
1494 UNBLOCK_INPUT;
1495 x += win_x;
1496 y += win_y;
1497 }
1498 }
1499 #endif /* HAVE_X11 */
1500
1501 /* Adjust coordinates to be root-window-relative. */
1502 x += f->display.x->left_pos;
1503 y += f->display.x->top_pos;
1504
1505 /* Create all the necessary panes and their items. */
1506 i = 0;
1507 while (i < menu_items_used)
1508 {
1509 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1510 {
1511 /* Create a new pane. */
1512 Lisp_Object pane_name, prefix;
1513 char *pane_string;
1514
1515 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1516 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1517 pane_string = (NILP (pane_name)
1518 ? "" : (char *) XSTRING (pane_name)->data);
1519 if (keymaps && !NILP (prefix))
1520 pane_string++;
1521
1522 lpane = XMenuAddPane (XDISPLAY menu, pane_string, TRUE);
1523 if (lpane == XM_FAILURE)
1524 {
1525 XMenuDestroy (XDISPLAY menu);
1526 *error = "Can't create pane";
1527 return Qnil;
1528 }
1529 i += MENU_ITEMS_PANE_LENGTH;
1530 }
1531 else
1532 {
1533 /* Create a new item within current pane. */
1534 Lisp_Object item_name, enable, descrip;
1535
1536 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
1537 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
1538 descrip
1539 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
1540 if (!NILP (descrip))
1541 item_name = concat2 (item_name, descrip);
1542
1543 if (XMenuAddSelection (XDISPLAY menu, lpane, 0,
1544 XSTRING (item_name)->data,
1545 !NILP (enable))
1546 == XM_FAILURE)
1547 {
1548 XMenuDestroy (XDISPLAY menu);
1549 *error = "Can't add selection to menu";
1550 return Qnil;
1551 }
1552 i += MENU_ITEMS_ITEM_LENGTH;
1553 }
1554 }
1555
1556 /* All set and ready to fly. */
1557 XMenuRecompute (XDISPLAY menu);
1558 dispwidth = DisplayWidth (x_current_display, XDefaultScreen (x_current_display));
1559 dispheight = DisplayHeight (x_current_display, XDefaultScreen (x_current_display));
1560 x = min (x, dispwidth);
1561 y = min (y, dispheight);
1562 x = max (x, 1);
1563 y = max (y, 1);
1564 XMenuLocate (XDISPLAY menu, 0, 0, x, y,
1565 &ulx, &uly, &width, &height);
1566 if (ulx+width > dispwidth)
1567 {
1568 x -= (ulx + width) - dispwidth;
1569 ulx = dispwidth - width;
1570 }
1571 if (uly+height > dispheight)
1572 {
1573 y -= (uly + height) - dispheight;
1574 uly = dispheight - height;
1575 }
1576 if (ulx < 0) x -= ulx;
1577 if (uly < 0) y -= uly;
1578
1579 XMenuSetFreeze (menu, TRUE);
1580 pane = selidx = 0;
1581
1582 status = XMenuActivate (XDISPLAY menu, &pane, &selidx,
1583 x, y, ButtonReleaseMask, &datap);
1584 switch (status)
1585 {
1586 case XM_SUCCESS:
1587 #ifdef XDEBUG
1588 fprintf (stderr, "pane= %d line = %d\n", panes, selidx);
1589 #endif
1590
1591 /* Find the item number SELIDX in pane number PANE. */
1592 i = 0;
1593 while (i < menu_items_used)
1594 {
1595 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1596 {
1597 if (pane == 0)
1598 pane_prefix
1599 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1600 pane--;
1601 i += MENU_ITEMS_PANE_LENGTH;
1602 }
1603 else
1604 {
1605 if (pane == -1)
1606 {
1607 if (selidx == 0)
1608 {
1609 entry
1610 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
1611 if (keymaps != 0)
1612 {
1613 entry = Fcons (entry, Qnil);
1614 if (!NILP (pane_prefix))
1615 entry = Fcons (pane_prefix, entry);
1616 }
1617 break;
1618 }
1619 selidx--;
1620 }
1621 i += MENU_ITEMS_ITEM_LENGTH;
1622 }
1623 }
1624 break;
1625
1626 case XM_FAILURE:
1627 XMenuDestroy (XDISPLAY menu);
1628 *error = "Can't activate menu";
1629 case XM_IA_SELECT:
1630 case XM_NO_SELECT:
1631 entry = Qnil;
1632 break;
1633 }
1634 XMenuDestroy (XDISPLAY menu);
1635 return entry;
1636 }
1637 #endif /* not USE_X_TOOLKIT */
1638 \f
1639 syms_of_xmenu ()
1640 {
1641 staticpro (&menu_items);
1642 menu_items = Qnil;
1643
1644 popup_id_tick = (1<<16);
1645 defsubr (&Sx_popup_menu);
1646 }