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