Merge from emacs--devo--0
[emacs.git] / src / macmenu.c
blob1d2e89ddd254e2cb2dfe9f4bed24d67352b18a60
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.
5 This file is part of GNU Emacs.
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.
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.
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. */
22 /* Contributed by Andrew Choi (akochoi@mac.com). */
24 #include <config.h>
26 #include <stdio.h>
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"
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 */
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"
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
63 #include "dispextern.h"
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 */
76 static const int min_menu_id[] = {0, 1, 234, 235, 236, 256, 16384, 32768};
78 #define DIALOG_WINDOW_RESOURCE 130
80 #if TARGET_API_MAC_CARBON
81 #define HAVE_DIALOGS 1
82 #endif
84 #undef HAVE_MULTILINGUAL_MENU
86 /******************************************************************/
87 /* Definitions copied from lwlib.h */
89 typedef void * XtPointer;
91 enum button_type
93 BUTTON_TYPE_NONE,
94 BUTTON_TYPE_TOGGLE,
95 BUTTON_TYPE_RADIO
98 /* This structure is based on the one in ../lwlib/lwlib.h, modified
99 for Mac OS. */
100 typedef struct _widget_value
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;
144 /* we resource the widget_value structures; this points to the next
145 one on the free list if this one has been deallocated.
147 struct _widget_value *free_list;
148 #endif
149 } widget_value;
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)
156 /******************************************************************/
158 #ifndef TRUE
159 #define TRUE 1
160 #define FALSE 0
161 #endif /* no TRUE */
163 Lisp_Object Qdebug_on_next_call;
165 extern Lisp_Object Vmenu_updating_frame;
167 extern Lisp_Object Qmenu_bar, Qmac_apple_event;
169 extern Lisp_Object QCtoggle, QCradio;
171 extern Lisp_Object Voverriding_local_map;
172 extern Lisp_Object Voverriding_local_map_menu_flag;
174 extern Lisp_Object Qoverriding_local_map, Qoverriding_terminal_local_map;
176 extern Lisp_Object Qmenu_bar_update_hook;
178 void set_frame_menubar P_ ((FRAME_PTR, int, int));
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
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));
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));
208 /* This holds a Lisp vector that holds the results of decoding
209 the keymaps or alist-of-alists that specify a menu.
211 It describes the panes and items within the panes.
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.
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.
224 A single vector slot containing quote indicates that the
225 following items should appear on the right of a dialog box.
227 Using a Lisp vector to hold this information while we decode it
228 takes care of protecting all the data from GC. */
230 #define MENU_ITEMS_PANE_NAME 1
231 #define MENU_ITEMS_PANE_PREFIX 2
232 #define MENU_ITEMS_PANE_LENGTH 3
234 enum menu_item_idx
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
247 static Lisp_Object menu_items;
249 /* Number of slots currently allocated in menu_items. */
250 static int menu_items_allocated;
252 /* This is the index in menu_items of the first empty slot. */
253 static int menu_items_used;
255 /* The number of panes currently recorded in menu_items,
256 excluding those within submenus. */
257 static int menu_items_n_panes;
259 /* Current depth within submenus. */
260 static int menu_items_submenu_depth;
262 /* Nonzero means a menu is currently active. */
263 static int popup_activated_flag;
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.
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>. */
272 int pending_menu_activation;
274 /* Initialize the menu_items structure if we haven't already done so.
275 Also mark it as currently empty. */
277 static void
278 init_menu_items ()
280 if (NILP (menu_items))
282 menu_items_allocated = 60;
283 menu_items = Fmake_vector (make_number (menu_items_allocated), Qnil);
286 menu_items_used = 0;
287 menu_items_n_panes = 0;
288 menu_items_submenu_depth = 0;
291 /* Call at the end of generating the data in menu_items. */
293 static void
294 finish_menu_items ()
298 /* Call when finished using the data for the current menu
299 in menu_items. */
301 static void
302 discard_menu_items ()
304 /* Free the structure if it is especially large.
305 Otherwise, hold on to it, to save time. */
306 if (menu_items_allocated > 200)
308 menu_items = Qnil;
309 menu_items_allocated = 0;
313 /* This undoes save_menu_items, and it is called by the specpdl unwind
314 mechanism. */
316 static Lisp_Object
317 restore_menu_items (saved)
318 Lisp_Object saved;
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;
331 /* Push the whole state of menu_items processing onto the specpdl.
332 It will be restored when the specpdl is unwound. */
334 static void
335 save_menu_items ()
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;
345 /* Make the menu_items vector twice as large. */
347 static void
348 grow_menu_items ()
350 menu_items_allocated *= 2;
351 menu_items = larger_vector (menu_items, menu_items_allocated, Qnil);
354 /* Begin a submenu. */
356 static void
357 push_submenu_start ()
359 if (menu_items_used + 1 > menu_items_allocated)
360 grow_menu_items ();
362 XVECTOR (menu_items)->contents[menu_items_used++] = Qnil;
363 menu_items_submenu_depth++;
366 /* End a submenu. */
368 static void
369 push_submenu_end ()
371 if (menu_items_used + 1 > menu_items_allocated)
372 grow_menu_items ();
374 XVECTOR (menu_items)->contents[menu_items_used++] = Qlambda;
375 menu_items_submenu_depth--;
378 /* Indicate boundary between left and right. */
380 static void
381 push_left_right_boundary ()
383 if (menu_items_used + 1 > menu_items_allocated)
384 grow_menu_items ();
386 XVECTOR (menu_items)->contents[menu_items_used++] = Qquote;
389 /* Start a new menu pane in menu_items.
390 NAME is the pane name. PREFIX_VEC is a prefix key for this pane. */
392 static void
393 push_menu_pane (name, prefix_vec)
394 Lisp_Object name, prefix_vec;
396 if (menu_items_used + MENU_ITEMS_PANE_LENGTH > menu_items_allocated)
397 grow_menu_items ();
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;
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'. */
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;
418 if (menu_items_used + MENU_ITEMS_ITEM_LENGTH > menu_items_allocated)
419 grow_menu_items ();
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;
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. */
436 static void
437 keymap_panes (keymaps, nmaps, notreal)
438 Lisp_Object *keymaps;
439 int nmaps;
440 int notreal;
442 int mapno;
444 init_menu_items ();
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);
453 finish_menu_items ();
456 /* Args passed between single_keymap_panes and single_menu_item. */
457 struct skp
459 Lisp_Object pending_maps;
460 int maxdepth, notreal;
463 static void single_menu_item P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
464 void *));
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.
473 If we encounter submenus deeper than MAXDEPTH levels, ignore them. */
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;
483 struct skp skp;
484 struct gcpro gcpro1;
486 skp.pending_maps = Qnil;
487 skp.maxdepth = maxdepth;
488 skp.notreal = notreal;
490 if (maxdepth <= 0)
491 return;
493 push_menu_pane (pane_name, prefix);
495 GCPRO1 (skp.pending_maps);
496 map_keymap (keymap, single_menu_item, Qnil, &skp, 1);
497 UNGCPRO;
499 /* Process now any submenus which want to be panes at this level. */
500 while (CONSP (skp.pending_maps))
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);
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. */
523 static void
524 single_menu_item (key, item, dummy, skp_v)
525 Lisp_Object key, item, dummy;
526 void *skp_v;
528 Lisp_Object map, item_string, enabled;
529 struct gcpro gcpro1, gcpro2;
530 int res;
531 struct skp *skp = skp_v;
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. */
540 map = XVECTOR (item_properties)->contents[ITEM_PROPERTY_MAP];
542 if (skp->notreal)
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;
551 enabled = XVECTOR (item_properties)->contents[ITEM_PROPERTY_ENABLE];
552 item_string = XVECTOR (item_properties)->contents[ITEM_PROPERTY_NAME];
554 if (!NILP (map) && SREF (item_string, 0) == '@')
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;
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]);
570 /* Display a submenu using the toolkit. */
571 if (! (NILP (map) || NILP (enabled)))
573 push_submenu_start ();
574 single_keymap_panes (map, Qnil, key, 0, skp->maxdepth - 1);
575 push_submenu_end ();
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. */
583 static void
584 list_of_panes (menu)
585 Lisp_Object menu;
587 Lisp_Object tail;
589 init_menu_items ();
591 for (tail = menu; CONSP (tail); tail = XCDR (tail))
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);
603 finish_menu_items ();
606 /* Push the items in a single pane defined by the alist PANE. */
608 static void
609 list_of_items (pane)
610 Lisp_Object pane;
612 Lisp_Object tail, item, item1;
614 for (tail = pane; CONSP (tail); tail = XCDR (tail))
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))
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);
627 else
628 push_left_right_boundary ();
633 static Lisp_Object
634 cleanup_popup_menu (arg)
635 Lisp_Object arg;
637 discard_menu_items ();
638 return Qnil;
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.
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.
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.
659 You can also use a list of keymaps as MENU.
660 Then each keymap makes a separate pane.
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.
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.
675 If POSITION is nil, don't display the menu at all, just precalculate the
676 cached information about equivalent key sequences.
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;
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;
699 #ifdef HAVE_MENUS
700 if (! NILP (position))
702 check_mac ();
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))))
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;
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
723 window = selected_window;
724 XSETFASTINT (x, 0);
725 XSETFASTINT (y, 0);
728 else
730 tem = Fcar (position);
731 if (CONSP (tem))
733 window = Fcar (Fcdr (position));
734 x = XCAR (tem);
735 y = Fcar (XCDR (tem));
737 else
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);
748 CHECK_NUMBER (x);
749 CHECK_NUMBER (y);
751 /* Decode where to put the menu. */
753 if (FRAMEP (window))
755 f = XFRAME (window);
756 xpos = 0;
757 ypos = 0;
759 else if (WINDOWP (window))
761 CHECK_LIVE_WINDOW (window);
762 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
764 xpos = WINDOW_LEFT_EDGE_X (XWINDOW (window));
765 ypos = WINDOW_TOP_EDGE_Y (XWINDOW (window));
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);
772 xpos += XINT (x);
773 ypos += XINT (y);
775 XSETFRAME (Vmenu_updating_frame, f);
777 else
778 Vmenu_updating_frame = Qnil;
779 #endif /* HAVE_MENUS */
781 title = Qnil;
782 GCPRO1 (title);
784 /* Decode the menu items from what was specified. */
786 keymap = get_keymap (menu, 0, 0);
787 if (CONSP (keymap))
789 /* We were given a keymap. Extract menu info from the keymap. */
790 Lisp_Object prompt;
792 /* Extract the detailed info to make one pane. */
793 keymap_panes (&menu, 1, NILP (position));
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;
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;
805 keymaps = 1;
807 else if (CONSP (menu) && KEYMAPP (XCAR (menu)))
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;
815 title = Qnil;
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))
821 Lisp_Object prompt;
823 maps[i++] = keymap = get_keymap (XCAR (tem), 1, 0);
825 prompt = Fkeymap_prompt (keymap);
826 if (NILP (title) && !NILP (prompt))
827 title = prompt;
830 /* Extract the detailed info to make one pane. */
831 keymap_panes (maps, nmaps, NILP (position));
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;
837 keymaps = 1;
839 else
841 /* We were given an old-fashioned menu. */
842 title = Fcar (menu);
843 CHECK_STRING (title);
845 list_of_panes (Fcdr (menu));
847 keymaps = 0;
850 if (NILP (position))
852 discard_menu_items ();
853 UNGCPRO;
854 return Qnil;
857 #ifdef HAVE_MENUS
858 /* Display them in a menu. */
859 record_unwind_protect (cleanup_popup_menu, Qnil);
860 BLOCK_INPUT;
862 selection = mac_menu_show (f, xpos, ypos, for_click,
863 keymaps, title, &error_name);
864 UNBLOCK_INPUT;
865 unbind_to (specpdl_count, Qnil);
867 UNGCPRO;
868 #endif /* HAVE_MENUS */
870 if (error_name) error (error_name);
871 return selection;
874 #ifdef HAVE_MENUS
876 /* Regard ESC and C-g as Cancel even without the Cancel button. */
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;
885 Boolean result;
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)))
894 *item_hit = kStdCancelItemIndex;
895 return true;
898 return result;
900 #endif
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.
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.
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.)
919 If HEADER is non-nil, the frame title for the box is "Information",
920 otherwise it is "Question".
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;
928 FRAME_PTR f = NULL;
929 Lisp_Object window;
931 check_mac ();
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))))
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;
947 (*mouse_position_hook) (&new_f, 1, &bar_window, &part, &x, &y, &time);
949 if (new_f != 0)
950 XSETFRAME (window, new_f);
951 else
952 window = selected_window;
953 #endif
954 window = selected_window;
956 else if (CONSP (position))
958 Lisp_Object tem;
959 tem = Fcar (position);
960 if (CONSP (tem))
961 window = Fcar (Fcdr (position));
962 else
964 tem = Fcar (Fcdr (position)); /* EVENT_START (position) */
965 window = Fcar (tem); /* POSN_WINDOW (tem) */
968 else if (WINDOWP (position) || FRAMEP (position))
969 window = position;
970 else
971 window = Qnil;
973 /* Decode where to put the menu. */
975 if (FRAMEP (window))
976 f = XFRAME (window);
977 else if (WINDOWP (window))
979 CHECK_LIVE_WINDOW (window);
980 f = XFRAME (WINDOW_FRAME (XWINDOW (window)));
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);
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))))
1000 OSStatus err = noErr;
1001 AlertStdCFStringAlertParamRec param;
1002 CFStringRef error_string, explanation_string;
1003 DialogRef alert;
1004 DialogItemIndex item_hit;
1005 Lisp_Object tem;
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);
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))
1019 error_string = cfstring_create_with_string (XCAR (contents));
1020 if (error_string == NULL)
1021 err = memFullErr;
1022 explanation_string = NULL;
1024 else
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
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)
1040 CFRelease (error_string);
1041 err = memFullErr;
1045 if (err == noErr)
1046 err = GetStandardAlertDefaultParams (&param,
1047 kStdCFStringAlertVersionOne);
1048 if (err == noErr)
1050 param.movable = true;
1051 param.position = kWindowAlertPositionParentWindow;
1052 if (NILP (header))
1054 param.defaultText = CFSTR ("Yes");
1055 param.otherText = CFSTR ("No");
1056 #if 0
1057 param.cancelText = CFSTR ("Cancel");
1058 param.cancelButton = kAlertStdAlertCancelButton;
1059 #endif
1061 err = CreateStandardAlert (kAlertNoteAlert, error_string,
1062 explanation_string, &param, &alert);
1063 CFRelease (error_string);
1064 if (explanation_string)
1065 CFRelease (explanation_string);
1067 if (err == noErr)
1068 err = RunStandardAlert (alert, mac_dialog_modal_filter, &item_hit);
1069 UNBLOCK_INPUT;
1071 if (err == noErr)
1073 if (item_hit == kStdCancelItemIndex)
1074 Fsignal (Qquit, Qnil);
1075 else if (item_hit == kStdOkItemIndex)
1076 return Qt;
1077 else
1078 return Qnil;
1081 #endif
1082 #ifndef HAVE_DIALOGS
1083 /* Display a menu with these alternatives
1084 in the middle of frame F. */
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));
1092 return Fx_popup_menu (newpos,
1093 Fcons (Fcar (contents), Fcons (contents, Qnil)));
1095 #else /* HAVE_DIALOGS */
1097 Lisp_Object title;
1098 char *error_name;
1099 Lisp_Object selection;
1100 int specpdl_count = SPECPDL_INDEX ();
1102 /* Decode the dialog items from what was specified. */
1103 title = Fcar (contents);
1104 CHECK_STRING (title);
1106 list_of_panes (Fcons (contents, Qnil));
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);
1115 if (error_name) error (error_name);
1116 return selection;
1118 #endif /* HAVE_DIALOGS */
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.
1125 To activate the menu bar, we use the button-press event location
1126 that was saved in saved_menu_event_location.
1128 But first we recompute the menu bar contents (the whole tree).
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. */
1134 void
1135 x_activate_menubar (f)
1136 FRAME_PTR f;
1138 SInt32 menu_choice;
1139 SInt16 menu_id, menu_item;
1140 extern Point saved_menu_event_location;
1142 set_frame_menubar (f, 0, 1);
1143 BLOCK_INPUT;
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);
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)
1158 MenuRef menu = GetMenuRef (menu_id);
1160 if (menu)
1162 UInt32 refcon;
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);
1170 HiliteMenu (0);
1172 UNBLOCK_INPUT;
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. */
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;
1187 Lisp_Object prefix, entry;
1188 Lisp_Object *subprefix_stack;
1189 int submenu_depth = 0;
1190 int i;
1192 entry = Qnil;
1193 subprefix_stack = (Lisp_Object *) alloca (menu_bar_items_used * sizeof (Lisp_Object));
1194 prefix = Qnil;
1195 i = 0;
1197 while (i < menu_bar_items_used)
1199 if (EQ (XVECTOR (vector)->contents[i], Qnil))
1201 subprefix_stack[submenu_depth++] = prefix;
1202 prefix = entry;
1203 i++;
1205 else if (EQ (XVECTOR (vector)->contents[i], Qlambda))
1207 prefix = subprefix_stack[--submenu_depth];
1208 i++;
1210 else if (EQ (XVECTOR (vector)->contents[i], Qt))
1212 prefix = XVECTOR (vector)->contents[i + MENU_ITEMS_PANE_PREFIX];
1213 i += MENU_ITEMS_PANE_LENGTH;
1215 else
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)
1222 int j;
1223 struct input_event buf;
1224 Lisp_Object frame;
1225 EVENT_INIT (buf);
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);
1233 for (j = 0; j < submenu_depth; j++)
1234 if (!NILP (subprefix_stack[j]))
1236 buf.kind = MENU_BAR_EVENT;
1237 buf.frame_or_window = frame;
1238 buf.arg = subprefix_stack[j];
1239 kbd_buffer_store_event (&buf);
1242 if (!NILP (prefix))
1244 buf.kind = MENU_BAR_EVENT;
1245 buf.frame_or_window = frame;
1246 buf.arg = prefix;
1247 kbd_buffer_store_event (&buf);
1250 buf.kind = MENU_BAR_EVENT;
1251 buf.frame_or_window = frame;
1252 buf.arg = entry;
1253 kbd_buffer_store_event (&buf);
1255 return;
1257 i += MENU_ITEMS_ITEM_LENGTH;
1262 /* Allocate a widget_value, blocking input. */
1264 widget_value *
1265 xmalloc_widget_value ()
1267 widget_value *value;
1269 BLOCK_INPUT;
1270 value = malloc_widget_value ();
1271 UNBLOCK_INPUT;
1273 return value;
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. */
1281 void
1282 free_menubar_widget_value_tree (wv)
1283 widget_value *wv;
1285 if (! wv) return;
1287 wv->name = wv->value = wv->key = (char *) 0xDEADBEEF;
1289 if (wv->contents && (wv->contents != (widget_value*)1))
1291 free_menubar_widget_value_tree (wv->contents);
1292 wv->contents = (widget_value *) 0xDEADBEEF;
1294 if (wv->next)
1296 free_menubar_widget_value_tree (wv->next);
1297 wv->next = (widget_value *) 0xDEADBEEF;
1299 BLOCK_INPUT;
1300 free_widget_value (wv);
1301 UNBLOCK_INPUT;
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. */
1308 static int
1309 parse_single_submenu (item_key, item_name, maps)
1310 Lisp_Object item_key, item_name, maps;
1312 Lisp_Object length;
1313 int len;
1314 Lisp_Object *mapvec;
1315 int i;
1316 int top_level_items = 0;
1318 length = Flength (maps);
1319 len = XINT (length);
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++)
1325 mapvec[i] = Fcar (maps);
1326 maps = Fcdr (maps);
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++)
1333 if (!KEYMAPP (mapvec[i]))
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);
1342 else
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);
1352 return top_level_items;
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. */
1359 static widget_value *
1360 digest_single_submenu (start, end, top_level_items)
1361 int start, end, top_level_items;
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;
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;
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)
1388 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
1390 submenu_stack[submenu_depth++] = save_wv;
1391 save_wv = prev_wv;
1392 prev_wv = 0;
1393 i++;
1395 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
1397 prev_wv = save_wv;
1398 save_wv = submenu_stack[--submenu_depth];
1399 i++;
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))
1410 /* Create a new pane. */
1411 Lisp_Object pane_name, prefix;
1412 char *pane_string;
1414 panes_seen++;
1416 pane_name = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_NAME];
1417 prefix = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
1419 #ifndef HAVE_MULTILINGUAL_MENU
1420 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
1422 pane_name = ENCODE_MENU_STRING (pane_name);
1423 AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
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 = "";
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, ""))
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;
1451 else
1452 save_wv = first_wv;
1454 prev_wv = 0;
1455 i += MENU_ITEMS_PANE_LENGTH;
1457 else
1459 /* Create a new item within current pane. */
1460 Lisp_Object item_name, enable, descrip, def, type, selected;
1461 Lisp_Object help;
1463 /* All items should be contained in panes. */
1464 if (panes_seen == 0)
1465 abort ();
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);
1475 #ifndef HAVE_MULTILINGUAL_MENU
1476 if (STRING_MULTIBYTE (item_name))
1478 item_name = ENCODE_MENU_STRING (item_name);
1479 AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
1482 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
1484 descrip = ENCODE_MENU_STRING (descrip);
1485 AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
1487 #endif /* not HAVE_MULTILINGUAL_MENU */
1489 wv = xmalloc_widget_value ();
1490 if (prev_wv)
1491 prev_wv->next = wv;
1492 else
1493 save_wv->contents = wv;
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);
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 ();
1513 wv->selected = !NILP (selected);
1514 if (! STRINGP (help))
1515 help = Qnil;
1517 wv->help = help;
1519 prev_wv = wv;
1521 i += MENU_ITEMS_ITEM_LENGTH;
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)
1529 wv = first_wv->contents;
1530 free_widget_value (first_wv);
1531 return wv;
1534 return first_wv;
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;
1546 widget_value *wv;
1548 for (wv = first_wv; wv; wv = wv->next)
1550 if (STRINGP (wv->lname))
1552 wv->name = SDATA (wv->lname);
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)
1558 if (wv->name[0] == '@')
1559 wv->name++;
1560 wv->value = 0;
1564 if (STRINGP (wv->lkey))
1565 wv->key = SDATA (wv->lkey);
1567 if (wv->contents)
1568 update_submenu_strings (wv->contents);
1573 #if TARGET_API_MAC_CARBON
1574 extern Lisp_Object Vshow_help_function;
1576 static Lisp_Object
1577 restore_show_help_function (old_show_help_function)
1578 Lisp_Object old_show_help_function;
1580 Vshow_help_function = old_show_help_function;
1582 return Qnil;
1585 static pascal OSStatus
1586 menu_target_item_handler (next_handler, event, data)
1587 EventHandlerCallRef next_handler;
1588 EventRef event;
1589 void *data;
1591 OSStatus err;
1592 MenuRef menu;
1593 MenuItemIndex menu_item;
1594 Lisp_Object help;
1595 GrafPtr port;
1596 int specpdl_count = SPECPDL_INDEX ();
1598 /* Don't be bothered with the overflowed toolbar items menu. */
1599 if (!popup_activated ())
1600 return eventNotHandledErr;
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;
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);
1624 return err == noErr ? noErr : eventNotHandledErr;
1627 OSStatus
1628 install_menu_target_item_handler ()
1630 static const EventTypeSpec specs[] =
1631 {{kEventClassMenu, kEventMenuTargetItem}};
1633 return InstallApplicationEventHandler (NewEventHandlerUPP
1634 (menu_target_item_handler),
1635 GetEventTypeCount (specs),
1636 specs, NULL, NULL);
1638 #endif /* TARGET_API_MAC_CARBON */
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). */
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;
1650 OSStatus err;
1651 UInt32 keyCode;
1652 UInt32 keyModifiers;
1654 err = GetEventParameter (theEvent, kEventParamKeyCode,
1655 typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
1657 if (err == noErr)
1658 err = GetEventParameter (theEvent, kEventParamKeyModifiers,
1659 typeUInt32, NULL, sizeof(UInt32),
1660 NULL, &keyModifiers);
1662 if (err == noErr && mac_quit_char_key_p (keyModifiers, keyCode))
1664 MenuRef menu = userData != 0
1665 ? (MenuRef)userData : AcquireRootMenu ();
1667 CancelMenuTracking (menu, true, 0);
1668 if (!userData) ReleaseMenu (menu);
1669 return noErr;
1672 return CallNextEventHandler (nextHandler, theEvent);
1674 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
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. */
1681 static void
1682 install_menu_quit_handler (kind, root_menu)
1683 enum mac_menu_kind kind;
1684 MenuRef root_menu;
1686 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
1687 static const EventTypeSpec typesList[] =
1688 {{kEventClassKeyboard, kEventRawKeyDown}};
1689 int id;
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++)
1697 MenuRef menu = GetMenuRef (id);
1699 if (menu == NULL)
1700 break;
1701 InstallMenuEventHandler (menu, menu_quit_handler,
1702 GetEventTypeCount (typesList),
1703 typesList, root_menu, NULL);
1705 #endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 1030 */
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. */
1712 void
1713 set_frame_menubar (f, first_time, deep_p)
1714 FRAME_PTR f;
1715 int first_time;
1716 int deep_p;
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;
1725 XSETFRAME (Vmenu_updating_frame, f);
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
1735 if (deep_p)
1737 /* Make a widget-value tree representing the entire menu trees. */
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));
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;
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);
1758 record_unwind_save_match_data ();
1759 if (NILP (Voverriding_local_map_menu_flag))
1761 specbind (Qoverriding_terminal_local_map, Qnil);
1762 specbind (Qoverriding_local_map, Qnil);
1765 set_buffer_internal_1 (XBUFFER (buffer));
1767 /* Run the Lucid hook. */
1768 safe_run_hooks (Qactivate_menubar_hook);
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));
1777 items = FRAME_MENU_BAR_ITEMS (f);
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));
1784 /* Fill in menu_items with the current menu bar contents.
1785 This can evaluate Lisp code. */
1786 save_menu_items ();
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)
1798 Lisp_Object key, string, maps;
1800 last_i = i;
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;
1808 submenu_start[i] = menu_items_used;
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;
1815 submenu_end[i] = menu_items_used;
1818 finish_menu_items ();
1820 /* Convert menu_items into widget_value trees
1821 to display the menu. This cannot evaluate Lisp code. */
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;
1831 for (i = 0; i < last_i; i += 4)
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;
1846 set_buffer_internal_1 (prev);
1848 /* If there has been no change in the Lisp-level contents
1849 of the menu bar, skip redisplaying it. Just exit. */
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)
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;
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;
1870 /* This calls restore_menu_items to restore menu_items, etc.,
1871 as they were outside. */
1872 unbind_to (specpdl_count, Qnil);
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)
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;
1889 else
1891 /* Make a widget-value tree containing
1892 just the top level menu bar strings. */
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;
1902 items = FRAME_MENU_BAR_ITEMS (f);
1903 for (i = 0; i < XVECTOR (items)->size; i += 4)
1905 Lisp_Object string;
1907 string = XVECTOR (items)->contents[i + 1];
1908 if (NILP (string))
1909 break;
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);
1923 if (prev_wv)
1924 prev_wv->next = wv;
1925 else
1926 first_wv->contents = wv;
1927 prev_wv = wv;
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;
1936 /* Create or update the menu bar widget. */
1938 BLOCK_INPUT;
1940 /* Non-null value to indicate menubar has already been "created". */
1941 f->output_data.mac->menubar_widget = 1;
1943 fill_menubar (first_wv->contents, deep_p);
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);
1950 UNBLOCK_INPUT;
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. */
1956 void
1957 free_frame_menubar (f)
1958 FRAME_PTR f;
1960 f->output_data.mac->menubar_widget = 0;
1964 static Lisp_Object
1965 pop_down_menu (arg)
1966 Lisp_Object arg;
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]);
1972 BLOCK_INPUT;
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;
1978 /* delete all menus */
1979 dispose_menus (MAC_MENU_POPUP_SUB, 0);
1980 DeleteMenu (min_menu_id[MAC_MENU_POPUP]);
1981 DisposeMenu (menu);
1983 UNBLOCK_INPUT;
1985 return Qnil;
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. */
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.) */
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;
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;
2024 int first_pane;
2025 int specpdl_count = SPECPDL_INDEX ();
2027 *error = NULL;
2029 if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
2031 *error = "Empty menu";
2032 return Qnil;
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;
2046 /* Loop over all panes and items, filling in the tree. */
2047 i = 0;
2048 while (i < menu_items_used)
2050 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
2052 submenu_stack[submenu_depth++] = save_wv;
2053 save_wv = prev_wv;
2054 prev_wv = 0;
2055 first_pane = 1;
2056 i++;
2058 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
2060 prev_wv = save_wv;
2061 save_wv = submenu_stack[--submenu_depth];
2062 first_pane = 0;
2063 i++;
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))
2074 /* Create a new pane. */
2075 Lisp_Object pane_name, prefix;
2076 char *pane_string;
2078 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
2079 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
2081 #ifndef HAVE_MULTILINGUAL_MENU
2082 if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
2084 pane_name = ENCODE_MENU_STRING (pane_name);
2085 AREF (menu_items, i + MENU_ITEMS_PANE_NAME) = pane_name;
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 = "";
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, ""))
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;
2115 else if (first_pane)
2117 save_wv = wv;
2118 prev_wv = 0;
2120 first_pane = 0;
2121 i += MENU_ITEMS_PANE_LENGTH;
2123 else
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);
2135 #ifndef HAVE_MULTILINGUAL_MENU
2136 if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
2138 item_name = ENCODE_MENU_STRING (item_name);
2139 AREF (menu_items, i + MENU_ITEMS_ITEM_NAME) = item_name;
2142 if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
2144 descrip = ENCODE_MENU_STRING (descrip);
2145 AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY) = descrip;
2147 #endif /* not HAVE_MULTILINGUAL_MENU */
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);
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 ();
2172 wv->selected = !NILP (selected);
2174 if (! STRINGP (help))
2175 help = Qnil;
2177 wv->help = help;
2179 prev_wv = wv;
2181 i += MENU_ITEMS_ITEM_LENGTH;
2185 /* Deal with the title, if it is non-nil. */
2186 if (!NILP (title))
2188 widget_value *wv_title = xmalloc_widget_value ();
2189 widget_value *wv_sep = xmalloc_widget_value ();
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;
2197 #ifndef HAVE_MULTILINGUAL_MENU
2198 if (STRING_MULTIBYTE (title))
2199 title = ENCODE_MENU_STRING (title);
2200 #endif
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;
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]);
2217 /* Free the widget_value objects we used to specify the
2218 contents. */
2219 free_menubar_widget_value_tree (first_wv);
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);
2225 /* No selection has been chosen yet. */
2226 menu_item_selection = 0;
2228 record_unwind_protect (pop_down_menu, make_save_value (f, 0));
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);
2234 /* Display the menu. */
2235 popup_activated_flag = 1;
2236 menu_item_choice = PopUpMenuSelect (menu, y, x, 0);
2237 popup_activated_flag = 0;
2239 /* Get the refcon to find the correct item */
2240 if (menu_item_choice)
2242 MenuRef sel_menu = GetMenuRef (HiWord (menu_item_choice));
2244 if (sel_menu)
2245 GetMenuItemRefCon (sel_menu, LoWord (menu_item_choice),
2246 &menu_item_selection);
2249 unbind_to (specpdl_count, Qnil);
2251 /* Find the selected item, and its pane, to return
2252 the proper value. */
2253 if (menu_item_selection != 0)
2255 Lisp_Object prefix, entry;
2257 prefix = entry = Qnil;
2258 i = 0;
2259 while (i < menu_items_used)
2261 if (EQ (XVECTOR (menu_items)->contents[i], Qnil))
2263 subprefix_stack[submenu_depth++] = prefix;
2264 prefix = entry;
2265 i++;
2267 else if (EQ (XVECTOR (menu_items)->contents[i], Qlambda))
2269 prefix = subprefix_stack[--submenu_depth];
2270 i++;
2272 else if (EQ (XVECTOR (menu_items)->contents[i], Qt))
2274 prefix
2275 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
2276 i += MENU_ITEMS_PANE_LENGTH;
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
2284 entry
2285 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
2286 if (menu_item_selection == i)
2288 if (keymaps != 0)
2290 int j;
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);
2299 return entry;
2301 i += MENU_ITEMS_ITEM_LENGTH;
2305 else if (!for_click)
2306 /* Make "Cancel" equivalent to C-g. */
2307 Fsignal (Qquit, Qnil);
2309 return Qnil;
2313 #ifdef HAVE_DIALOGS
2314 /* Construct native Mac OS dialog based on widget_value tree. */
2316 #if TARGET_API_MAC_CARBON
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)
2326 extern EMACS_TIME timer_check P_ ((int));
2327 static int quit_dialog_event_loop;
2329 static pascal OSStatus
2330 mac_handle_dialog_event (next_handler, event, data)
2331 EventHandlerCallRef next_handler;
2332 EventRef event;
2333 void *data;
2335 OSStatus err, result = eventNotHandledErr;
2336 WindowRef window = (WindowRef) data;
2338 switch (GetEventClass (event))
2340 case kEventClassCommand:
2342 HICommand command;
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))
2350 SetWRefCon (window, command.commandID);
2351 quit_dialog_event_loop = 1;
2352 break;
2355 result = CallNextEventHandler (next_handler, event);
2357 break;
2359 case kEventClassKeyboard:
2361 OSStatus result;
2362 char char_code;
2364 result = CallNextEventHandler (next_handler, event);
2365 if (result != eventNotHandledErr)
2366 break;
2368 err = GetEventParameter (event, kEventParamKeyMacCharCodes,
2369 typeChar, NULL, sizeof (char),
2370 NULL, &char_code);
2371 if (err == noErr)
2372 switch (char_code)
2374 case kEscapeCharCode:
2375 quit_dialog_event_loop = 1;
2376 break;
2378 default:
2380 UInt32 modifiers, key_code;
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;
2393 break;
2396 break;
2398 default:
2399 abort ();
2402 if (quit_dialog_event_loop)
2404 err = QuitEventLoop (GetCurrentEventLoop ());
2405 if (err == noErr)
2406 result = noErr;
2409 return result;
2412 static OSStatus
2413 install_dialog_event_handler (window)
2414 WindowRef window;
2416 static const EventTypeSpec specs[] =
2417 {{kEventClassCommand, kEventCommandProcess},
2418 {kEventClassKeyboard, kEventRawKeyDown}};
2419 static EventHandlerUPP handle_dialog_eventUPP = NULL;
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);
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)
2444 static Lisp_Object
2445 pop_down_dialog (arg)
2446 Lisp_Object arg;
2448 struct Lisp_Save_Value *p = XSAVE_VALUE (arg);
2449 WindowRef window = p->pointer;
2451 BLOCK_INPUT;
2453 if (popup_activated_flag)
2454 EndAppModalStateForWindow (window);
2455 DisposeWindow (window);
2456 popup_activated_flag = 0;
2458 UNBLOCK_INPUT;
2460 return Qnil;
2463 static int
2464 create_and_show_dialog (f, first_wv)
2465 FRAME_PTR f;
2466 widget_value *first_wv;
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 ();
2478 dialog_name = first_wv->name;
2479 nb_buttons = dialog_name[1] - '0';
2480 first_group_count = nb_buttons - (dialog_name[4] - '0');
2482 wv = first_wv->contents;
2483 message = wv->value;
2485 wv = wv->next;
2486 SetRect (&empty_rect, 0, 0, 0, 0);
2488 /* Create dialog window. */
2489 err = CreateNewWindow (kMovableModalWindowClass,
2490 kWindowStandardHandlerAttribute,
2491 &empty_rect, &window);
2492 if (err == noErr)
2494 record_unwind_protect (pop_down_dialog, make_save_value (window, 0));
2495 err = SetThemeWindowBackground (window, kThemeBrushMovableModalBackground,
2496 true);
2498 if (err == noErr)
2499 err = SetWindowTitleWithCFString (window, (dialog_name[0] == 'Q'
2500 ? CFSTR ("Question")
2501 : CFSTR ("Information")));
2503 /* Create button controls and measure their optimal bounds. */
2504 if (err == noErr)
2506 buttons = alloca (sizeof (ControlRef) * nb_buttons);
2507 rects = alloca (sizeof (Rect) * nb_buttons);
2508 for (i = 0; i < nb_buttons; i++)
2510 CFStringRef label = cfstring_create_with_utf8_cstring (wv->value);
2512 if (label == NULL)
2513 err = memFullErr;
2514 else
2516 err = CreatePushButtonControl (window, &empty_rect,
2517 label, &buttons[i]);
2518 CFRelease (label);
2520 if (err == noErr)
2522 if (!wv->enabled)
2524 #ifdef MAC_OSX
2525 err = DisableControl (buttons[i]);
2526 #else
2527 err = DeactivateControl (buttons[i]);
2528 #endif
2530 else if (default_button == NULL)
2531 default_button = buttons[i];
2533 if (err == noErr)
2535 SInt16 unused;
2537 rects[i] = empty_rect;
2538 err = GetBestControlRect (buttons[i], &rects[i], &unused);
2540 if (err == noErr)
2542 UInt32 command_id;
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;
2550 command_id = DIALOG_BUTTON_MAKE_COMMAND_ID ((int) wv->call_data);
2551 err = SetControlCommandID (buttons[i], command_id);
2553 if (err != noErr)
2554 break;
2555 wv = wv->next;
2559 /* Layout buttons. rects[i] is set relative to the bottom-right
2560 corner of the inner box. */
2561 if (err == noErr)
2563 short bottom, right, max_height, left_align_shift;
2565 inner_width = DIALOG_MIN_INNER_WIDTH;
2566 bottom = right = max_height = 0;
2567 for (i = 0; i < nb_buttons; i++)
2569 if (right - rects[i].right < - inner_width)
2571 if (i != first_group_count
2572 && right - rects[i].right >= - DIALOG_MAX_INNER_WIDTH)
2573 inner_width = - (right - rects[i].right);
2574 else
2576 bottom -= max_height + DIALOG_BUTTON_BUTTON_VERTICAL_SPACE;
2577 right = max_height = 0;
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;
2588 buttons_height = - (bottom - max_height);
2590 left_align_shift = - (inner_width + rects[nb_buttons - 1].left);
2591 for (i = nb_buttons - 1; i >= first_group_count; i--)
2593 if (bottom != rects[i].bottom)
2595 left_align_shift = - (inner_width + rects[i].left);
2596 bottom = rects[i].bottom;
2598 OffsetRect (&rects[i], left_align_shift, 0);
2602 /* Create a static text control and measure its bounds. */
2603 if (err == noErr)
2605 CFStringRef message_string;
2606 Rect bounds;
2608 message_string = cfstring_create_with_utf8_cstring (message);
2609 if (message_string == NULL)
2610 err = memFullErr;
2611 else
2613 ControlFontStyleRec text_style;
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);
2621 if (err == noErr)
2623 SInt16 unused;
2625 bounds = empty_rect;
2626 err = GetBestControlRect (text, &bounds, &unused);
2628 if (err == noErr)
2630 text_height = bounds.bottom - bounds.top;
2631 if (text_height < DIALOG_TEXT_MIN_HEIGHT)
2632 text_height = DIALOG_TEXT_MIN_HEIGHT;
2636 /* Place buttons. */
2637 if (err == noErr)
2639 inner_height = (text_height + DIALOG_TEXT_BUTTONS_VERTICAL_SPACE
2640 + buttons_height);
2642 for (i = 0; i < nb_buttons; i++)
2644 OffsetRect (&rects[i], DIALOG_LEFT_MARGIN + inner_width,
2645 DIALOG_TOP_MARGIN + inner_height);
2646 SetControlBounds (buttons[i], &rects[i]);
2650 /* Place text. */
2651 if (err == noErr)
2653 Rect bounds;
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);
2661 /* Create the application icon at the upper-left corner. */
2662 if (err == noErr)
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;
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)
2692 Rect bounds;
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);
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)
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);
2717 if (err == noErr)
2719 SetWRefCon (window, 0);
2720 ShowWindow (window);
2721 BringToFront (window);
2722 popup_activated_flag = 1;
2723 err = BeginAppModalStateForWindow (window);
2725 if (err == noErr)
2727 EventTargetRef toolbox_dispatcher = GetEventDispatcherTarget ();
2729 quit_dialog_event_loop = 0;
2730 while (1)
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;
2738 if (secs < 0 || (secs == 0 && usecs == 0))
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;
2746 timeout = (secs * kEventDurationSecond
2747 + usecs * kEventDurationMicrosecond);
2748 err = ReceiveNextEvent (0, NULL, timeout, kEventRemoveFromQueue,
2749 &event);
2750 if (err == noErr)
2752 SendEventToEventTarget (event, toolbox_dispatcher);
2753 ReleaseEvent (event);
2755 #ifdef MAC_OSX
2756 else if (err != eventLoopTimedOutErr)
2758 if (err == eventLoopQuitErr)
2759 err = noErr;
2760 break;
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)
2767 err = noErr;
2768 break;
2770 #endif
2773 if (err == noErr)
2775 UInt32 command_id = GetWRefCon (window);
2777 if (DIALOG_BUTTON_COMMAND_ID_P (command_id))
2778 result = DIALOG_BUTTON_COMMAND_ID_VALUE (command_id);
2781 unbind_to (specpdl_count, Qnil);
2783 return result;
2785 #else /* not TARGET_API_MAC_CARBON */
2786 static int
2787 mac_dialog (widget_value *wv)
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;
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);
2812 wv = wv->contents;
2813 prompt = (char *) alloca (strlen (wv->value) + 1);
2814 strcpy (prompt, wv->value);
2815 c2pstr (prompt);
2817 wv = wv->next;
2818 for (i = 0; i < nb_buttons; i++)
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;
2828 window_ptr = GetNewCWindow (DIALOG_WINDOW_RESOURCE, NULL, (WindowRef) -1);
2830 SetPortWindowPort (window_ptr);
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;
2840 if (left_count != 0 && nb_buttons - left_count != 0)
2841 dialog_width += 12;
2843 dialog_width = max (dialog_width, StringWidth (prompt) + 26);
2845 SizeWindow (window_ptr, dialog_width, 78, 0);
2846 ShowWindow (window_ptr);
2848 SetPortWindowPort (window_ptr);
2850 TextFont (0);
2852 MoveTo (13, 29);
2853 DrawString (prompt);
2855 left = 13;
2856 for (i = 0; i < nb_buttons; i++)
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;
2867 i = 0;
2868 while (!i)
2870 if (WaitNextEvent (mDownMask, &event_record, 10, NULL))
2871 if (event_record.what == mouseDown)
2873 part_code = FindWindow (event_record.where, &window_ptr);
2874 if (part_code == inContent)
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);
2886 DisposeWindow (window_ptr);
2888 return i;
2890 #endif /* not TARGET_API_MAC_CARBON */
2892 static char * button_names [] = {
2893 "button1", "button2", "button3", "button4", "button5",
2894 "button6", "button7", "button8", "button9", "button10" };
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;
2903 int i, nb_buttons=0;
2904 char dialog_name[6];
2905 int menu_item_selection;
2907 widget_value *wv, *first_wv = 0, *prev_wv = 0;
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;
2914 *error_name = NULL;
2916 if (menu_items_n_panes > 1)
2918 *error_name = "Multiple panes in dialog box";
2919 return Qnil;
2922 /* Create a tree of widget_value objects
2923 representing the text label and buttons. */
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;
2940 /* Loop over all panes and items, filling in the tree. */
2941 i = MENU_ITEMS_PANE_LENGTH;
2942 while (i < menu_items_used)
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];
2952 if (NILP (item_name))
2954 free_menubar_widget_value_tree (first_wv);
2955 *error_name = "Submenu in dialog items";
2956 return Qnil;
2958 if (EQ (item_name, Qquote))
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;
2966 if (nb_buttons >= 9)
2968 free_menubar_widget_value_tree (first_wv);
2969 *error_name = "Too many dialog items";
2970 return Qnil;
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;
2985 if (! boundary_seen)
2986 left_count++;
2988 nb_buttons++;
2989 i += MENU_ITEMS_ITEM_LENGTH;
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;
2997 wv = xmalloc_widget_value ();
2998 wv->name = dialog_name;
2999 wv->help = Qnil;
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';
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;
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);
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
3034 /* Free the widget_value objects we used to specify the contents. */
3035 free_menubar_widget_value_tree (first_wv);
3037 /* Find the selected item, and its pane, to return
3038 the proper value. */
3039 if (menu_item_selection != 0)
3041 Lisp_Object prefix;
3043 prefix = Qnil;
3044 i = 0;
3045 while (i < menu_items_used)
3047 Lisp_Object entry;
3049 if (EQ (XVECTOR (menu_items)->contents[i], Qt))
3051 prefix
3052 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_PANE_PREFIX];
3053 i += MENU_ITEMS_PANE_LENGTH;
3055 else if (EQ (XVECTOR (menu_items)->contents[i], Qquote))
3057 /* This is the boundary between left-side elts and
3058 right-side elts. */
3059 ++i;
3061 else
3063 entry
3064 = XVECTOR (menu_items)->contents[i + MENU_ITEMS_ITEM_VALUE];
3065 if (menu_item_selection == i)
3067 if (keymaps != 0)
3069 entry = Fcons (entry, Qnil);
3070 if (!NILP (prefix))
3071 entry = Fcons (prefix, entry);
3073 return entry;
3075 i += MENU_ITEMS_ITEM_LENGTH;
3079 else
3080 /* Make "Cancel" equivalent to C-g. */
3081 Fsignal (Qquit, Qnil);
3083 return Qnil;
3085 #endif /* HAVE_DIALOGS */
3088 /* Is this item a separator? */
3089 static int
3090 name_is_separator (name)
3091 const char *name;
3093 const char *start = name;
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);
3103 static void
3104 add_menu_item (menu, pos, wv)
3105 MenuRef menu;
3106 int pos;
3107 widget_value *wv;
3109 #if TARGET_API_MAC_CARBON
3110 CFStringRef item_name;
3111 #else
3112 Str255 item_name;
3113 #endif
3115 if (name_is_separator (wv->name))
3116 AppendMenu (menu, "\p-");
3117 else
3119 AppendMenu (menu, "\pX");
3121 #if TARGET_API_MAC_CARBON
3122 item_name = cfstring_create_with_utf8_cstring (wv->name);
3124 if (wv->key != NULL)
3126 CFStringRef name, key;
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);
3136 SetMenuItemTextWithCFString (menu, pos, item_name);
3137 CFRelease (item_name);
3139 if (wv->enabled)
3140 EnableMenuItem (menu, pos);
3141 else
3142 DisableMenuItem (menu, pos);
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)
3152 int len = strlen (item_name);
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);
3158 c2pstr (item_name);
3159 SetMenuItemText (menu, pos, item_name);
3161 if (wv->enabled)
3162 EnableItem (menu, pos);
3163 else
3164 DisableItem (menu, pos);
3165 #endif /* ! TARGET_API_MAC_CARBON */
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);
3174 SetMenuItemRefCon (menu, pos, (UInt32) wv->call_data);
3178 /* Construct native Mac OS menu based on widget_value tree. */
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;
3187 int pos;
3189 for (pos = 1; wv != NULL; wv = wv->next, pos++)
3191 add_menu_item (menu, pos, wv);
3192 if (wv->contents && submenu_id < min_menu_id[kind + 1])
3194 MenuRef submenu = NewMenu (submenu_id, "\pX");
3196 InsertMenu (submenu, -1);
3197 SetMenuItemHierarchicalID (menu, pos, submenu_id);
3198 submenu_id = fill_menu (submenu, wv->contents, kind, submenu_id + 1);
3202 return submenu_id;
3205 /* Construct native Mac OS menubar based on widget_value tree. */
3207 static void
3208 fill_menubar (wv, deep_p)
3209 widget_value *wv;
3210 int deep_p;
3212 int id, submenu_id;
3213 #if !TARGET_API_MAC_CARBON
3214 int title_changed_p = 0;
3215 #endif
3217 /* Clean up the menu bar when filled by the entire menu trees. */
3218 if (deep_p)
3220 dispose_menus (MAC_MENU_MENU_BAR, 0);
3221 dispose_menus (MAC_MENU_MENU_BAR_SUB, 0);
3222 #if !TARGET_API_MAC_CARBON
3223 title_changed_p = 1;
3224 #endif
3227 /* Fill menu bar titles and submenus. Reuse the existing menu bar
3228 titles as much as possible to minimize redraw (if !deep_p). */
3229 submenu_id = min_menu_id[MAC_MENU_MENU_BAR_SUB];
3230 for (id = min_menu_id[MAC_MENU_MENU_BAR];
3231 wv != NULL && id < min_menu_id[MAC_MENU_MENU_BAR + 1];
3232 wv = wv->next, id++)
3234 OSStatus err = noErr;
3235 MenuRef menu;
3236 #if TARGET_API_MAC_CARBON
3237 CFStringRef title;
3239 title = CFStringCreateWithCString (NULL, wv->name,
3240 kCFStringEncodingMacRoman);
3241 #else
3242 Str255 title;
3244 strncpy (title, wv->name, 255);
3245 title[255] = '\0';
3246 c2pstr (title);
3247 #endif
3249 menu = GetMenuRef (id);
3250 if (menu)
3252 #if TARGET_API_MAC_CARBON
3253 CFStringRef old_title;
3255 err = CopyMenuTitleAsCFString (menu, &old_title);
3256 if (err == noErr)
3258 if (CFStringCompare (title, old_title, 0) != kCFCompareEqualTo)
3259 err = SetMenuTitleWithCFString (menu, title);
3260 CFRelease (old_title);
3262 else
3263 err = SetMenuTitleWithCFString (menu, title);
3264 #else /* !TARGET_API_MAC_CARBON */
3265 if (!EqualString (title, (*menu)->menuData, false, false))
3267 DeleteMenu (id);
3268 DisposeMenu (menu);
3269 menu = NewMenu (id, title);
3270 InsertMenu (menu, GetMenuRef (id + 1) ? id + 1 : 0);
3271 title_changed_p = 1;
3273 #endif /* !TARGET_API_MAC_CARBON */
3276 if (!menu)
3278 #if TARGET_API_MAC_CARBON
3279 err = CreateNewMenu (id, 0, &menu);
3280 if (err == noErr)
3281 err = SetMenuTitleWithCFString (menu, title);
3282 #else
3283 menu = NewMenu (id, title);
3284 #endif
3285 if (err == noErr)
3287 InsertMenu (menu, 0);
3288 #if !TARGET_API_MAC_CARBON
3289 title_changed_p = 1;
3290 #endif
3293 #if TARGET_API_MAC_CARBON
3294 CFRelease (title);
3295 #endif
3297 if (err == noErr)
3298 if (wv->contents)
3299 submenu_id = fill_menu (menu, wv->contents, MAC_MENU_MENU_BAR_SUB,
3300 submenu_id);
3303 if (id < min_menu_id[MAC_MENU_MENU_BAR + 1] && GetMenuRef (id))
3305 dispose_menus (MAC_MENU_MENU_BAR, id);
3306 #if !TARGET_API_MAC_CARBON
3307 title_changed_p = 1;
3308 #endif
3311 #if !TARGET_API_MAC_CARBON
3312 if (title_changed_p)
3313 InvalMenuBar ();
3314 #endif
3317 /* Dispose of menus that belong to KIND, and remove them from the menu
3318 list. ID is the lower bound of menu IDs that will be processed. */
3320 static void
3321 dispose_menus (kind, id)
3322 enum mac_menu_kind kind;
3323 int id;
3325 for (id = max (id, min_menu_id[kind]); id < min_menu_id[kind + 1]; id++)
3327 MenuRef menu = GetMenuRef (id);
3329 if (menu == NULL)
3330 break;
3331 DeleteMenu (id);
3332 DisposeMenu (menu);
3336 #endif /* HAVE_MENUS */
3338 /* Detect if a menu is currently active. */
3341 popup_activated ()
3343 return popup_activated_flag;
3346 /* The following is used by delayed window autoselection. */
3348 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0,
3349 doc: /* Return t if a menu or popup dialog is active. */)
3352 #if TARGET_API_MAC_CARBON
3353 return (popup_activated ()) ? Qt : Qnil;
3354 #else
3355 /* Always return Qnil since menu selection functions do not return
3356 until a selection has been made or cancelled. */
3357 return Qnil;
3358 #endif
3361 void
3362 syms_of_macmenu ()
3364 staticpro (&menu_items);
3365 menu_items = Qnil;
3367 Qdebug_on_next_call = intern ("debug-on-next-call");
3368 staticpro (&Qdebug_on_next_call);
3370 defsubr (&Sx_popup_menu);
3371 defsubr (&Smenu_or_popup_active_p);
3372 #ifdef HAVE_MENUS
3373 defsubr (&Sx_popup_dialog);
3374 #endif
3377 /* arch-tag: 40b2c6c7-b8a9-4a49-b930-1b2707184cce
3378 (do not change this comment) */