Merge from emacs--rel--22
[bpt/emacs.git] / src / macmenu.c
1 /* Menu support for GNU Emacs on Mac OS.
2 Copyright (C) 2000, 2001, 2002, 2003, 2004,
3 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA. */
21
22 /* Contributed by Andrew Choi (akochoi@mac.com). */
23
24 #include <config.h>
25
26 #include <stdio.h>
27
28 #include "lisp.h"
29 #include "frame.h"
30 #include "termhooks.h"
31 #include "keyboard.h"
32 #include "keymap.h"
33 #include "window.h"
34 #include "blockinput.h"
35 #include "buffer.h"
36 #include "charset.h"
37 #include "coding.h"
38
39 #if !TARGET_API_MAC_CARBON
40 #include <MacTypes.h>
41 #include <Menus.h>
42 #include <Quickdraw.h>
43 #include <ToolUtils.h>
44 #include <Fonts.h>
45 #include <Controls.h>
46 #include <Windows.h>
47 #include <Events.h>
48 #if defined (__MRC__) || (__MSL__ >= 0x6000)
49 #include <ControlDefinitions.h>
50 #endif
51 #endif /* not TARGET_API_MAC_CARBON */
52
53 /* This may include sys/types.h, and that somehow loses
54 if this is not done before the other system files. */
55 #include "macterm.h"
56
57 /* Load sys/types.h if not already loaded.
58 In some systems loading it twice is suicidal. */
59 #ifndef makedev
60 #include <sys/types.h>
61 #endif
62
63 #include "dispextern.h"
64
65 enum mac_menu_kind { /* Menu ID range */
66 MAC_MENU_APPLE, /* 0 (Reserved by Apple) */
67 MAC_MENU_MENU_BAR, /* 1 .. 233 */
68 MAC_MENU_M_APPLE, /* 234 (== M_APPLE) */
69 MAC_MENU_POPUP, /* 235 */
70 MAC_MENU_DRIVER, /* 236 .. 255 (Reserved) */
71 MAC_MENU_MENU_BAR_SUB, /* 256 .. 16383 */
72 MAC_MENU_POPUP_SUB, /* 16384 .. 32767 */
73 MAC_MENU_END /* 32768 */
74 };
75
76 static const int min_menu_id[] = {0, 1, 234, 235, 236, 256, 16384, 32768};
77
78 #define DIALOG_WINDOW_RESOURCE 130
79
80 #if TARGET_API_MAC_CARBON
81 #define HAVE_DIALOGS 1
82 #endif
83
84 #undef HAVE_MULTILINGUAL_MENU
85
86 /******************************************************************/
87 /* Definitions copied from lwlib.h */
88
89 typedef void * XtPointer;
90
91 enum button_type
92 {
93 BUTTON_TYPE_NONE,
94 BUTTON_TYPE_TOGGLE,
95 BUTTON_TYPE_RADIO
96 };
97
98 /* This structure is based on the one in ../lwlib/lwlib.h, modified
99 for Mac OS. */
100 typedef struct _widget_value
101 {
102 /* name of widget */
103 Lisp_Object lname;
104 char* name;
105 /* value (meaning depend on widget type) */
106 char* value;
107 /* keyboard equivalent. no implications for XtTranslations */
108 Lisp_Object lkey;
109 char* key;
110 /* Help string or nil if none.
111 GC finds this string through the frame's menu_bar_vector
112 or through menu_items. */
113 Lisp_Object help;
114 /* true if enabled */
115 Boolean enabled;
116 /* true if selected */
117 Boolean selected;
118 /* The type of a button. */
119 enum button_type button_type;
120 /* true if menu title */
121 Boolean title;
122 #if 0
123 /* true if was edited (maintained by get_value) */
124 Boolean edited;
125 /* true if has changed (maintained by lw library) */
126 change_type change;
127 /* true if this widget itself has changed,
128 but not counting the other widgets found in the `next' field. */
129 change_type this_one_change;
130 #endif
131 /* Contents of the sub-widgets, also selected slot for checkbox */
132 struct _widget_value* contents;
133 /* data passed to callback */
134 XtPointer call_data;
135 /* next one in the list */
136 struct _widget_value* next;
137 #if 0
138 /* slot for the toolkit dependent part. Always initialize to NULL. */
139 void* toolkit_data;
140 /* tell us if we should free the toolkit data slot when freeing the
141 widget_value itself. */
142 Boolean free_toolkit_data;
143
144 /* we resource the widget_value structures; this points to the next
145 one on the free list if this one has been deallocated.
146 */
147 struct _widget_value *free_list;
148 #endif
149 } widget_value;
150
151 /* Assumed by other routines to zero area returned. */
152 #define malloc_widget_value() (void *)memset (xmalloc (sizeof (widget_value)),\
153 0, (sizeof (widget_value)))
154 #define free_widget_value(wv) xfree (wv)
155
156 /******************************************************************/
157
158 #ifndef TRUE
159 #define TRUE 1
160 #define FALSE 0
161 #endif /* no TRUE */
162
163 Lisp_Object Qdebug_on_next_call;
164
165 extern Lisp_Object Vmenu_updating_frame;
166
167 extern Lisp_Object Qmenu_bar, Qmac_apple_event;
168
169 extern Lisp_Object QCtoggle, QCradio;
170
171 extern Lisp_Object Voverriding_local_map;
172 extern Lisp_Object Voverriding_local_map_menu_flag;
173
174 extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
175
176 extern Lisp_Object Qmenu_bar_update_hook;
177
178 void set_frame_menubar P_ ((FRAME_PTR, int, int));
179
180 #if TARGET_API_MAC_CARBON
181 #define ENCODE_MENU_STRING(str) ENCODE_UTF_8 (str)
182 #else
183 #define ENCODE_MENU_STRING(str) ENCODE_SYSTEM (str)
184 #endif
185
186 static void push_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
187 Lisp_Object, Lisp_Object, Lisp_Object,
188 Lisp_Object, Lisp_Object));
189 #ifdef HAVE_DIALOGS
190 static Lisp_Object mac_dialog_show P_ ((FRAME_PTR, int, Lisp_Object,
191 Lisp_Object, char **));
192 #endif
193 static Lisp_Object mac_menu_show P_ ((struct frame *, int, int, int, int,
194 Lisp_Object, char **));
195 static void keymap_panes P_ ((Lisp_Object *, int, int));
196 static void single_keymap_panes P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
197 int, int));
198 static void list_of_panes P_ ((Lisp_Object));
199 static void list_of_items P_ ((Lisp_Object));
200
201 static void find_and_call_menu_selection P_ ((FRAME_PTR, int, Lisp_Object,
202 void *));
203 static int fill_menu P_ ((MenuRef, widget_value *, enum mac_menu_kind, int));
204 static void fill_menubar P_ ((widget_value *, int));
205 static void dispose_menus P_ ((enum mac_menu_kind, int));
206
207 \f
208 /* This holds a Lisp vector that holds the results of decoding
209 the keymaps or alist-of-alists that specify a menu.
210
211 It describes the panes and items within the panes.
212
213 Each pane is described by 3 elements in the vector:
214 t, the pane name, the pane's prefix key.
215 Then follow the pane's items, with 5 elements per item:
216 the item string, the enable flag, the item's value,
217 the definition, and the equivalent keyboard key's description string.
218
219 In some cases, multiple levels of menus may be described.
220 A single vector slot containing nil indicates the start of a submenu.
221 A single vector slot containing lambda indicates the end of a submenu.
222 The submenu follows a menu item which is the way to reach the submenu.
223
224 A single vector slot containing quote indicates that the
225 following items should appear on the right of a dialog box.
226
227 Using a Lisp vector to hold this information while we decode it
228 takes care of protecting all the data from GC. */
229
230 #define MENU_ITEMS_PANE_NAME 1
231 #define MENU_ITEMS_PANE_PREFIX 2
232 #define MENU_ITEMS_PANE_LENGTH 3
233
234 enum menu_item_idx
235 {
236 MENU_ITEMS_ITEM_NAME = 0,
237 MENU_ITEMS_ITEM_ENABLE,
238 MENU_ITEMS_ITEM_VALUE,
239 MENU_ITEMS_ITEM_EQUIV_KEY,
240 MENU_ITEMS_ITEM_DEFINITION,
241 MENU_ITEMS_ITEM_TYPE,
242 MENU_ITEMS_ITEM_SELECTED,
243 MENU_ITEMS_ITEM_HELP,
244 MENU_ITEMS_ITEM_LENGTH
245 };
246
247 static Lisp_Object menu_items;
248
249 /* Number of slots currently allocated in menu_items. */
250 static int menu_items_allocated;
251
252 /* This is the index in menu_items of the first empty slot. */
253 static int menu_items_used;
254
255 /* The number of panes currently recorded in menu_items,
256 excluding those within submenus. */
257 static int menu_items_n_panes;
258
259 /* Current depth within submenus. */
260 static int menu_items_submenu_depth;
261
262 /* Nonzero means a menu is currently active. */
263 static int popup_activated_flag;
264
265 /* This is set nonzero after the user activates the menu bar, and set
266 to zero again after the menu bars are redisplayed by prepare_menu_bar.
267 While it is nonzero, all calls to set_frame_menubar go deep.
268
269 I don't understand why this is needed, but it does seem to be
270 needed on Motif, according to Marcus Daniels <marcus@sysc.pdx.edu>. */
271
272 int pending_menu_activation;
273 \f
274 /* Initialize the menu_items structure if we haven't already done so.
275 Also mark it as currently empty. */
276
277 static void
278 init_menu_items ()
279 {
280 if (NILP (menu_items))
281 {
282 menu_items_allocated = 60;
283 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
284 }
285
286 menu_items_used = 0;
287 menu_items_n_panes = 0;
288 menu_items_submenu_depth = 0;
289 }
290
291 /* Call at the end of generating the data in menu_items. */
292
293 static void
294 finish_menu_items ()
295 {
296 }
297
298 /* Call when finished using the data for the current menu
299 in menu_items. */
300
301 static void
302 discard_menu_items ()
303 {
304 /* Free the structure if it is especially large.
305 Otherwise, hold on to it, to save time. */
306 if (menu_items_allocated > 200)
307 {
308 menu_items = Qnil;
309 menu_items_allocated = 0;
310 }
311 }
312
313 /* This undoes save_menu_items, and it is called by the specpdl unwind
314 mechanism. */
315
316 static Lisp_Object
317 restore_menu_items (saved)
318 Lisp_Object saved;
319 {
320 menu_items = XCAR (saved);
321 menu_items_allocated = (VECTORP (menu_items) ? ASIZE (menu_items) : 0);
322 saved = XCDR (saved);
323 menu_items_used = XINT (XCAR (saved));
324 saved = XCDR (saved);
325 menu_items_n_panes = XINT (XCAR (saved));
326 saved = XCDR (saved);
327 menu_items_submenu_depth = XINT (XCAR (saved));
328 return Qnil;
329 }
330
331 /* Push the whole state of menu_items processing onto the specpdl.
332 It will be restored when the specpdl is unwound. */
333
334 static void
335 save_menu_items ()
336 {
337 Lisp_Object saved = list4 (menu_items,
338 make_number (menu_items_used),
339 make_number (menu_items_n_panes),
340 make_number (menu_items_submenu_depth));
341 record_unwind_protect (restore_menu_items, saved);
342 menu_items = Qnil;
343 }
344 \f
345 /* Make the menu_items vector twice as large. */
346
347 static void
348 grow_menu_items ()
349 {
350 menu_items_allocated *= 2;
351 menu_items = larger_vector (menu_items, menu_items_allocated, Qnil);
352 }
353
354 /* Begin a submenu. */
355
356 static void
357 push_submenu_start ()
358 {
359 if (menu_items_used + 1 > menu_items_allocated)
360 grow_menu_items ();
361
362 XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
363 menu_items_submenu_depth++;
364 }
365
366 /* End a submenu. */
367
368 static void
369 push_submenu_end ()
370 {
371 if (menu_items_used + 1 > menu_items_allocated)
372 grow_menu_items ();
373
374 XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
375 menu_items_submenu_depth--;
376 }
377
378 /* Indicate boundary between left and right. */
379
380 static void
381 push_left_right_boundary ()
382 {
383 if (menu_items_used + 1 > menu_items_allocated)
384 grow_menu_items ();
385
386 XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
387 }
388
389 /* Start a new menu pane in menu_items.
390 NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
391
392 static void
393 push_menu_pane (name, prefix_vec)
394 Lisp_Object name, prefix_vec;
395 {
396 if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
397 grow_menu_items ();
398
399 if (menu_items_submenu_depth == 0)
400 menu_items_n_panes++;
401 XVECTOR (menu_items)->contents[menu_items_used++] = Qt;
402 XVECTOR (menu_items)->contents[menu_items_used++] = name;
403 XVECTOR (menu_items)->contents[menu_items_used++] = prefix_vec;
404 }
405
406 /* Push one menu item into the current pane. NAME is the string to
407 display. ENABLE if non-nil means this item can be selected. KEY
408 is the key generated by choosing this item, or nil if this item
409 doesn't really have a definition. DEF is the definition of this
410 item. EQUIV is the textual description of the keyboard equivalent
411 for this item (or nil if none). TYPE is the type of this menu
412 item, one of nil, `toggle' or `radio'. */
413
414 static void
415 push_menu_item (name, enable, key, def, equiv, type, selected, help)
416 Lisp_Object name, enable, key, def, equiv, type, selected, help;
417 {
418 if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
419 grow_menu_items ();
420
421 XVECTOR (menu_items)->contents[menu_items_used++] = name;
422 XVECTOR (menu_items)->contents[menu_items_used++] = enable;
423 XVECTOR (menu_items)->contents[menu_items_used++] = key;
424 XVECTOR (menu_items)->contents[menu_items_used++] = equiv;
425 XVECTOR (menu_items)->contents[menu_items_used++] = def;
426 XVECTOR (menu_items)->contents[menu_items_used++] = type;
427 XVECTOR (menu_items)->contents[menu_items_used++] = selected;
428 XVECTOR (menu_items)->contents[menu_items_used++] = help;
429 }
430 \f
431 /* Look through KEYMAPS, a vector of keymaps that is NMAPS long,
432 and generate menu panes for them in menu_items.
433 If NOTREAL is nonzero,
434 don't bother really computing whether an item is enabled. */
435
436 static void
437 keymap_panes (keymaps, nmaps, notreal)
438 Lisp_Object *keymaps;
439 int nmaps;
440 int notreal;
441 {
442 int mapno;
443
444 init_menu_items ();
445
446 /* Loop over the given keymaps, making a pane for each map.
447 But don't make a pane that is empty--ignore that map instead.
448 P is the number of panes we have made so far. */
449 for (mapno = 0; mapno < nmaps; mapno++)
450 single_keymap_panes (keymaps[mapno],
451 Fkeymap_prompt (keymaps[mapno]), Qnil, notreal, 10);
452
453 finish_menu_items ();
454 }
455
456 /* Args passed between single_keymap_panes and single_menu_item. */
457 struct skp
458 {
459 Lisp_Object pending_maps;
460 int maxdepth, notreal;
461 };
462
463 static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
464 void *));
465
466 /* This is a recursive subroutine of keymap_panes.
467 It handles one keymap, KEYMAP.
468 The other arguments are passed along
469 or point to local variables of the previous function.
470 If NOTREAL is nonzero, only check for equivalent key bindings, don't
471 evaluate expressions in menu items and don't make any menu.
472
473 If we encounter submenus deeper than MAXDEPTH levels, ignore them. */
474
475 static void
476 single_keymap_panes (keymap, pane_name, prefix, notreal, maxdepth)
477 Lisp_Object keymap;
478 Lisp_Object pane_name;
479 Lisp_Object prefix;
480 int notreal;
481 int maxdepth;
482 {
483 struct skp skp;
484 struct gcpro gcpro1;
485
486 skp.pending_maps = Qnil;
487 skp.maxdepth = maxdepth;
488 skp.notreal = notreal;
489
490 if (maxdepth <= 0)
491 return;
492
493 push_menu_pane (pane_name, prefix);
494
495 GCPRO1 (skp.pending_maps);
496 map_keymap (keymap, single_menu_item, Qnil, &skp, 1);
497 UNGCPRO;
498
499 /* Process now any submenus which want to be panes at this level. */
500 while (CONSP (skp.pending_maps))
501 {
502 Lisp_Object elt, eltcdr, string;
503 elt = XCAR (skp.pending_maps);
504 eltcdr = XCDR (elt);
505 string = XCAR (eltcdr);
506 /* We no longer discard the @ from the beginning of the string here.
507 Instead, we do this in mac_menu_show. */
508 single_keymap_panes (Fcar (elt), string,
509 XCDR (eltcdr), notreal, maxdepth - 1);
510 skp.pending_maps = XCDR (skp.pending_maps);
511 }
512 }
513 \f
514 /* This is a subroutine of single_keymap_panes that handles one
515 keymap entry.
516 KEY is a key in a keymap and ITEM is its binding.
517 SKP->PENDING_MAPS_PTR is a list of keymaps waiting to be made into
518 separate panes.
519 If SKP->NOTREAL is nonzero, only check for equivalent key bindings, don't
520 evaluate expressions in menu items and don't make any menu.
521 If we encounter submenus deeper than SKP->MAXDEPTH levels, ignore them. */
522
523 static void
524 single_menu_item (key, item, dummy, skp_v)
525 Lisp_Object key, item, dummy;
526 void *skp_v;
527 {
528 Lisp_Object map, item_string, enabled;
529 struct gcpro gcpro1, gcpro2;
530 int res;
531 struct skp *skp = skp_v;
532
533 /* Parse the menu item and leave the result in item_properties. */
534 GCPRO2 (key, item);
535 res = parse_menu_item (item, skp->notreal, 0);
536 UNGCPRO;
537 if (!res)
538 return; /* Not a menu item. */
539
540 map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
541
542 if (skp->notreal)
543 {
544 /* We don't want to make a menu, just traverse the keymaps to
545 precompute equivalent key bindings. */
546 if (!NILP (map))
547 single_keymap_panes (map, Qnil, key, 1, skp->maxdepth - 1);
548 return;
549 }
550
551 enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE];
552 item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME];
553
554 if (!NILP (map) && SREF (item_string, 0) == '@')
555 {
556 if (!NILP (enabled))
557 /* An enabled separate pane. Remember this to handle it later. */
558 skp->pending_maps = Fcons (Fcons (map, Fcons (item_string, key)),
559 skp->pending_maps);
560 return;
561 }
562
563 push_menu_item (item_string, enabled, key,
564 XVECTOR (item_properties)->contents[ITEM_PROPERTY_DEF],
565 XVECTOR (item_properties)->contents[ITEM_PROPERTY_KEYEQ],
566 XVECTOR (item_properties)->contents[ITEM_PROPERTY_TYPE],
567 XVECTOR (item_properties)->contents[ITEM_PROPERTY_SELECTED],
568 XVECTOR (item_properties)->contents[ITEM_PROPERTY_HELP]);
569
570 /* Display a submenu using the toolkit. */
571 if (! (NILP (map) || NILP (enabled)))
572 {
573 push_submenu_start ();
574 single_keymap_panes (map, Qnil, key, 0, skp->maxdepth - 1);
575 push_submenu_end ();
576 }
577 }
578 \f
579 /* Push all the panes and items of a menu described by the
580 alist-of-alists MENU.
581 This handles old-fashioned calls to x-popup-menu. */
582
583 static void
584 list_of_panes (menu)
585 Lisp_Object menu;
586 {
587 Lisp_Object tail;
588
589 init_menu_items ();
590
591 for (tail = menu; CONSP (tail); tail = XCDR (tail))
592 {
593 Lisp_Object elt, pane_name, pane_data;
594 elt = XCAR (tail);
595 pane_name = Fcar (elt);
596 CHECK_STRING (pane_name);
597 push_menu_pane (ENCODE_MENU_STRING (pane_name), Qnil);
598 pane_data = Fcdr (elt);
599 CHECK_CONS (pane_data);
600 list_of_items (pane_data);
601 }
602
603 finish_menu_items ();
604 }
605
606 /* Push the items in a single pane defined by the alist PANE. */
607
608 static void
609 list_of_items (pane)
610 Lisp_Object pane;
611 {
612 Lisp_Object tail, item, item1;
613
614 for (tail = pane; CONSP (tail); tail = XCDR (tail))
615 {
616 item = XCAR (tail);
617 if (STRINGP (item))
618 push_menu_item (ENCODE_MENU_STRING (item), Qnil, Qnil, Qt,
619 Qnil, Qnil, Qnil, Qnil);
620 else if (CONSP (item))
621 {
622 item1 = XCAR (item);
623 CHECK_STRING (item1);
624 push_menu_item (ENCODE_MENU_STRING (item1), Qt, XCDR (item),
625 Qt, Qnil, Qnil, Qnil, Qnil);
626 }
627 else
628 push_left_right_boundary ();
629
630 }
631 }
632 \f
633 static Lisp_Object
634 cleanup_popup_menu (arg)
635 Lisp_Object arg;
636 {
637 discard_menu_items ();
638 return Qnil;
639 }
640
641 DEFUN ("x-popup-menu", Fx_popup_menu, Sx_popup_menu, 2, 2, 0,
642 doc: /* Pop up a deck-of-cards menu and return user's selection.
643 POSITION is a position specification. This is either a mouse button event
644 or a list ((XOFFSET YOFFSET) WINDOW)
645 where XOFFSET and YOFFSET are positions in pixels from the top left
646 corner of WINDOW. (WINDOW may be a window or a frame object.)
647 This controls the position of the top left of the menu as a whole.
648 If POSITION is t, it means to use the current mouse position.
649
650 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
651 The menu items come from key bindings that have a menu string as well as
652 a definition; actually, the "definition" in such a key binding looks like
653 \(STRING . REAL-DEFINITION). To give the menu a title, put a string into
654 the keymap as a top-level element.
655
656 If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
657 Otherwise, REAL-DEFINITION should be a valid key binding definition.
658
659 You can also use a list of keymaps as MENU.
660 Then each keymap makes a separate pane.
661
662 When MENU is a keymap or a list of keymaps, the return value is the
663 list of events corresponding to the user's choice. Note that
664 `x-popup-menu' does not actually execute the command bound to that
665 sequence of events.
666
667 Alternatively, you can specify a menu of multiple panes
668 with a list of the form (TITLE PANE1 PANE2...),
669 where each pane is a list of form (TITLE ITEM1 ITEM2...).
670 Each ITEM is normally a cons cell (STRING . VALUE);
671 but a string can appear as an item--that makes a nonselectable line
672 in the menu.
673 With this form of menu, the return value is VALUE from the chosen item.
674
675 If POSITION is nil, don't display the menu at all, just precalculate the
676 cached information about equivalent key sequences.
677
678 If the user gets rid of the menu without making a valid choice, for
679 instance by clicking the mouse away from a valid choice or by typing
680 keyboard input, then this normally results in a quit and
681 `x-popup-menu' does not return. But if POSITION is a mouse button
682 event (indicating that the user invoked the menu with the mouse) then
683 no quit occurs and `x-popup-menu' returns nil. */)
684 (position, menu)
685 Lisp_Object position, menu;
686 {
687 Lisp_Object keymap, tem;
688 int xpos = 0, ypos = 0;
689 Lisp_Object title;
690 char *error_name = NULL;
691 Lisp_Object selection;
692 FRAME_PTR f = NULL;
693 Lisp_Object x, y, window;
694 int keymaps = 0;
695 int for_click = 0;
696 int specpdl_count = SPECPDL_INDEX ();
697 struct gcpro gcpro1;
698
699 #ifdef HAVE_MENUS
700 if (! NILP (position))
701 {
702 check_mac ();
703
704 /* Decode the first argument: find the window and the coordinates. */
705 if (EQ (position, Qt)
706 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
707 || EQ (XCAR (position), Qtool_bar)
708 || EQ (XCAR (position), Qmac_apple_event))))
709 {
710 /* Use the mouse's current position. */
711 FRAME_PTR new_f = SELECTED_FRAME ();
712 Lisp_Object bar_window;
713 enum scroll_bar_part part;
714 unsigned long time;
715
716 if (FRAME_TERMINAL (new_f)->mouse_position_hook)
717 (*FRAME_TERMINAL (new_f)->mouse_position_hook) (&new_f, 1, &bar_window,
718 &part, &x, &y, &time);
719 if (new_f != 0)
720 XSETFRAME (window, new_f);
721 else
722 {
723 window = selected_window;
724 XSETFASTINT (x, 0);
725 XSETFASTINT (y, 0);
726 }
727 }
728 else
729 {
730 tem = Fcar (position);
731 if (CONSP (tem))
732 {
733 window = Fcar (Fcdr (position));
734 x = XCAR (tem);
735 y = Fcar (XCDR (tem));
736 }
737 else
738 {
739 for_click = 1;
740 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
741 window = Fcar (tem); /* POSN_WINDOW (tem) */
742 tem = Fcar (Fcdr (Fcdr (tem))); /* POSN_WINDOW_POSN (tem) */
743 x = Fcar (tem);
744 y = Fcdr (tem);
745 }
746 }
747
748 CHECK_NUMBER (x);
749 CHECK_NUMBER (y);
750
751 /* Decode where to put the menu. */
752
753 if (FRAMEP (window))
754 {
755 f = XFRAME (window);
756 xpos = 0;
757 ypos = 0;
758 }
759 else if (WINDOWP (window))
760 {
761 CHECK_LIVE_WINDOW (window);
762 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
763
764 xpos = WINDOW_LEFT_EDGE_X (XWINDOW (window));
765 ypos = WINDOW_TOP_EDGE_Y (XWINDOW (window));
766 }
767 else
768 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
769 but I don't want to make one now. */
770 CHECK_WINDOW (window);
771
772 xpos += XINT (x);
773 ypos += XINT (y);
774
775 XSETFRAME (Vmenu_updating_frame, f);
776 }
777 else
778 Vmenu_updating_frame = Qnil;
779 #endif /* HAVE_MENUS */
780
781 title = Qnil;
782 GCPRO1 (title);
783
784 /* Decode the menu items from what was specified. */
785
786 keymap = get_keymap (menu, 0, 0);
787 if (CONSP (keymap))
788 {
789 /* We were given a keymap. Extract menu info from the keymap. */
790 Lisp_Object prompt;
791
792 /* Extract the detailed info to make one pane. */
793 keymap_panes (&menu, 1, NILP (position));
794
795 /* Search for a string appearing directly as an element of the keymap.
796 That string is the title of the menu. */
797 prompt = Fkeymap_prompt (keymap);
798 if (NILP (title) && !NILP (prompt))
799 title = prompt;
800
801 /* Make that be the pane title of the first pane. */
802 if (!NILP (prompt) && menu_items_n_panes >= 0)
803 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = prompt;
804
805 keymaps = 1;
806 }
807 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
808 {
809 /* We were given a list of keymaps. */
810 int nmaps = XFASTINT (Flength (menu));
811 Lisp_Object *maps
812 = (Lisp_Object *) alloca (nmaps * sizeof (Lisp_Object));
813 int i;
814
815 title = Qnil;
816
817 /* The first keymap that has a prompt string
818 supplies the menu title. */
819 for (tem = menu, i = 0; CONSP (tem); tem = XCDR (tem))
820 {
821 Lisp_Object prompt;
822
823 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
824
825 prompt = Fkeymap_prompt (keymap);
826 if (NILP (title) && !NILP (prompt))
827 title = prompt;
828 }
829
830 /* Extract the detailed info to make one pane. */
831 keymap_panes (maps, nmaps, NILP (position));
832
833 /* Make the title be the pane title of the first pane. */
834 if (!NILP (title) && menu_items_n_panes >= 0)
835 XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME] = title;
836
837 keymaps = 1;
838 }
839 else
840 {
841 /* We were given an old-fashioned menu. */
842 title = Fcar (menu);
843 CHECK_STRING (title);
844
845 list_of_panes (Fcdr (menu));
846
847 keymaps = 0;
848 }
849
850 if (NILP (position))
851 {
852 discard_menu_items ();
853 UNGCPRO;
854 return Qnil;
855 }
856
857 #ifdef HAVE_MENUS
858 /* Display them in a menu. */
859 record_unwind_protect (cleanup_popup_menu, Qnil);
860 BLOCK_INPUT;
861
862 selection = mac_menu_show (f, xpos, ypos, for_click,
863 keymaps, title, &error_name);
864 UNBLOCK_INPUT;
865 unbind_to (specpdl_count, Qnil);
866
867 UNGCPRO;
868 #endif /* HAVE_MENUS */
869
870 if (error_name) error (error_name);
871 return selection;
872 }
873
874 #ifdef HAVE_MENUS
875
876 /* Regard ESC and C-g as Cancel even without the Cancel button. */
877
878 #if 0 /* defined (MAC_OSX) */
879 static Boolean
880 mac_dialog_modal_filter (dialog, event, item_hit)
881 DialogRef dialog;
882 EventRecord *event;
883 DialogItemIndex *item_hit;
884 {
885 Boolean result;
886
887 result = StdFilterProc (dialog, event, item_hit);
888 if (result == false
889 && (event->what == keyDown || event->what == autoKey)
890 && ((event->message & charCodeMask) == kEscapeCharCode
891 || mac_quit_char_key_p (event->modifiers,
892 (event->message & keyCodeMask) >> 8)))
893 {
894 *item_hit = kStdCancelItemIndex;
895 return true;
896 }
897
898 return result;
899 }
900 #endif
901
902 DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0,
903 doc: /* Pop up a dialog box and return user's selection.
904 POSITION specifies which frame to use.
905 This is normally a mouse button event or a window or frame.
906 If POSITION is t, it means to use the frame the mouse is on.
907 The dialog box appears in the middle of the specified frame.
908
909 CONTENTS specifies the alternatives to display in the dialog box.
910 It is a list of the form (DIALOG ITEM1 ITEM2...).
911 Each ITEM is a cons cell (STRING . VALUE).
912 The return value is VALUE from the chosen item.
913
914 An ITEM may also be just a string--that makes a nonselectable item.
915 An ITEM may also be nil--that means to put all preceding items
916 on the left of the dialog box and all following items on the right.
917 \(By default, approximately half appear on each side.)
918
919 If HEADER is non-nil, the frame title for the box is "Information",
920 otherwise it is "Question".
921
922 If the user gets rid of the dialog box without making a valid choice,
923 for instance using the window manager, then this produces a quit and
924 `x-popup-dialog' does not return. */)
925 (position, contents, header)
926 Lisp_Object position, contents, header;
927 {
928 FRAME_PTR f = NULL;
929 Lisp_Object window;
930
931 check_mac ();
932
933 /* Decode the first argument: find the window or frame to use. */
934 if (EQ (position, Qt)
935 || (CONSP (position) && (EQ (XCAR (position), Qmenu_bar)
936 || EQ (XCAR (position), Qtool_bar)
937 || EQ (XCAR (position), Qmac_apple_event))))
938 {
939 #if 0 /* Using the frame the mouse is on may not be right. */
940 /* Use the mouse's current position. */
941 FRAME_PTR new_f = SELECTED_FRAME ();
942 Lisp_Object bar_window;
943 enum scroll_bar_part part;
944 unsigned long time;
945 Lisp_Object x, y;
946
947 (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
948
949 if (new_f != 0)
950 XSETFRAME (window, new_f);
951 else
952 window = selected_window;
953 #endif
954 window = selected_window;
955 }
956 else if (CONSP (position))
957 {
958 Lisp_Object tem;
959 tem = Fcar (position);
960 if (CONSP (tem))
961 window = Fcar (Fcdr (position));
962 else
963 {
964 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
965 window = Fcar (tem); /* POSN_WINDOW (tem) */
966 }
967 }
968 else if (WINDOWP (position) || FRAMEP (position))
969 window = position;
970 else
971 window = Qnil;
972
973 /* Decode where to put the menu. */
974
975 if (FRAMEP (window))
976 f = XFRAME (window);
977 else if (WINDOWP (window))
978 {
979 CHECK_LIVE_WINDOW (window);
980 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
981 }
982 else
983 /* ??? Not really clean; should be CHECK_WINDOW_OR_FRAME,
984 but I don't want to make one now. */
985 CHECK_WINDOW (window);
986
987 #if 0 /* defined (MAC_OSX) */
988 /* Special treatment for Fmessage_box, Fyes_or_no_p, and Fy_or_n_p. */
989 if (EQ (position, Qt)
990 && STRINGP (Fcar (contents))
991 && ((!NILP (Fequal (XCDR (contents),
992 Fcons (Fcons (build_string ("OK"), Qt), Qnil)))
993 && EQ (header, Qt))
994 || (!NILP (Fequal (XCDR (contents),
995 Fcons (Fcons (build_string ("Yes"), Qt),
996 Fcons (Fcons (build_string ("No"), Qnil),
997 Qnil))))
998 && NILP (header))))
999 {
1000 OSStatus err = noErr;
1001 AlertStdCFStringAlertParamRec param;
1002 CFStringRef error_string, explanation_string;
1003 DialogRef alert;
1004 DialogItemIndex item_hit;
1005 Lisp_Object tem;
1006
1007 /* Force a redisplay before showing the dialog. If a frame is
1008 created just before showing the dialog, its contents may not
1009 have been fully drawn. */
1010 Fredisplay (Qt);
1011
1012 tem = Fstring_match (concat3 (build_string ("\\("),
1013 call0 (intern ("sentence-end")),
1014 build_string ("\\)\n")),
1015 XCAR (contents), Qnil);
1016 BLOCK_INPUT;
1017 if (NILP (tem))
1018 {
1019 error_string = cfstring_create_with_string (XCAR (contents));
1020 if (error_string == NULL)
1021 err = memFullErr;
1022 explanation_string = NULL;
1023 }
1024 else
1025 {
1026 tem = Fmatch_end (make_number (1));
1027 error_string =
1028 cfstring_create_with_string (Fsubstring (XCAR (contents),
1029 make_number (0), tem));
1030 if (error_string == NULL)
1031 err = memFullErr;
1032 else
1033 {
1034 XSETINT (tem, XINT (tem) + 1);
1035 explanation_string =
1036 cfstring_create_with_string (Fsubstring (XCAR (contents),
1037 tem, Qnil));
1038 if (explanation_string == NULL)
1039 {
1040 CFRelease (error_string);
1041 err = memFullErr;
1042 }
1043 }
1044 }
1045 if (err == noErr)
1046 err = GetStandardAlertDefaultParams (&param,
1047 kStdCFStringAlertVersionOne);
1048 if (err == noErr)
1049 {
1050 param.movable = true;
1051 param.position = kWindowAlertPositionParentWindow;
1052 if (NILP (header))
1053 {
1054 param.defaultText = CFSTR ("Yes");
1055 param.otherText = CFSTR ("No");
1056 #if 0
1057 param.cancelText = CFSTR ("Cancel");
1058 param.cancelButton = kAlertStdAlertCancelButton;
1059 #endif
1060 }
1061 err = CreateStandardAlert (kAlertNoteAlert, error_string,
1062 explanation_string, &param, &alert);
1063 CFRelease (error_string);
1064 if (explanation_string)
1065 CFRelease (explanation_string);
1066 }
1067 if (err == noErr)
1068 err = RunStandardAlert (alert, mac_dialog_modal_filter, &item_hit);
1069 UNBLOCK_INPUT;
1070
1071 if (err == noErr)
1072 {
1073 if (item_hit == kStdCancelItemIndex)
1074 Fsignal (Qquit, Qnil);
1075 else if (item_hit == kStdOkItemIndex)
1076 return Qt;
1077 else
1078 return Qnil;
1079 }
1080 }
1081 #endif
1082 #ifndef HAVE_DIALOGS
1083 /* Display a menu with these alternatives
1084 in the middle of frame F. */
1085 {
1086 Lisp_Object x, y, frame, newpos;
1087 XSETFRAME (frame, f);
1088 XSETINT (x, x_pixel_width (f) / 2);
1089 XSETINT (y, x_pixel_height (f) / 2);
1090 newpos = Fcons (Fcons (x, Fcons (y, Qnil)), Fcons (frame, Qnil));
1091
1092 return Fx_popup_menu (newpos,
1093 Fcons (Fcar (contents), Fcons (contents, Qnil)));
1094 }
1095 #else /* HAVE_DIALOGS */
1096 {
1097 Lisp_Object title;
1098 char *error_name;
1099 Lisp_Object selection;
1100 int specpdl_count = SPECPDL_INDEX ();
1101
1102 /* Decode the dialog items from what was specified. */
1103 title = Fcar (contents);
1104 CHECK_STRING (title);
1105
1106 list_of_panes (Fcons (contents, Qnil));
1107
1108 /* Display them in a dialog box. */
1109 record_unwind_protect (cleanup_popup_menu, Qnil);
1110 BLOCK_INPUT;
1111 selection = mac_dialog_show (f, 0, title, header, &error_name);
1112 UNBLOCK_INPUT;
1113 unbind_to (specpdl_count, Qnil);
1114
1115 if (error_name) error (error_name);
1116 return selection;
1117 }
1118 #endif /* HAVE_DIALOGS */
1119 }
1120
1121 /* Activate the menu bar of frame F.
1122 This is called from keyboard.c when it gets the
1123 MENU_BAR_ACTIVATE_EVENT out of the Emacs event queue.
1124
1125 To activate the menu bar, we use the button-press event location
1126 that was saved in saved_menu_event_location.
1127
1128 But first we recompute the menu bar contents (the whole tree).
1129
1130 The reason for saving the button event until here, instead of
1131 passing it to the toolkit right away, is that we can safely
1132 execute Lisp code. */
1133
1134 void
1135 x_activate_menubar (f)
1136 FRAME_PTR f;
1137 {
1138 SInt32 menu_choice;
1139 SInt16 menu_id, menu_item;
1140 extern Point saved_menu_event_location;
1141
1142 set_frame_menubar (f, 0, 1);
1143 BLOCK_INPUT;
1144
1145 popup_activated_flag = 1;
1146 menu_choice = MenuSelect (saved_menu_event_location);
1147 popup_activated_flag = 0;
1148 menu_id = HiWord (menu_choice);
1149 menu_item = LoWord (menu_choice);
1150
1151 #if !TARGET_API_MAC_CARBON
1152 if (menu_id == min_menu_id[MAC_MENU_M_APPLE])
1153 do_apple_menu (menu_item);
1154 else
1155 #endif
1156 if (menu_id)
1157 {
1158 MenuRef menu = GetMenuRef (menu_id);
1159
1160 if (menu)
1161 {
1162 UInt32 refcon;
1163
1164 GetMenuItemRefCon (menu, menu_item, &refcon);
1165 find_and_call_menu_selection (f, f->menu_bar_items_used,
1166 f->menu_bar_vector, (void *) refcon);
1167 }
1168 }
1169
1170 HiliteMenu (0);
1171
1172 UNBLOCK_INPUT;
1173 }
1174
1175 /* Find the menu selection and store it in the keyboard buffer.
1176 F is the frame the menu is on.
1177 MENU_BAR_ITEMS_USED is the length of VECTOR.
1178 VECTOR is an array of menu events for the whole menu. */
1179
1180 static void
1181 find_and_call_menu_selection (f, menu_bar_items_used, vector, client_data)
1182 FRAME_PTR f;
1183 int menu_bar_items_used;
1184 Lisp_Object vector;
1185 void *client_data;
1186 {
1187 Lisp_Object prefix, entry;
1188 Lisp_Object *subprefix_stack;
1189 int submenu_depth = 0;
1190 int i;
1191
1192 entry = Qnil;
1193 subprefix_stack = (Lisp_Object *) alloca (menu_bar_items_used * sizeof (Lisp_Object));
1194 prefix = Qnil;
1195 i = 0;
1196
1197 while (i < menu_bar_items_used)
1198 {
1199 if (EQ (XVECTOR (vector)->contents[i], Qnil))
1200 {
1201 subprefix_stack[submenu_depth++] = prefix;
1202 prefix = entry;
1203 i++;
1204 }
1205 else if (EQ (XVECTOR (vector)->contents[i], Qlambda))
1206 {
1207 prefix = subprefix_stack[--submenu_depth];
1208 i++;
1209 }
1210 else if (EQ (XVECTOR (vector)->contents[i], Qt))
1211 {
1212 prefix = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
1213 i += MENU_ITEMS_PANE_LENGTH;
1214 }
1215 else
1216 {
1217 entry = XVECTOR (vector)->contents[i + MENU_ITEMS_ITEM_VALUE];
1218 /* The EMACS_INT cast avoids a warning. There's no problem
1219 as long as pointers have enough bits to hold small integers. */
1220 if ((int) (EMACS_INT) client_data == i)
1221 {
1222 int j;
1223 struct input_event buf;
1224 Lisp_Object frame;
1225 EVENT_INIT (buf);
1226
1227 XSETFRAME (frame, f);
1228 buf.kind = MENU_BAR_EVENT;
1229 buf.frame_or_window = frame;
1230 buf.arg = frame;
1231 kbd_buffer_store_event (&buf);
1232
1233 for (j = 0; j < submenu_depth; j++)
1234 if (!NILP (subprefix_stack[j]))
1235 {
1236 buf.kind = MENU_BAR_EVENT;
1237 buf.frame_or_window = frame;
1238 buf.arg = subprefix_stack[j];
1239 kbd_buffer_store_event (&buf);
1240 }
1241
1242 if (!NILP (prefix))
1243 {
1244 buf.kind = MENU_BAR_EVENT;
1245 buf.frame_or_window = frame;
1246 buf.arg = prefix;
1247 kbd_buffer_store_event (&buf);
1248 }
1249
1250 buf.kind = MENU_BAR_EVENT;
1251 buf.frame_or_window = frame;
1252 buf.arg = entry;
1253 kbd_buffer_store_event (&buf);
1254
1255 return;
1256 }
1257 i += MENU_ITEMS_ITEM_LENGTH;
1258 }
1259 }
1260 }
1261
1262 /* Allocate a widget_value, blocking input. */
1263
1264 widget_value *
1265 xmalloc_widget_value ()
1266 {
1267 widget_value *value;
1268
1269 BLOCK_INPUT;
1270 value = malloc_widget_value ();
1271 UNBLOCK_INPUT;
1272
1273 return value;
1274 }
1275
1276 /* This recursively calls free_widget_value on the tree of widgets.
1277 It must free all data that was malloc'ed for these widget_values.
1278 In Emacs, many slots are pointers into the data of Lisp_Strings, and
1279 must be left alone. */
1280
1281 void
1282 free_menubar_widget_value_tree (wv)
1283 widget_value *wv;
1284 {
1285 if (! wv) return;
1286
1287 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
1288
1289 if (wv->contents && (wv->contents != (widget_value*)1))
1290 {
1291 free_menubar_widget_value_tree (wv->contents);
1292 wv->contents = (widget_value *) 0xDEADBEEF;
1293 }
1294 if (wv->next)
1295 {
1296 free_menubar_widget_value_tree (wv->next);
1297 wv->next = (widget_value *) 0xDEADBEEF;
1298 }
1299 BLOCK_INPUT;
1300 free_widget_value (wv);
1301 UNBLOCK_INPUT;
1302 }
1303 \f
1304 /* Set up data in menu_items for a menu bar item
1305 whose event type is ITEM_KEY (with string ITEM_NAME)
1306 and whose contents come from the list of keymaps MAPS. */
1307
1308 static int
1309 parse_single_submenu (item_key, item_name, maps)
1310 Lisp_Object item_key, item_name, maps;
1311 {
1312 Lisp_Object length;
1313 int len;
1314 Lisp_Object *mapvec;
1315 int i;
1316 int top_level_items = 0;
1317
1318 length = Flength (maps);
1319 len = XINT (length);
1320
1321 /* Convert the list MAPS into a vector MAPVEC. */
1322 mapvec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
1323 for (i = 0; i < len; i++)
1324 {
1325 mapvec[i] = Fcar (maps);
1326 maps = Fcdr (maps);
1327 }
1328
1329 /* Loop over the given keymaps, making a pane for each map.
1330 But don't make a pane that is empty--ignore that map instead. */
1331 for (i = 0; i < len; i++)
1332 {
1333 if (!KEYMAPP (mapvec[i]))
1334 {
1335 /* Here we have a command at top level in the menu bar
1336 as opposed to a submenu. */
1337 top_level_items = 1;
1338 push_menu_pane (Qnil, Qnil);
1339 push_menu_item (item_name, Qt, item_key, mapvec[i],
1340 Qnil, Qnil, Qnil, Qnil);
1341 }
1342 else
1343 {
1344 Lisp_Object prompt;
1345 prompt = Fkeymap_prompt (mapvec[i]);
1346 single_keymap_panes (mapvec[i],
1347 !NILP (prompt) ? prompt : item_name,
1348 item_key, 0, 10);
1349 }
1350 }
1351
1352 return top_level_items;
1353 }
1354
1355 /* Create a tree of widget_value objects
1356 representing the panes and items
1357 in menu_items starting at index START, up to index END. */
1358
1359 static widget_value *
1360 digest_single_submenu (start, end, top_level_items)
1361 int start, end, top_level_items;
1362 {
1363 widget_value *wv, *prev_wv, *save_wv, *first_wv;
1364 int i;
1365 int submenu_depth = 0;
1366 widget_value **submenu_stack;
1367 int panes_seen = 0;
1368
1369 submenu_stack
1370 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
1371 wv = xmalloc_widget_value ();
1372 wv->name = "menu";
1373 wv->value = 0;
1374 wv->enabled = 1;
1375 wv->button_type = BUTTON_TYPE_NONE;
1376 wv->help = Qnil;
1377 first_wv = wv;
1378 save_wv = 0;
1379 prev_wv = 0;
1380
1381 /* Loop over all panes and items made by the preceding call
1382 to parse_single_submenu and construct a tree of widget_value objects.
1383 Ignore the panes and items used by previous calls to
1384 digest_single_submenu, even though those are also in menu_items. */
1385 i = start;
1386 while (i < end)
1387 {
1388 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1389 {
1390 submenu_stack[submenu_depth++] = save_wv;
1391 save_wv = prev_wv;
1392 prev_wv = 0;
1393 i++;
1394 }
1395 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1396 {
1397 prev_wv = save_wv;
1398 save_wv = submenu_stack[--submenu_depth];
1399 i++;
1400 }
1401 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
1402 && submenu_depth != 0)
1403 i += MENU_ITEMS_PANE_LENGTH;
1404 /* Ignore a nil in the item list.
1405 It's meaningful only for dialog boxes. */
1406 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
1407 i += 1;
1408 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
1409 {
1410 /* Create a new pane. */
1411 Lisp_Object pane_name, prefix;
1412 char *pane_string;
1413
1414 panes_seen++;
1415
1416 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1417 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1418
1419 #ifndef HAVE_MULTILINGUAL_MENU
1420 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
1421 {
1422 pane_name = ENCODE_MENU_STRING (pane_name);
1423 AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
1424 }
1425 #endif
1426 pane_string = (NILP (pane_name)
1427 ? "" : (char *) SDATA (pane_name));
1428 /* If there is just one top-level pane, put all its items directly
1429 under the top-level menu. */
1430 if (menu_items_n_panes == 1)
1431 pane_string = "";
1432
1433 /* If the pane has a meaningful name,
1434 make the pane a top-level menu item
1435 with its items as a submenu beneath it. */
1436 if (strcmp (pane_string, ""))
1437 {
1438 wv = xmalloc_widget_value ();
1439 if (save_wv)
1440 save_wv->next = wv;
1441 else
1442 first_wv->contents = wv;
1443 wv->lname = pane_name;
1444 /* Set value to 1 so update_submenu_strings can handle '@' */
1445 wv->value = (char *)1;
1446 wv->enabled = 1;
1447 wv->button_type = BUTTON_TYPE_NONE;
1448 wv->help = Qnil;
1449 save_wv = wv;
1450 }
1451 else
1452 save_wv = first_wv;
1453
1454 prev_wv = 0;
1455 i += MENU_ITEMS_PANE_LENGTH;
1456 }
1457 else
1458 {
1459 /* Create a new item within current pane. */
1460 Lisp_Object item_name, enable, descrip, def, type, selected;
1461 Lisp_Object help;
1462
1463 /* All items should be contained in panes. */
1464 if (panes_seen == 0)
1465 abort ();
1466
1467 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
1468 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
1469 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
1470 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
1471 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
1472 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
1473 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
1474
1475 #ifndef HAVE_MULTILINGUAL_MENU
1476 if (STRING_MULTIBYTE (item_name))
1477 {
1478 item_name = ENCODE_MENU_STRING (item_name);
1479 AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
1480 }
1481
1482 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
1483 {
1484 descrip = ENCODE_MENU_STRING (descrip);
1485 AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
1486 }
1487 #endif /* not HAVE_MULTILINGUAL_MENU */
1488
1489 wv = xmalloc_widget_value ();
1490 if (prev_wv)
1491 prev_wv->next = wv;
1492 else
1493 save_wv->contents = wv;
1494
1495 wv->lname = item_name;
1496 if (!NILP (descrip))
1497 wv->lkey = descrip;
1498 wv->value = 0;
1499 /* The EMACS_INT cast avoids a warning. There's no problem
1500 as long as pointers have enough bits to hold small integers. */
1501 wv->call_data = (!NILP (def) ? (void *) (EMACS_INT) i : 0);
1502 wv->enabled = !NILP (enable);
1503
1504 if (NILP (type))
1505 wv->button_type = BUTTON_TYPE_NONE;
1506 else if (EQ (type, QCradio))
1507 wv->button_type = BUTTON_TYPE_RADIO;
1508 else if (EQ (type, QCtoggle))
1509 wv->button_type = BUTTON_TYPE_TOGGLE;
1510 else
1511 abort ();
1512
1513 wv->selected = !NILP (selected);
1514 if (! STRINGP (help))
1515 help = Qnil;
1516
1517 wv->help = help;
1518
1519 prev_wv = wv;
1520
1521 i += MENU_ITEMS_ITEM_LENGTH;
1522 }
1523 }
1524
1525 /* If we have just one "menu item"
1526 that was originally a button, return it by itself. */
1527 if (top_level_items && first_wv->contents && first_wv->contents->next == 0)
1528 {
1529 wv = first_wv->contents;
1530 free_widget_value (first_wv);
1531 return wv;
1532 }
1533
1534 return first_wv;
1535 }
1536
1537 /* Walk through the widget_value tree starting at FIRST_WV and update
1538 the char * pointers from the corresponding lisp values.
1539 We do this after building the whole tree, since GC may happen while the
1540 tree is constructed, and small strings are relocated. So we must wait
1541 until no GC can happen before storing pointers into lisp values. */
1542 static void
1543 update_submenu_strings (first_wv)
1544 widget_value *first_wv;
1545 {
1546 widget_value *wv;
1547
1548 for (wv = first_wv; wv; wv = wv->next)
1549 {
1550 if (STRINGP (wv->lname))
1551 {
1552 wv->name = SDATA (wv->lname);
1553
1554 /* Ignore the @ that means "separate pane".
1555 This is a kludge, but this isn't worth more time. */
1556 if (wv->value == (char *)1)
1557 {
1558 if (wv->name[0] == '@')
1559 wv->name++;
1560 wv->value = 0;
1561 }
1562 }
1563
1564 if (STRINGP (wv->lkey))
1565 wv->key = SDATA (wv->lkey);
1566
1567 if (wv->contents)
1568 update_submenu_strings (wv->contents);
1569 }
1570 }
1571
1572 \f
1573 #if TARGET_API_MAC_CARBON
1574 extern Lisp_Object Vshow_help_function;
1575
1576 static Lisp_Object
1577 restore_show_help_function (old_show_help_function)
1578 Lisp_Object old_show_help_function;
1579 {
1580 Vshow_help_function = old_show_help_function;
1581
1582 return Qnil;
1583 }
1584
1585 static pascal OSStatus
1586 menu_target_item_handler (next_handler, event, data)
1587 EventHandlerCallRef next_handler;
1588 EventRef event;
1589 void *data;
1590 {
1591 OSStatus err;
1592 MenuRef menu;
1593 MenuItemIndex menu_item;
1594 Lisp_Object help;
1595 GrafPtr port;
1596 int specpdl_count = SPECPDL_INDEX ();
1597
1598 /* Don't be bothered with the overflowed toolbar items menu. */
1599 if (!popup_activated ())
1600 return eventNotHandledErr;
1601
1602 err = GetEventParameter (event, kEventParamDirectObject, typeMenuRef,
1603 NULL, sizeof (MenuRef), NULL, &menu);
1604 if (err == noErr)
1605 err = GetEventParameter (event, kEventParamMenuItemIndex,
1606 typeMenuItemIndex, NULL,
1607 sizeof (MenuItemIndex), NULL, &menu_item);
1608 if (err == noErr)
1609 err = GetMenuItemProperty (menu, menu_item,
1610 MAC_EMACS_CREATOR_CODE, 'help',
1611 sizeof (Lisp_Object), NULL, &help);
1612 if (err != noErr)
1613 help = Qnil;
1614
1615 /* Temporarily bind Vshow_help_function to Qnil because we don't
1616 want tooltips during menu tracking. */
1617 record_unwind_protect (restore_show_help_function, Vshow_help_function);
1618 Vshow_help_function = Qnil;
1619 GetPort (&port);
1620 show_help_echo (help, Qnil, Qnil, Qnil, 1);
1621 SetPort (port);
1622 unbind_to (specpdl_count, Qnil);
1623
1624 return err == noErr ? noErr : eventNotHandledErr;
1625 }
1626
1627 OSStatus
1628 install_menu_target_item_handler ()
1629 {
1630 static const EventTypeSpec specs[] =
1631 {{kEventClassMenu, kEventMenuTargetItem}};
1632
1633 return InstallApplicationEventHandler (NewEventHandlerUPP
1634 (menu_target_item_handler),
1635 GetEventTypeCount (specs),
1636 specs, NULL, NULL);
1637 }
1638 #endif /* TARGET_API_MAC_CARBON */
1639
1640 /* Event handler function that pops down a menu on C-g. We can only pop
1641 down menus if CancelMenuTracking is present (OSX 10.3 or later). */
1642
1643 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
1644 static pascal OSStatus
1645 menu_quit_handler (nextHandler, theEvent, userData)
1646 EventHandlerCallRef nextHandler;
1647 EventRef theEvent;
1648 void* userData;
1649 {
1650 OSStatus err;
1651 UInt32 keyCode;
1652 UInt32 keyModifiers;
1653
1654 err = GetEventParameter (theEvent, kEventParamKeyCode,
1655 typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
1656
1657 if (err == noErr)
1658 err = GetEventParameter (theEvent, kEventParamKeyModifiers,
1659 typeUInt32, NULL, sizeof(UInt32),
1660 NULL, &keyModifiers);
1661
1662 if (err == noErr && mac_quit_char_key_p (keyModifiers, keyCode))
1663 {
1664 MenuRef menu = userData != 0
1665 ? (MenuRef)userData : AcquireRootMenu ();
1666
1667 CancelMenuTracking (menu, true, 0);
1668 if (!userData) ReleaseMenu (menu);
1669 return noErr;
1670 }
1671
1672 return CallNextEventHandler (nextHandler, theEvent);
1673 }
1674 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
1675
1676 /* Add event handler to all menus that belong to KIND so we can detect
1677 C-g. ROOT_MENU is the root menu of the tracking session to dismiss
1678 when C-g is detected. NULL means the menu bar. If
1679 CancelMenuTracking isn't available, do nothing. */
1680
1681 static void
1682 install_menu_quit_handler (kind, root_menu)
1683 enum mac_menu_kind kind;
1684 MenuRef root_menu;
1685 {
1686 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
1687 static const EventTypeSpec typesList[] =
1688 {{kEventClassKeyboard, kEventRawKeyDown}};
1689 int id;
1690
1691 #if MAC_OS_X_VERSION_MIN_REQUIRED == 1020
1692 if (CancelMenuTracking == NULL)
1693 return;
1694 #endif
1695 for (id = min_menu_id[kind]; id < min_menu_id[kind + 1]; id++)
1696 {
1697 MenuRef menu = GetMenuRef (id);
1698
1699 if (menu == NULL)
1700 break;
1701 InstallMenuEventHandler (menu, menu_quit_handler,
1702 GetEventTypeCount (typesList),
1703 typesList, root_menu, NULL);
1704 }
1705 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
1706 }
1707
1708 /* Set the contents of the menubar widgets of frame F.
1709 The argument FIRST_TIME is currently ignored;
1710 it is set the first time this is called, from initialize_frame_menubar. */
1711
1712 void
1713 set_frame_menubar (f, first_time, deep_p)
1714 FRAME_PTR f;
1715 int first_time;
1716 int deep_p;
1717 {
1718 int menubar_widget = f->output_data.mac->menubar_widget;
1719 Lisp_Object items;
1720 widget_value *wv, *first_wv, *prev_wv = 0;
1721 int i, last_i = 0;
1722 int *submenu_start, *submenu_end;
1723 int *submenu_top_level_items, *submenu_n_panes;
1724
1725 XSETFRAME (Vmenu_updating_frame, f);
1726
1727 /* This seems to be unnecessary for Carbon. */
1728 #if 0
1729 if (! menubar_widget)
1730 deep_p = 1;
1731 else if (pending_menu_activation && !deep_p)
1732 deep_p = 1;
1733 #endif
1734
1735 if (deep_p)
1736 {
1737 /* Make a widget-value tree representing the entire menu trees. */
1738
1739 struct buffer *prev = current_buffer;
1740 Lisp_Object buffer;
1741 int specpdl_count = SPECPDL_INDEX ();
1742 int previous_menu_items_used = f->menu_bar_items_used;
1743 Lisp_Object *previous_items
1744 = (Lisp_Object *) alloca (previous_menu_items_used
1745 * sizeof (Lisp_Object));
1746
1747 /* If we are making a new widget, its contents are empty,
1748 do always reinitialize them. */
1749 if (! menubar_widget)
1750 previous_menu_items_used = 0;
1751
1752 buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->buffer;
1753 specbind (Qinhibit_quit, Qt);
1754 /* Don't let the debugger step into this code
1755 because it is not reentrant. */
1756 specbind (Qdebug_on_next_call, Qnil);
1757
1758 record_unwind_save_match_data ();
1759 if (NILP (Voverriding_local_map_menu_flag))
1760 {
1761 specbind (Qoverriding_terminal_local_map, Qnil);
1762 specbind (Qoverriding_local_map, Qnil);
1763 }
1764
1765 set_buffer_internal_1 (XBUFFER (buffer));
1766
1767 /* Run the Lucid hook. */
1768 safe_run_hooks (Qactivate_menubar_hook);
1769
1770 /* If it has changed current-menubar from previous value,
1771 really recompute the menubar from the value. */
1772 if (! NILP (Vlucid_menu_bar_dirty_flag))
1773 call0 (Qrecompute_lucid_menubar);
1774 safe_run_hooks (Qmenu_bar_update_hook);
1775 FRAME_MENU_BAR_ITEMS (f) = menu_bar_items (FRAME_MENU_BAR_ITEMS (f));
1776
1777 items = FRAME_MENU_BAR_ITEMS (f);
1778
1779 /* Save the frame's previous menu bar contents data. */
1780 if (previous_menu_items_used)
1781 bcopy (XVECTOR (f->menu_bar_vector)->contents, previous_items,
1782 previous_menu_items_used * sizeof (Lisp_Object));
1783
1784 /* Fill in menu_items with the current menu bar contents.
1785 This can evaluate Lisp code. */
1786 save_menu_items ();
1787
1788 menu_items = f->menu_bar_vector;
1789 menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
1790 submenu_start = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
1791 submenu_end = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
1792 submenu_n_panes = (int *) alloca (XVECTOR (items)->size * sizeof (int));
1793 submenu_top_level_items
1794 = (int *) alloca (XVECTOR (items)->size * sizeof (int *));
1795 init_menu_items ();
1796 for (i = 0; i < XVECTOR (items)->size; i += 4)
1797 {
1798 Lisp_Object key, string, maps;
1799
1800 last_i = i;
1801
1802 key = XVECTOR (items)->contents[i];
1803 string = XVECTOR (items)->contents[i + 1];
1804 maps = XVECTOR (items)->contents[i + 2];
1805 if (NILP (string))
1806 break;
1807
1808 submenu_start[i] = menu_items_used;
1809
1810 menu_items_n_panes = 0;
1811 submenu_top_level_items[i]
1812 = parse_single_submenu (key, string, maps);
1813 submenu_n_panes[i] = menu_items_n_panes;
1814
1815 submenu_end[i] = menu_items_used;
1816 }
1817
1818 finish_menu_items ();
1819
1820 /* Convert menu_items into widget_value trees
1821 to display the menu. This cannot evaluate Lisp code. */
1822
1823 wv = xmalloc_widget_value ();
1824 wv->name = "menubar";
1825 wv->value = 0;
1826 wv->enabled = 1;
1827 wv->button_type = BUTTON_TYPE_NONE;
1828 wv->help = Qnil;
1829 first_wv = wv;
1830
1831 for (i = 0; i < last_i; i += 4)
1832 {
1833 menu_items_n_panes = submenu_n_panes[i];
1834 wv = digest_single_submenu (submenu_start[i], submenu_end[i],
1835 submenu_top_level_items[i]);
1836 if (prev_wv)
1837 prev_wv->next = wv;
1838 else
1839 first_wv->contents = wv;
1840 /* Don't set wv->name here; GC during the loop might relocate it. */
1841 wv->enabled = 1;
1842 wv->button_type = BUTTON_TYPE_NONE;
1843 prev_wv = wv;
1844 }
1845
1846 set_buffer_internal_1 (prev);
1847
1848 /* If there has been no change in the Lisp-level contents
1849 of the menu bar, skip redisplaying it. Just exit. */
1850
1851 /* Compare the new menu items with the ones computed last time. */
1852 for (i = 0; i < previous_menu_items_used; i++)
1853 if (menu_items_used == i
1854 || (!EQ (previous_items[i], XVECTOR (menu_items)->contents[i])))
1855 break;
1856 if (i == menu_items_used && i == previous_menu_items_used && i != 0)
1857 {
1858 /* The menu items have not changed. Don't bother updating
1859 the menus in any form, since it would be a no-op. */
1860 free_menubar_widget_value_tree (first_wv);
1861 discard_menu_items ();
1862 unbind_to (specpdl_count, Qnil);
1863 return;
1864 }
1865
1866 /* The menu items are different, so store them in the frame. */
1867 f->menu_bar_vector = menu_items;
1868 f->menu_bar_items_used = menu_items_used;
1869
1870 /* This calls restore_menu_items to restore menu_items, etc.,
1871 as they were outside. */
1872 unbind_to (specpdl_count, Qnil);
1873
1874 /* Now GC cannot happen during the lifetime of the widget_value,
1875 so it's safe to store data from a Lisp_String. */
1876 wv = first_wv->contents;
1877 for (i = 0; i < XVECTOR (items)->size; i += 4)
1878 {
1879 Lisp_Object string;
1880 string = XVECTOR (items)->contents[i + 1];
1881 if (NILP (string))
1882 break;
1883 wv->name = (char *) SDATA (string);
1884 update_submenu_strings (wv->contents);
1885 wv = wv->next;
1886 }
1887
1888 }
1889 else
1890 {
1891 /* Make a widget-value tree containing
1892 just the top level menu bar strings. */
1893
1894 wv = xmalloc_widget_value ();
1895 wv->name = "menubar";
1896 wv->value = 0;
1897 wv->enabled = 1;
1898 wv->button_type = BUTTON_TYPE_NONE;
1899 wv->help = Qnil;
1900 first_wv = wv;
1901
1902 items = FRAME_MENU_BAR_ITEMS (f);
1903 for (i = 0; i < XVECTOR (items)->size; i += 4)
1904 {
1905 Lisp_Object string;
1906
1907 string = XVECTOR (items)->contents[i + 1];
1908 if (NILP (string))
1909 break;
1910
1911 wv = xmalloc_widget_value ();
1912 wv->name = (char *) SDATA (string);
1913 wv->value = 0;
1914 wv->enabled = 1;
1915 wv->button_type = BUTTON_TYPE_NONE;
1916 wv->help = Qnil;
1917 /* This prevents lwlib from assuming this
1918 menu item is really supposed to be empty. */
1919 /* The EMACS_INT cast avoids a warning.
1920 This value just has to be different from small integers. */
1921 wv->call_data = (void *) (EMACS_INT) (-1);
1922
1923 if (prev_wv)
1924 prev_wv->next = wv;
1925 else
1926 first_wv->contents = wv;
1927 prev_wv = wv;
1928 }
1929
1930 /* Forget what we thought we knew about what is in the
1931 detailed contents of the menu bar menus.
1932 Changing the top level always destroys the contents. */
1933 f->menu_bar_items_used = 0;
1934 }
1935
1936 /* Create or update the menu bar widget. */
1937
1938 BLOCK_INPUT;
1939
1940 /* Non-null value to indicate menubar has already been "created". */
1941 f->output_data.mac->menubar_widget = 1;
1942
1943 fill_menubar (first_wv->contents, deep_p);
1944
1945 /* Add event handler so we can detect C-g. */
1946 install_menu_quit_handler (MAC_MENU_MENU_BAR, NULL);
1947 install_menu_quit_handler (MAC_MENU_MENU_BAR_SUB, NULL);
1948 free_menubar_widget_value_tree (first_wv);
1949
1950 UNBLOCK_INPUT;
1951 }
1952
1953 /* Get rid of the menu bar of frame F, and free its storage.
1954 This is used when deleting a frame, and when turning off the menu bar. */
1955
1956 void
1957 free_frame_menubar (f)
1958 FRAME_PTR f;
1959 {
1960 f->output_data.mac->menubar_widget = 0;
1961 }
1962
1963 \f
1964 static Lisp_Object
1965 pop_down_menu (arg)
1966 Lisp_Object arg;
1967 {
1968 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
1969 FRAME_PTR f = p->pointer;
1970 MenuRef menu = GetMenuRef (min_menu_id[MAC_MENU_POPUP]);
1971
1972 BLOCK_INPUT;
1973
1974 /* Must reset this manually because the button release event is not
1975 passed to Emacs event loop. */
1976 FRAME_MAC_DISPLAY_INFO (f)->grabbed = 0;
1977
1978 /* delete all menus */
1979 dispose_menus (MAC_MENU_POPUP_SUB, 0);
1980 DeleteMenu (min_menu_id[MAC_MENU_POPUP]);
1981 DisposeMenu (menu);
1982
1983 UNBLOCK_INPUT;
1984
1985 return Qnil;
1986 }
1987
1988 /* Mac_menu_show actually displays a menu using the panes and items in
1989 menu_items and returns the value selected from it; we assume input
1990 is blocked by the caller. */
1991
1992 /* F is the frame the menu is for.
1993 X and Y are the frame-relative specified position,
1994 relative to the inside upper left corner of the frame F.
1995 FOR_CLICK is nonzero if this menu was invoked for a mouse click.
1996 KEYMAPS is 1 if this menu was specified with keymaps;
1997 in that case, we return a list containing the chosen item's value
1998 and perhaps also the pane's prefix.
1999 TITLE is the specified menu title.
2000 ERROR is a place to store an error message string in case of failure.
2001 (We return nil on failure, but the value doesn't actually matter.) */
2002
2003 static Lisp_Object
2004 mac_menu_show (f, x, y, for_click, keymaps, title, error)
2005 FRAME_PTR f;
2006 int x;
2007 int y;
2008 int for_click;
2009 int keymaps;
2010 Lisp_Object title;
2011 char **error;
2012 {
2013 int i;
2014 int menu_item_choice;
2015 UInt32 menu_item_selection;
2016 MenuRef menu;
2017 widget_value *wv, *save_wv = 0, *first_wv = 0, *prev_wv = 0;
2018 widget_value **submenu_stack
2019 = (widget_value **) alloca (menu_items_used * sizeof (widget_value *));
2020 Lisp_Object *subprefix_stack
2021 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object));
2022 int submenu_depth = 0;
2023
2024 int first_pane;
2025 int specpdl_count = SPECPDL_INDEX ();
2026
2027 *error = NULL;
2028
2029 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
2030 {
2031 *error = "Empty menu";
2032 return Qnil;
2033 }
2034
2035 /* Create a tree of widget_value objects
2036 representing the panes and their items. */
2037 wv = xmalloc_widget_value ();
2038 wv->name = "menu";
2039 wv->value = 0;
2040 wv->enabled = 1;
2041 wv->button_type = BUTTON_TYPE_NONE;
2042 wv->help = Qnil;
2043 first_wv = wv;
2044 first_pane = 1;
2045
2046 /* Loop over all panes and items, filling in the tree. */
2047 i = 0;
2048 while (i < menu_items_used)
2049 {
2050 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
2051 {
2052 submenu_stack[submenu_depth++] = save_wv;
2053 save_wv = prev_wv;
2054 prev_wv = 0;
2055 first_pane = 1;
2056 i++;
2057 }
2058 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
2059 {
2060 prev_wv = save_wv;
2061 save_wv = submenu_stack[--submenu_depth];
2062 first_pane = 0;
2063 i++;
2064 }
2065 else if (EQ (XVECTOR (menu_items)->contents[i], Qt)
2066 && submenu_depth != 0)
2067 i += MENU_ITEMS_PANE_LENGTH;
2068 /* Ignore a nil in the item list.
2069 It's meaningful only for dialog boxes. */
2070 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2071 i += 1;
2072 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2073 {
2074 /* Create a new pane. */
2075 Lisp_Object pane_name, prefix;
2076 char *pane_string;
2077
2078 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
2079 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
2080
2081 #ifndef HAVE_MULTILINGUAL_MENU
2082 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
2083 {
2084 pane_name = ENCODE_MENU_STRING (pane_name);
2085 AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
2086 }
2087 #endif
2088 pane_string = (NILP (pane_name)
2089 ? "" : (char *) SDATA (pane_name));
2090 /* If there is just one top-level pane, put all its items directly
2091 under the top-level menu. */
2092 if (menu_items_n_panes == 1)
2093 pane_string = "";
2094
2095 /* If the pane has a meaningful name,
2096 make the pane a top-level menu item
2097 with its items as a submenu beneath it. */
2098 if (!keymaps && strcmp (pane_string, ""))
2099 {
2100 wv = xmalloc_widget_value ();
2101 if (save_wv)
2102 save_wv->next = wv;
2103 else
2104 first_wv->contents = wv;
2105 wv->name = pane_string;
2106 if (keymaps && !NILP (prefix))
2107 wv->name++;
2108 wv->value = 0;
2109 wv->enabled = 1;
2110 wv->button_type = BUTTON_TYPE_NONE;
2111 wv->help = Qnil;
2112 save_wv = wv;
2113 prev_wv = 0;
2114 }
2115 else if (first_pane)
2116 {
2117 save_wv = wv;
2118 prev_wv = 0;
2119 }
2120 first_pane = 0;
2121 i += MENU_ITEMS_PANE_LENGTH;
2122 }
2123 else
2124 {
2125 /* Create a new item within current pane. */
2126 Lisp_Object item_name, enable, descrip, def, type, selected, help;
2127 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
2128 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
2129 descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
2130 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
2131 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
2132 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
2133 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
2134
2135 #ifndef HAVE_MULTILINGUAL_MENU
2136 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
2137 {
2138 item_name = ENCODE_MENU_STRING (item_name);
2139 AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
2140 }
2141
2142 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
2143 {
2144 descrip = ENCODE_MENU_STRING (descrip);
2145 AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
2146 }
2147 #endif /* not HAVE_MULTILINGUAL_MENU */
2148
2149 wv = xmalloc_widget_value ();
2150 if (prev_wv)
2151 prev_wv->next = wv;
2152 else
2153 save_wv->contents = wv;
2154 wv->name = (char *) SDATA (item_name);
2155 if (!NILP (descrip))
2156 wv->key = (char *) SDATA (descrip);
2157 wv->value = 0;
2158 /* Use the contents index as call_data, since we are
2159 restricted to 16-bits. */
2160 wv->call_data = !NILP (def) ? (void *) (EMACS_INT) i : 0;
2161 wv->enabled = !NILP (enable);
2162
2163 if (NILP (type))
2164 wv->button_type = BUTTON_TYPE_NONE;
2165 else if (EQ (type, QCtoggle))
2166 wv->button_type = BUTTON_TYPE_TOGGLE;
2167 else if (EQ (type, QCradio))
2168 wv->button_type = BUTTON_TYPE_RADIO;
2169 else
2170 abort ();
2171
2172 wv->selected = !NILP (selected);
2173
2174 if (! STRINGP (help))
2175 help = Qnil;
2176
2177 wv->help = help;
2178
2179 prev_wv = wv;
2180
2181 i += MENU_ITEMS_ITEM_LENGTH;
2182 }
2183 }
2184
2185 /* Deal with the title, if it is non-nil. */
2186 if (!NILP (title))
2187 {
2188 widget_value *wv_title = xmalloc_widget_value ();
2189 widget_value *wv_sep = xmalloc_widget_value ();
2190
2191 /* Maybe replace this separator with a bitmap or owner-draw item
2192 so that it looks better. Having two separators looks odd. */
2193 wv_sep->name = "--";
2194 wv_sep->next = first_wv->contents;
2195 wv_sep->help = Qnil;
2196
2197 #ifndef HAVE_MULTILINGUAL_MENU
2198 if (STRING_MULTIBYTE (title))
2199 title = ENCODE_MENU_STRING (title);
2200 #endif
2201
2202 wv_title->name = (char *) SDATA (title);
2203 wv_title->enabled = FALSE;
2204 wv_title->title = TRUE;
2205 wv_title->button_type = BUTTON_TYPE_NONE;
2206 wv_title->help = Qnil;
2207 wv_title->next = wv_sep;
2208 first_wv->contents = wv_title;
2209 }
2210
2211 /* Actually create the menu. */
2212 menu = NewMenu (min_menu_id[MAC_MENU_POPUP], "\p");
2213 InsertMenu (menu, -1);
2214 fill_menu (menu, first_wv->contents, MAC_MENU_POPUP_SUB,
2215 min_menu_id[MAC_MENU_POPUP_SUB]);
2216
2217 /* Free the widget_value objects we used to specify the
2218 contents. */
2219 free_menubar_widget_value_tree (first_wv);
2220
2221 /* Adjust coordinates to be root-window-relative. */
2222 x += f->left_pos + FRAME_OUTER_TO_INNER_DIFF_X (f);
2223 y += f->top_pos + FRAME_OUTER_TO_INNER_DIFF_Y (f);
2224
2225 /* No selection has been chosen yet. */
2226 menu_item_selection = 0;
2227
2228 record_unwind_protect (pop_down_menu, make_save_value (f, 0));
2229
2230 /* Add event handler so we can detect C-g. */
2231 install_menu_quit_handler (MAC_MENU_POPUP, menu);
2232 install_menu_quit_handler (MAC_MENU_POPUP_SUB, menu);
2233
2234 /* Display the menu. */
2235 popup_activated_flag = 1;
2236 menu_item_choice = PopUpMenuSelect (menu, y, x, 0);
2237 popup_activated_flag = 0;
2238
2239 /* Get the refcon to find the correct item */
2240 if (menu_item_choice)
2241 {
2242 MenuRef sel_menu = GetMenuRef (HiWord (menu_item_choice));
2243
2244 if (sel_menu)
2245 GetMenuItemRefCon (sel_menu, LoWord (menu_item_choice),
2246 &menu_item_selection);
2247 }
2248
2249 unbind_to (specpdl_count, Qnil);
2250
2251 /* Find the selected item, and its pane, to return
2252 the proper value. */
2253 if (menu_item_selection != 0)
2254 {
2255 Lisp_Object prefix, entry;
2256
2257 prefix = entry = Qnil;
2258 i = 0;
2259 while (i < menu_items_used)
2260 {
2261 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
2262 {
2263 subprefix_stack[submenu_depth++] = prefix;
2264 prefix = entry;
2265 i++;
2266 }
2267 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
2268 {
2269 prefix = subprefix_stack[--submenu_depth];
2270 i++;
2271 }
2272 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2273 {
2274 prefix
2275 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2276 i += MENU_ITEMS_PANE_LENGTH;
2277 }
2278 /* Ignore a nil in the item list.
2279 It's meaningful only for dialog boxes. */
2280 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
2281 i += 1;
2282 else
2283 {
2284 entry
2285 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2286 if (menu_item_selection == i)
2287 {
2288 if (keymaps != 0)
2289 {
2290 int j;
2291
2292 entry = Fcons (entry, Qnil);
2293 if (!NILP (prefix))
2294 entry = Fcons (prefix, entry);
2295 for (j = submenu_depth - 1; j >= 0; j--)
2296 if (!NILP (subprefix_stack[j]))
2297 entry = Fcons (subprefix_stack[j], entry);
2298 }
2299 return entry;
2300 }
2301 i += MENU_ITEMS_ITEM_LENGTH;
2302 }
2303 }
2304 }
2305 else if (!for_click)
2306 /* Make "Cancel" equivalent to C-g. */
2307 Fsignal (Qquit, Qnil);
2308
2309 return Qnil;
2310 }
2311 \f
2312
2313 #ifdef HAVE_DIALOGS
2314 /* Construct native Mac OS dialog based on widget_value tree. */
2315
2316 #if TARGET_API_MAC_CARBON
2317
2318 #define DIALOG_BUTTON_COMMAND_ID_OFFSET 'Bt\0\0'
2319 #define DIALOG_BUTTON_COMMAND_ID_P(id) \
2320 (((id) & ~0xffff) == DIALOG_BUTTON_COMMAND_ID_OFFSET)
2321 #define DIALOG_BUTTON_COMMAND_ID_VALUE(id) \
2322 ((id) - DIALOG_BUTTON_COMMAND_ID_OFFSET)
2323 #define DIALOG_BUTTON_MAKE_COMMAND_ID(value) \
2324 ((value) + DIALOG_BUTTON_COMMAND_ID_OFFSET)
2325
2326 extern EMACS_TIME timer_check P_ ((int));
2327 static int quit_dialog_event_loop;
2328
2329 static pascal OSStatus
2330 mac_handle_dialog_event (next_handler, event, data)
2331 EventHandlerCallRef next_handler;
2332 EventRef event;
2333 void *data;
2334 {
2335 OSStatus err, result = eventNotHandledErr;
2336 WindowRef window = (WindowRef) data;
2337
2338 switch (GetEventClass (event))
2339 {
2340 case kEventClassCommand:
2341 {
2342 HICommand command;
2343
2344 err = GetEventParameter (event, kEventParamDirectObject,
2345 typeHICommand, NULL, sizeof (HICommand),
2346 NULL, &command);
2347 if (err == noErr)
2348 if (DIALOG_BUTTON_COMMAND_ID_P (command.commandID))
2349 {
2350 SetWRefCon (window, command.commandID);
2351 quit_dialog_event_loop = 1;
2352 break;
2353 }
2354
2355 result = CallNextEventHandler (next_handler, event);
2356 }
2357 break;
2358
2359 case kEventClassKeyboard:
2360 {
2361 OSStatus result;
2362 char char_code;
2363
2364 result = CallNextEventHandler (next_handler, event);
2365 if (result != eventNotHandledErr)
2366 break;
2367
2368 err = GetEventParameter (event, kEventParamKeyMacCharCodes,
2369 typeChar, NULL, sizeof (char),
2370 NULL, &char_code);
2371 if (err == noErr)
2372 switch (char_code)
2373 {
2374 case kEscapeCharCode:
2375 quit_dialog_event_loop = 1;
2376 break;
2377
2378 default:
2379 {
2380 UInt32 modifiers, key_code;
2381
2382 err = GetEventParameter (event, kEventParamKeyModifiers,
2383 typeUInt32, NULL, sizeof (UInt32),
2384 NULL, &modifiers);
2385 if (err == noErr)
2386 err = GetEventParameter (event, kEventParamKeyCode,
2387 typeUInt32, NULL, sizeof (UInt32),
2388 NULL, &key_code);
2389 if (err == noErr)
2390 if (mac_quit_char_key_p (modifiers, key_code))
2391 quit_dialog_event_loop = 1;
2392 }
2393 break;
2394 }
2395 }
2396 break;
2397
2398 default:
2399 abort ();
2400 }
2401
2402 if (quit_dialog_event_loop)
2403 {
2404 err = QuitEventLoop (GetCurrentEventLoop ());
2405 if (err == noErr)
2406 result = noErr;
2407 }
2408
2409 return result;
2410 }
2411
2412 static OSStatus
2413 install_dialog_event_handler (window)
2414 WindowRef window;
2415 {
2416 static const EventTypeSpec specs[] =
2417 {{kEventClassCommand, kEventCommandProcess},
2418 {kEventClassKeyboard, kEventRawKeyDown}};
2419 static EventHandlerUPP handle_dialog_eventUPP = NULL;
2420
2421 if (handle_dialog_eventUPP == NULL)
2422 handle_dialog_eventUPP = NewEventHandlerUPP (mac_handle_dialog_event);
2423 return InstallWindowEventHandler (window, handle_dialog_eventUPP,
2424 GetEventTypeCount (specs), specs,
2425 window, NULL);
2426 }
2427
2428 #define DIALOG_LEFT_MARGIN (112)
2429 #define DIALOG_TOP_MARGIN (24)
2430 #define DIALOG_RIGHT_MARGIN (24)
2431 #define DIALOG_BOTTOM_MARGIN (20)
2432 #define DIALOG_MIN_INNER_WIDTH (338)
2433 #define DIALOG_MAX_INNER_WIDTH (564)
2434 #define DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE (12)
2435 #define DIALOG_BUTTON_BUTTON_VERTICAL_SPACE (12)
2436 #define DIALOG_BUTTON_MIN_WIDTH (68)
2437 #define DIALOG_TEXT_MIN_HEIGHT (50)
2438 #define DIALOG_TEXT_BUTTONS_VERTICAL_SPACE (10)
2439 #define DIALOG_ICON_WIDTH (64)
2440 #define DIALOG_ICON_HEIGHT (64)
2441 #define DIALOG_ICON_LEFT_MARGIN (24)
2442 #define DIALOG_ICON_TOP_MARGIN (15)
2443
2444 static Lisp_Object
2445 pop_down_dialog (arg)
2446 Lisp_Object arg;
2447 {
2448 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
2449 WindowRef window = p->pointer;
2450
2451 BLOCK_INPUT;
2452
2453 if (popup_activated_flag)
2454 EndAppModalStateForWindow (window);
2455 DisposeWindow (window);
2456 popup_activated_flag = 0;
2457
2458 UNBLOCK_INPUT;
2459
2460 return Qnil;
2461 }
2462
2463 static int
2464 create_and_show_dialog (f, first_wv)
2465 FRAME_PTR f;
2466 widget_value *first_wv;
2467 {
2468 OSStatus err;
2469 char *dialog_name, *message;
2470 int nb_buttons, first_group_count, i, result = 0;
2471 widget_value *wv;
2472 short buttons_height, text_height, inner_width, inner_height;
2473 Rect empty_rect, *rects;
2474 WindowRef window = NULL;
2475 ControlRef *buttons, default_button = NULL, text;
2476 int specpdl_count = SPECPDL_INDEX ();
2477
2478 dialog_name = first_wv->name;
2479 nb_buttons = dialog_name[1] - '0';
2480 first_group_count = nb_buttons - (dialog_name[4] - '0');
2481
2482 wv = first_wv->contents;
2483 message = wv->value;
2484
2485 wv = wv->next;
2486 SetRect (&empty_rect, 0, 0, 0, 0);
2487
2488 /* Create dialog window. */
2489 err = CreateNewWindow (kMovableModalWindowClass,
2490 kWindowStandardHandlerAttribute,
2491 &empty_rect, &window);
2492 if (err == noErr)
2493 {
2494 record_unwind_protect (pop_down_dialog, make_save_value (window, 0));
2495 err = SetThemeWindowBackground (window, kThemeBrushMovableModalBackground,
2496 true);
2497 }
2498 if (err == noErr)
2499 err = SetWindowTitleWithCFString (window, (dialog_name[0] == 'Q'
2500 ? CFSTR ("Question")
2501 : CFSTR ("Information")));
2502
2503 /* Create button controls and measure their optimal bounds. */
2504 if (err == noErr)
2505 {
2506 buttons = alloca (sizeof (ControlRef) * nb_buttons);
2507 rects = alloca (sizeof (Rect) * nb_buttons);
2508 for (i = 0; i < nb_buttons; i++)
2509 {
2510 CFStringRef label = cfstring_create_with_utf8_cstring (wv->value);
2511
2512 if (label == NULL)
2513 err = memFullErr;
2514 else
2515 {
2516 err = CreatePushButtonControl (window, &empty_rect,
2517 label, &buttons[i]);
2518 CFRelease (label);
2519 }
2520 if (err == noErr)
2521 {
2522 if (!wv->enabled)
2523 {
2524 #ifdef MAC_OSX
2525 err = DisableControl (buttons[i]);
2526 #else
2527 err = DeactivateControl (buttons[i]);
2528 #endif
2529 }
2530 else if (default_button == NULL)
2531 default_button = buttons[i];
2532 }
2533 if (err == noErr)
2534 {
2535 SInt16 unused;
2536
2537 rects[i] = empty_rect;
2538 err = GetBestControlRect (buttons[i], &rects[i], &unused);
2539 }
2540 if (err == noErr)
2541 {
2542 UInt32 command_id;
2543
2544 OffsetRect (&rects[i], -rects[i].left, -rects[i].top);
2545 if (rects[i].right < DIALOG_BUTTON_MIN_WIDTH)
2546 rects[i].right = DIALOG_BUTTON_MIN_WIDTH;
2547 else if (rects[i].right > DIALOG_MAX_INNER_WIDTH)
2548 rects[i].right = DIALOG_MAX_INNER_WIDTH;
2549
2550 command_id = DIALOG_BUTTON_MAKE_COMMAND_ID ((int) wv->call_data);
2551 err = SetControlCommandID (buttons[i], command_id);
2552 }
2553 if (err != noErr)
2554 break;
2555 wv = wv->next;
2556 }
2557 }
2558
2559 /* Layout buttons. rects[i] is set relative to the bottom-right
2560 corner of the inner box. */
2561 if (err == noErr)
2562 {
2563 short bottom, right, max_height, left_align_shift;
2564
2565 inner_width = DIALOG_MIN_INNER_WIDTH;
2566 bottom = right = max_height = 0;
2567 for (i = 0; i < nb_buttons; i++)
2568 {
2569 if (right - rects[i].right < - inner_width)
2570 {
2571 if (i != first_group_count
2572 && right - rects[i].right >= - DIALOG_MAX_INNER_WIDTH)
2573 inner_width = - (right - rects[i].right);
2574 else
2575 {
2576 bottom -= max_height + DIALOG_BUTTON_BUTTON_VERTICAL_SPACE;
2577 right = max_height = 0;
2578 }
2579 }
2580 if (max_height < rects[i].bottom)
2581 max_height = rects[i].bottom;
2582 OffsetRect (&rects[i], right - rects[i].right,
2583 bottom - rects[i].bottom);
2584 right = rects[i].left - DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
2585 if (i == first_group_count - 1)
2586 right -= DIALOG_BUTTON_BUTTON_HORIZONTAL_SPACE;
2587 }
2588 buttons_height = - (bottom - max_height);
2589
2590 left_align_shift = - (inner_width + rects[nb_buttons - 1].left);
2591 for (i = nb_buttons - 1; i >= first_group_count; i--)
2592 {
2593 if (bottom != rects[i].bottom)
2594 {
2595 left_align_shift = - (inner_width + rects[i].left);
2596 bottom = rects[i].bottom;
2597 }
2598 OffsetRect (&rects[i], left_align_shift, 0);
2599 }
2600 }
2601
2602 /* Create a static text control and measure its bounds. */
2603 if (err == noErr)
2604 {
2605 CFStringRef message_string;
2606 Rect bounds;
2607
2608 message_string = cfstring_create_with_utf8_cstring (message);
2609 if (message_string == NULL)
2610 err = memFullErr;
2611 else
2612 {
2613 ControlFontStyleRec text_style;
2614
2615 text_style.flags = 0;
2616 SetRect (&bounds, 0, 0, inner_width, 0);
2617 err = CreateStaticTextControl (window, &bounds, message_string,
2618 &text_style, &text);
2619 CFRelease (message_string);
2620 }
2621 if (err == noErr)
2622 {
2623 SInt16 unused;
2624
2625 bounds = empty_rect;
2626 err = GetBestControlRect (text, &bounds, &unused);
2627 }
2628 if (err == noErr)
2629 {
2630 text_height = bounds.bottom - bounds.top;
2631 if (text_height < DIALOG_TEXT_MIN_HEIGHT)
2632 text_height = DIALOG_TEXT_MIN_HEIGHT;
2633 }
2634 }
2635
2636 /* Place buttons. */
2637 if (err == noErr)
2638 {
2639 inner_height = (text_height + DIALOG_TEXT_BUTTONS_VERTICAL_SPACE
2640 + buttons_height);
2641
2642 for (i = 0; i < nb_buttons; i++)
2643 {
2644 OffsetRect (&rects[i], DIALOG_LEFT_MARGIN + inner_width,
2645 DIALOG_TOP_MARGIN + inner_height);
2646 SetControlBounds (buttons[i], &rects[i]);
2647 }
2648 }
2649
2650 /* Place text. */
2651 if (err == noErr)
2652 {
2653 Rect bounds;
2654
2655 SetRect (&bounds, DIALOG_LEFT_MARGIN, DIALOG_TOP_MARGIN,
2656 DIALOG_LEFT_MARGIN + inner_width,
2657 DIALOG_TOP_MARGIN + text_height);
2658 SetControlBounds (text, &bounds);
2659 }
2660
2661 /* Create the application icon at the upper-left corner. */
2662 if (err == noErr)
2663 {
2664 ControlButtonContentInfo content;
2665 ControlRef icon;
2666 static const ProcessSerialNumber psn = {0, kCurrentProcess};
2667 #ifdef MAC_OSX
2668 FSRef app_location;
2669 #else
2670 ProcessInfoRec pinfo;
2671 FSSpec app_spec;
2672 #endif
2673 SInt16 unused;
2674
2675 content.contentType = kControlContentIconRef;
2676 #ifdef MAC_OSX
2677 err = GetProcessBundleLocation (&psn, &app_location);
2678 if (err == noErr)
2679 err = GetIconRefFromFileInfo (&app_location, 0, NULL, 0, NULL,
2680 kIconServicesNormalUsageFlag,
2681 &content.u.iconRef, &unused);
2682 #else
2683 bzero (&pinfo, sizeof (ProcessInfoRec));
2684 pinfo.processInfoLength = sizeof (ProcessInfoRec);
2685 pinfo.processAppSpec = &app_spec;
2686 err = GetProcessInformation (&psn, &pinfo);
2687 if (err == noErr)
2688 err = GetIconRefFromFile (&app_spec, &content.u.iconRef, &unused);
2689 #endif
2690 if (err == noErr)
2691 {
2692 Rect bounds;
2693
2694 SetRect (&bounds, DIALOG_ICON_LEFT_MARGIN, DIALOG_ICON_TOP_MARGIN,
2695 DIALOG_ICON_LEFT_MARGIN + DIALOG_ICON_WIDTH,
2696 DIALOG_ICON_TOP_MARGIN + DIALOG_ICON_HEIGHT);
2697 err = CreateIconControl (window, &bounds, &content, true, &icon);
2698 ReleaseIconRef (content.u.iconRef);
2699 }
2700 }
2701
2702 /* Show the dialog window and run event loop. */
2703 if (err == noErr)
2704 if (default_button)
2705 err = SetWindowDefaultButton (window, default_button);
2706 if (err == noErr)
2707 err = install_dialog_event_handler (window);
2708 if (err == noErr)
2709 {
2710 SizeWindow (window,
2711 DIALOG_LEFT_MARGIN + inner_width + DIALOG_RIGHT_MARGIN,
2712 DIALOG_TOP_MARGIN + inner_height + DIALOG_BOTTOM_MARGIN,
2713 true);
2714 err = RepositionWindow (window, FRAME_MAC_WINDOW (f),
2715 kWindowAlertPositionOnParentWindow);
2716 }
2717 if (err == noErr)
2718 {
2719 SetWRefCon (window, 0);
2720 ShowWindow (window);
2721 BringToFront (window);
2722 popup_activated_flag = 1;
2723 err = BeginAppModalStateForWindow (window);
2724 }
2725 if (err == noErr)
2726 {
2727 EventTargetRef toolbox_dispatcher = GetEventDispatcherTarget ();
2728
2729 quit_dialog_event_loop = 0;
2730 while (1)
2731 {
2732 EMACS_TIME next_time = timer_check (1);
2733 long secs = EMACS_SECS (next_time);
2734 long usecs = EMACS_USECS (next_time);
2735 EventTimeout timeout;
2736 EventRef event;
2737
2738 if (secs < 0 || (secs == 0 && usecs == 0))
2739 {
2740 /* Sometimes timer_check returns -1 (no timers) even if
2741 there are timers. So do a timeout anyway. */
2742 secs = 1;
2743 usecs = 0;
2744 }
2745
2746 timeout = (secs * kEventDurationSecond
2747 + usecs * kEventDurationMicrosecond);
2748 err = ReceiveNextEvent (0, NULL, timeout, kEventRemoveFromQueue,
2749 &event);
2750 if (err == noErr)
2751 {
2752 SendEventToEventTarget (event, toolbox_dispatcher);
2753 ReleaseEvent (event);
2754 }
2755 #if 0 /* defined (MAC_OSX) */
2756 else if (err != eventLoopTimedOutErr)
2757 {
2758 if (err == eventLoopQuitErr)
2759 err = noErr;
2760 break;
2761 }
2762 #else
2763 /* The return value of ReceiveNextEvent seems to be
2764 unreliable. Use our own global variable instead. */
2765 if (quit_dialog_event_loop)
2766 {
2767 err = noErr;
2768 break;
2769 }
2770 #endif
2771 }
2772 }
2773 if (err == noErr)
2774 {
2775 UInt32 command_id = GetWRefCon (window);
2776
2777 if (DIALOG_BUTTON_COMMAND_ID_P (command_id))
2778 result = DIALOG_BUTTON_COMMAND_ID_VALUE (command_id);
2779 }
2780
2781 unbind_to (specpdl_count, Qnil);
2782
2783 return result;
2784 }
2785 #else /* not TARGET_API_MAC_CARBON */
2786 static int
2787 mac_dialog (widget_value *wv)
2788 {
2789 char *dialog_name;
2790 char *prompt;
2791 char **button_labels;
2792 UInt32 *ref_cons;
2793 int nb_buttons;
2794 int left_count;
2795 int i;
2796 int dialog_width;
2797 Rect rect;
2798 WindowRef window_ptr;
2799 ControlRef ch;
2800 int left;
2801 EventRecord event_record;
2802 SInt16 part_code;
2803 int control_part_code;
2804 Point mouse;
2805
2806 dialog_name = wv->name;
2807 nb_buttons = dialog_name[1] - '0';
2808 left_count = nb_buttons - (dialog_name[4] - '0');
2809 button_labels = (char **) alloca (sizeof (char *) * nb_buttons);
2810 ref_cons = (UInt32 *) alloca (sizeof (UInt32) * nb_buttons);
2811
2812 wv = wv->contents;
2813 prompt = (char *) alloca (strlen (wv->value) + 1);
2814 strcpy (prompt, wv->value);
2815 c2pstr (prompt);
2816
2817 wv = wv->next;
2818 for (i = 0; i < nb_buttons; i++)
2819 {
2820 button_labels[i] = wv->value;
2821 button_labels[i] = (char *) alloca (strlen (wv->value) + 1);
2822 strcpy (button_labels[i], wv->value);
2823 c2pstr (button_labels[i]);
2824 ref_cons[i] = (UInt32) wv->call_data;
2825 wv = wv->next;
2826 }
2827
2828 window_ptr = GetNewCWindow (DIALOG_WINDOW_RESOURCE, NULL, (WindowRef) -1);
2829
2830 SetPortWindowPort (window_ptr);
2831
2832 TextFont (0);
2833 /* Left and right margins in the dialog are 13 pixels each.*/
2834 dialog_width = 14;
2835 /* Calculate width of dialog box: 8 pixels on each side of the text
2836 label in each button, 12 pixels between buttons. */
2837 for (i = 0; i < nb_buttons; i++)
2838 dialog_width += StringWidth (button_labels[i]) + 16 + 12;
2839
2840 if (left_count != 0 && nb_buttons - left_count != 0)
2841 dialog_width += 12;
2842
2843 dialog_width = max (dialog_width, StringWidth (prompt) + 26);
2844
2845 SizeWindow (window_ptr, dialog_width, 78, 0);
2846 ShowWindow (window_ptr);
2847
2848 SetPortWindowPort (window_ptr);
2849
2850 TextFont (0);
2851
2852 MoveTo (13, 29);
2853 DrawString (prompt);
2854
2855 left = 13;
2856 for (i = 0; i < nb_buttons; i++)
2857 {
2858 int button_width = StringWidth (button_labels[i]) + 16;
2859 SetRect (&rect, left, 45, left + button_width, 65);
2860 ch = NewControl (window_ptr, &rect, button_labels[i], 1, 0, 0, 0,
2861 kControlPushButtonProc, ref_cons[i]);
2862 left += button_width + 12;
2863 if (i == left_count - 1)
2864 left += 12;
2865 }
2866
2867 i = 0;
2868 while (!i)
2869 {
2870 if (WaitNextEvent (mDownMask, &event_record, 10, NULL))
2871 if (event_record.what == mouseDown)
2872 {
2873 part_code = FindWindow (event_record.where, &window_ptr);
2874 if (part_code == inContent)
2875 {
2876 mouse = event_record.where;
2877 GlobalToLocal (&mouse);
2878 control_part_code = FindControl (mouse, window_ptr, &ch);
2879 if (control_part_code == kControlButtonPart)
2880 if (TrackControl (ch, mouse, NULL))
2881 i = GetControlReference (ch);
2882 }
2883 }
2884 }
2885
2886 DisposeWindow (window_ptr);
2887
2888 return i;
2889 }
2890 #endif /* not TARGET_API_MAC_CARBON */
2891
2892 static char * button_names [] = {
2893 "button1", "button2", "button3", "button4", "button5",
2894 "button6", "button7", "button8", "button9", "button10" };
2895
2896 static Lisp_Object
2897 mac_dialog_show (f, keymaps, title, header, error_name)
2898 FRAME_PTR f;
2899 int keymaps;
2900 Lisp_Object title, header;
2901 char **error_name;
2902 {
2903 int i, nb_buttons=0;
2904 char dialog_name[6];
2905 int menu_item_selection;
2906
2907 widget_value *wv, *first_wv = 0, *prev_wv = 0;
2908
2909 /* Number of elements seen so far, before boundary. */
2910 int left_count = 0;
2911 /* 1 means we've seen the boundary between left-hand elts and right-hand. */
2912 int boundary_seen = 0;
2913
2914 *error_name = NULL;
2915
2916 if (menu_items_n_panes > 1)
2917 {
2918 *error_name = "Multiple panes in dialog box";
2919 return Qnil;
2920 }
2921
2922 /* Create a tree of widget_value objects
2923 representing the text label and buttons. */
2924 {
2925 Lisp_Object pane_name, prefix;
2926 char *pane_string;
2927 pane_name = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_NAME];
2928 prefix = XVECTOR (menu_items)->contents[MENU_ITEMS_PANE_PREFIX];
2929 pane_string = (NILP (pane_name)
2930 ? "" : (char *) SDATA (pane_name));
2931 prev_wv = xmalloc_widget_value ();
2932 prev_wv->value = pane_string;
2933 if (keymaps && !NILP (prefix))
2934 prev_wv->name++;
2935 prev_wv->enabled = 1;
2936 prev_wv->name = "message";
2937 prev_wv->help = Qnil;
2938 first_wv = prev_wv;
2939
2940 /* Loop over all panes and items, filling in the tree. */
2941 i = MENU_ITEMS_PANE_LENGTH;
2942 while (i < menu_items_used)
2943 {
2944
2945 /* Create a new item within current pane. */
2946 Lisp_Object item_name, enable, descrip;
2947 item_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_NAME];
2948 enable = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_ENABLE];
2949 descrip
2950 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_EQUIV_KEY];
2951
2952 if (NILP (item_name))
2953 {
2954 free_menubar_widget_value_tree (first_wv);
2955 *error_name = "Submenu in dialog items";
2956 return Qnil;
2957 }
2958 if (EQ (item_name, Qquote))
2959 {
2960 /* This is the boundary between left-side elts
2961 and right-side elts. Stop incrementing right_count. */
2962 boundary_seen = 1;
2963 i++;
2964 continue;
2965 }
2966 if (nb_buttons >= 9)
2967 {
2968 free_menubar_widget_value_tree (first_wv);
2969 *error_name = "Too many dialog items";
2970 return Qnil;
2971 }
2972
2973 wv = xmalloc_widget_value ();
2974 prev_wv->next = wv;
2975 wv->name = (char *) button_names[nb_buttons];
2976 if (!NILP (descrip))
2977 wv->key = (char *) SDATA (descrip);
2978 wv->value = (char *) SDATA (item_name);
2979 wv->call_data = (void *) i;
2980 /* menu item is identified by its index in menu_items table */
2981 wv->enabled = !NILP (enable);
2982 wv->help = Qnil;
2983 prev_wv = wv;
2984
2985 if (! boundary_seen)
2986 left_count++;
2987
2988 nb_buttons++;
2989 i += MENU_ITEMS_ITEM_LENGTH;
2990 }
2991
2992 /* If the boundary was not specified,
2993 by default put half on the left and half on the right. */
2994 if (! boundary_seen)
2995 left_count = nb_buttons - nb_buttons / 2;
2996
2997 wv = xmalloc_widget_value ();
2998 wv->name = dialog_name;
2999 wv->help = Qnil;
3000
3001 /* Frame title: 'Q' = Question, 'I' = Information.
3002 Can also have 'E' = Error if, one day, we want
3003 a popup for errors. */
3004 if (NILP(header))
3005 dialog_name[0] = 'Q';
3006 else
3007 dialog_name[0] = 'I';
3008
3009 /* Dialog boxes use a really stupid name encoding
3010 which specifies how many buttons to use
3011 and how many buttons are on the right. */
3012 dialog_name[1] = '0' + nb_buttons;
3013 dialog_name[2] = 'B';
3014 dialog_name[3] = 'R';
3015 /* Number of buttons to put on the right. */
3016 dialog_name[4] = '0' + nb_buttons - left_count;
3017 dialog_name[5] = 0;
3018 wv->contents = first_wv;
3019 first_wv = wv;
3020 }
3021
3022 /* Force a redisplay before showing the dialog. If a frame is created
3023 just before showing the dialog, its contents may not have been fully
3024 drawn. */
3025 Fredisplay (Qt);
3026
3027 /* Actually create the dialog. */
3028 #if TARGET_API_MAC_CARBON
3029 menu_item_selection = create_and_show_dialog (f, first_wv);
3030 #else
3031 menu_item_selection = mac_dialog (first_wv);
3032 #endif
3033
3034 /* Free the widget_value objects we used to specify the contents. */
3035 free_menubar_widget_value_tree (first_wv);
3036
3037 /* Find the selected item, and its pane, to return
3038 the proper value. */
3039 if (menu_item_selection != 0)
3040 {
3041 Lisp_Object prefix;
3042
3043 prefix = Qnil;
3044 i = 0;
3045 while (i < menu_items_used)
3046 {
3047 Lisp_Object entry;
3048
3049 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
3050 {
3051 prefix
3052 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
3053 i += MENU_ITEMS_PANE_LENGTH;
3054 }
3055 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
3056 {
3057 /* This is the boundary between left-side elts and
3058 right-side elts. */
3059 ++i;
3060 }
3061 else
3062 {
3063 entry
3064 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
3065 if (menu_item_selection == i)
3066 {
3067 if (keymaps != 0)
3068 {
3069 entry = Fcons (entry, Qnil);
3070 if (!NILP (prefix))
3071 entry = Fcons (prefix, entry);
3072 }
3073 return entry;
3074 }
3075 i += MENU_ITEMS_ITEM_LENGTH;
3076 }
3077 }
3078 }
3079 else
3080 /* Make "Cancel" equivalent to C-g. */
3081 Fsignal (Qquit, Qnil);
3082
3083 return Qnil;
3084 }
3085 #endif /* HAVE_DIALOGS */
3086 \f
3087
3088 /* Is this item a separator? */
3089 static int
3090 name_is_separator (name)
3091 const char *name;
3092 {
3093 const char *start = name;
3094
3095 /* Check if name string consists of only dashes ('-'). */
3096 while (*name == '-') name++;
3097 /* Separators can also be of the form "--:TripleSuperMegaEtched"
3098 or "--deep-shadow". We don't implement them yet, se we just treat
3099 them like normal separators. */
3100 return (*name == '\0' || start + 2 == name);
3101 }
3102
3103 static void
3104 add_menu_item (menu, pos, wv)
3105 MenuRef menu;
3106 int pos;
3107 widget_value *wv;
3108 {
3109 #if TARGET_API_MAC_CARBON
3110 CFStringRef item_name;
3111 #else
3112 Str255 item_name;
3113 #endif
3114
3115 if (name_is_separator (wv->name))
3116 AppendMenu (menu, "\p-");
3117 else
3118 {
3119 AppendMenu (menu, "\pX");
3120
3121 #if TARGET_API_MAC_CARBON
3122 item_name = cfstring_create_with_utf8_cstring (wv->name);
3123
3124 if (wv->key != NULL)
3125 {
3126 CFStringRef name, key;
3127
3128 name = item_name;
3129 key = cfstring_create_with_utf8_cstring (wv->key);
3130 item_name = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@ %@"),
3131 name, key);
3132 CFRelease (name);
3133 CFRelease (key);
3134 }
3135
3136 SetMenuItemTextWithCFString (menu, pos, item_name);
3137 CFRelease (item_name);
3138
3139 if (wv->enabled)
3140 EnableMenuItem (menu, pos);
3141 else
3142 DisableMenuItem (menu, pos);
3143
3144 if (STRINGP (wv->help))
3145 SetMenuItemProperty (menu, pos, MAC_EMACS_CREATOR_CODE, 'help',
3146 sizeof (Lisp_Object), &wv->help);
3147 #else /* ! TARGET_API_MAC_CARBON */
3148 item_name[sizeof (item_name) - 1] = '\0';
3149 strncpy (item_name, wv->name, sizeof (item_name) - 1);
3150 if (wv->key != NULL)
3151 {
3152 int len = strlen (item_name);
3153
3154 strncpy (item_name + len, " ", sizeof (item_name) - 1 - len);
3155 len = strlen (item_name);
3156 strncpy (item_name + len, wv->key, sizeof (item_name) - 1 - len);
3157 }
3158 c2pstr (item_name);
3159 SetMenuItemText (menu, pos, item_name);
3160
3161 if (wv->enabled)
3162 EnableItem (menu, pos);
3163 else
3164 DisableItem (menu, pos);
3165 #endif /* ! TARGET_API_MAC_CARBON */
3166
3167 /* Draw radio buttons and tickboxes. */
3168 if (wv->selected && (wv->button_type == BUTTON_TYPE_TOGGLE ||
3169 wv->button_type == BUTTON_TYPE_RADIO))
3170 SetItemMark (menu, pos, checkMark);
3171 else
3172 SetItemMark (menu, pos, noMark);
3173
3174 SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data);
3175 }
3176 }
3177
3178 /* Construct native Mac OS menu based on widget_value tree. */
3179
3180 static int
3181 fill_menu (menu, wv, kind, submenu_id)
3182 MenuRef menu;
3183 widget_value *wv;
3184 enum mac_menu_kind kind;
3185 int submenu_id;
3186 {
3187 int pos;
3188
3189 for (pos = 1; wv != NULL; wv = wv->next, pos++)
3190 {
3191 add_menu_item (menu, pos, wv);
3192 if (wv->contents && submenu_id < min_menu_id[kind + 1])
3193 {
3194 MenuRef submenu = NewMenu (submenu_id, "\pX");
3195
3196 InsertMenu (submenu, -1);
3197 #if TARGET_API_MAC_CARBON
3198 SetMenuItemHierarchicalMenu (menu, pos, submenu);
3199 #else
3200 SetMenuItemHierarchicalID (menu, pos, submenu_id);
3201 #endif
3202 submenu_id = fill_menu (submenu, wv->contents, kind, submenu_id + 1);
3203 }
3204 }
3205
3206 return submenu_id;
3207 }
3208
3209 /* Construct native Mac OS menubar based on widget_value tree. */
3210
3211 static void
3212 fill_menubar (wv, deep_p)
3213 widget_value *wv;
3214 int deep_p;
3215 {
3216 int id, submenu_id;
3217 #if !TARGET_API_MAC_CARBON
3218 int title_changed_p = 0;
3219 #endif
3220
3221 /* Clean up the menu bar when filled by the entire menu trees. */
3222 if (deep_p)
3223 {
3224 dispose_menus (MAC_MENU_MENU_BAR, 0);
3225 dispose_menus (MAC_MENU_MENU_BAR_SUB, 0);
3226 #if !TARGET_API_MAC_CARBON
3227 title_changed_p = 1;
3228 #endif
3229 }
3230
3231 /* Fill menu bar titles and submenus. Reuse the existing menu bar
3232 titles as much as possible to minimize redraw (if !deep_p). */
3233 submenu_id = min_menu_id[MAC_MENU_MENU_BAR_SUB];
3234 for (id = min_menu_id[MAC_MENU_MENU_BAR];
3235 wv != NULL && id < min_menu_id[MAC_MENU_MENU_BAR + 1];
3236 wv = wv->next, id++)
3237 {
3238 OSStatus err = noErr;
3239 MenuRef menu;
3240 #if TARGET_API_MAC_CARBON
3241 CFStringRef title;
3242
3243 title = CFStringCreateWithCString (NULL, wv->name,
3244 kCFStringEncodingMacRoman);
3245 #else
3246 Str255 title;
3247
3248 strncpy (title, wv->name, 255);
3249 title[255] = '\0';
3250 c2pstr (title);
3251 #endif
3252
3253 menu = GetMenuRef (id);
3254 if (menu)
3255 {
3256 #if TARGET_API_MAC_CARBON
3257 CFStringRef old_title;
3258
3259 err = CopyMenuTitleAsCFString (menu, &old_title);
3260 if (err == noErr)
3261 {
3262 if (CFStringCompare (title, old_title, 0) != kCFCompareEqualTo)
3263 {
3264 #ifdef MAC_OSX
3265 if (id + 1 == min_menu_id[MAC_MENU_MENU_BAR + 1]
3266 || GetMenuRef (id + 1) == NULL)
3267 {
3268 /* This is a workaround for Mac OS X 10.5 where
3269 just calling SetMenuTitleWithCFString fails
3270 to change the title of the last (Help) menu
3271 in the menu bar. */
3272 DeleteMenu (id);
3273 DisposeMenu (menu);
3274 menu = NULL;
3275 }
3276 else
3277 #endif /* MAC_OSX */
3278 err = SetMenuTitleWithCFString (menu, title);
3279 }
3280 CFRelease (old_title);
3281 }
3282 else
3283 err = SetMenuTitleWithCFString (menu, title);
3284 #else /* !TARGET_API_MAC_CARBON */
3285 if (!EqualString (title, (*menu)->menuData, false, false))
3286 {
3287 DeleteMenu (id);
3288 DisposeMenu (menu);
3289 menu = NewMenu (id, title);
3290 InsertMenu (menu, GetMenuRef (id + 1) ? id + 1 : 0);
3291 title_changed_p = 1;
3292 }
3293 #endif /* !TARGET_API_MAC_CARBON */
3294 }
3295
3296 if (!menu)
3297 {
3298 #if TARGET_API_MAC_CARBON
3299 err = CreateNewMenu (id, 0, &menu);
3300 if (err == noErr)
3301 err = SetMenuTitleWithCFString (menu, title);
3302 #else
3303 menu = NewMenu (id, title);
3304 #endif
3305 if (err == noErr)
3306 {
3307 InsertMenu (menu, 0);
3308 #if !TARGET_API_MAC_CARBON
3309 title_changed_p = 1;
3310 #endif
3311 }
3312 }
3313 #if TARGET_API_MAC_CARBON
3314 CFRelease (title);
3315 #endif
3316
3317 if (err == noErr)
3318 if (wv->contents)
3319 submenu_id = fill_menu (menu, wv->contents, MAC_MENU_MENU_BAR_SUB,
3320 submenu_id);
3321 }
3322
3323 if (id < min_menu_id[MAC_MENU_MENU_BAR + 1] && GetMenuRef (id))
3324 {
3325 dispose_menus (MAC_MENU_MENU_BAR, id);
3326 #if !TARGET_API_MAC_CARBON
3327 title_changed_p = 1;
3328 #endif
3329 }
3330
3331 #if !TARGET_API_MAC_CARBON
3332 if (title_changed_p)
3333 InvalMenuBar ();
3334 #endif
3335 }
3336
3337 /* Dispose of menus that belong to KIND, and remove them from the menu
3338 list. ID is the lower bound of menu IDs that will be processed. */
3339
3340 static void
3341 dispose_menus (kind, id)
3342 enum mac_menu_kind kind;
3343 int id;
3344 {
3345 for (id = max (id, min_menu_id[kind]); id < min_menu_id[kind + 1]; id++)
3346 {
3347 MenuRef menu = GetMenuRef (id);
3348
3349 if (menu == NULL)
3350 break;
3351 DeleteMenu (id);
3352 DisposeMenu (menu);
3353 }
3354 }
3355
3356 #endif /* HAVE_MENUS */
3357
3358 /* Detect if a menu is currently active. */
3359
3360 int
3361 popup_activated ()
3362 {
3363 return popup_activated_flag;
3364 }
3365
3366 /* The following is used by delayed window autoselection. */
3367
3368 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
3369 doc: /* Return t if a menu or popup dialog is active. */)
3370 ()
3371 {
3372 #if TARGET_API_MAC_CARBON
3373 return (popup_activated ()) ? Qt : Qnil;
3374 #else
3375 /* Always return Qnil since menu selection functions do not return
3376 until a selection has been made or cancelled. */
3377 return Qnil;
3378 #endif
3379 }
3380 \f
3381 void
3382 syms_of_macmenu ()
3383 {
3384 staticpro (&menu_items);
3385 menu_items = Qnil;
3386
3387 Qdebug_on_next_call = intern ("debug-on-next-call");
3388 staticpro (&Qdebug_on_next_call);
3389
3390 defsubr (&Sx_popup_menu);
3391 defsubr (&Smenu_or_popup_active_p);
3392 #ifdef HAVE_MENUS
3393 defsubr (&Sx_popup_dialog);
3394 #endif
3395 }
3396
3397 /* arch-tag: 40b2c6c7-b8a9-4a49-b930-1b2707184cce
3398 (do not change this comment) */