1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007-2012 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
20 By Adrian Robert, based on code from original nsmenu.m (Carl Edman,
21 Christian Limpach, Scott Bender, Christophe de Dinechin) and code in the
22 Carbon version by Yamamoto Mitsuharu. */
24 /* This should be the first include, as it may set up #defines affecting
25 interpretation of even the system includes. */
30 #include "character.h"
35 #include "blockinput.h"
37 #include "termhooks.h"
41 #define NSMENUPROFILE
0
44 #include
<sys
/timeb.h
>
45 #include
<sys
/types.h
>
48 #define MenuStagger
10.0
51 int menu_trace_num
= 0;
52 #define NSTRACE
(x
) fprintf (stderr
, "
%s:%d: [%d] " #x "\n", \
53 __FILE__
, __LINE__
, ++menu_trace_num
)
59 /* Include lisp -> C common menu parsing code */
60 #define ENCODE_MENU_STRING
(str
) ENCODE_UTF_8
(str
)
61 #include "nsmenu_common.c"
64 extern Lisp_Object Qundefined
, Qmenu_enable
, Qmenu_bar_update_hook
;
65 extern Lisp_Object QCtoggle
, QCradio
;
67 Lisp_Object Qdebug_on_next_call
;
68 extern Lisp_Object Qoverriding_local_map
, Qoverriding_terminal_local_map
;
70 extern
long context_menu_value
;
71 EmacsMenu
*mainMenu
, *svcsMenu
, *dockMenu
;
73 /* Nonzero means a menu is currently active. */
74 static int popup_activated_flag
;
76 /* Nonzero means we are tracking and updating menus. */
77 static int trackingMenu
;
80 /* NOTE: toolbar implementation is at end,
81 following complete menu implementation. */
84 /* ==========================================================================
86 Menu: Externally-called functions
88 ========================================================================== */
91 /* FIXME: not currently used, but should normalize with other terms. */
93 x_activate_menubar
(struct frame
*f
)
95 fprintf (stderr
, "XXX
: Received x_activate_menubar event.
\n"
);
99 /* Supposed to discard menubar and free storage. Since we share the
100 menubar among frames and update its context for the focused window,
101 there is nothing to do here. */
103 free_frame_menubar
(struct frame
*f
)
110 popup_activated
(void
)
112 return popup_activated_flag
;
116 /* --------------------------------------------------------------------------
117 Update menubar. Three cases:
118 1) deep_p = 0, submenu = nil: Fresh switch onto a frame -- either set up
119 just top-level menu strings (OS X), or goto case (2) (GNUstep).
120 2) deep_p = 1, submenu = nil: Recompute all submenus.
121 3) deep_p = 1, submenu = non-nil: Update contents of a single submenu.
122 -------------------------------------------------------------------------- */
124 ns_update_menubar
(struct frame
*f
, int deep_p
, EmacsMenu
*submenu
)
126 NSAutoreleasePool
*pool
;
127 id
menu = [NSApp mainMenu
];
128 static EmacsMenu
*last_submenu
= nil
;
130 const
char *submenuTitle
= [[submenu
title] UTF8String
];
131 extern int waiting_for_input
;
134 widget_value
*wv
, *first_wv
, *prev_wv
= 0;
142 NSTRACE
(set_frame_menubar
);
144 if (f
!= SELECTED_FRAME
())
146 XSETFRAME
(Vmenu_updating_frame
, f
);
147 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
150 pool
= [[NSAutoreleasePool alloc
] init
];
152 /* Menu may have been created automatically; if so, discard it. */
153 if ([menu isKindOfClass
: [EmacsMenu
class]] == NO
)
161 menu = [[EmacsMenu alloc
] initWithTitle
: ns_app_name
];
165 { /* close up anything on there */
166 id attMenu
= [menu attachedMenu
];
173 t
= -(1000*tb.time
+tb.millitm
);
176 #ifdef NS_IMPL_GNUSTEP
177 deep_p
= 1; /* until GNUstep NSMenu implements the Panther delegation model */
182 /* Fully parse one or more of the submenus. */
184 int
*submenu_start
, *submenu_end
;
185 int
*submenu_top_level_items
, *submenu_n_panes
;
186 struct buffer
*prev
= current_buffer
;
188 ptrdiff_t specpdl_count
= SPECPDL_INDEX
();
189 int previous_menu_items_used
= f
->menu_bar_items_used
;
190 Lisp_Object
*previous_items
191 = alloca
(previous_menu_items_used
* sizeof
*previous_items
);
193 /* lisp preliminaries */
194 buffer
= XWINDOW
(FRAME_SELECTED_WINDOW
(f
))->buffer
;
195 specbind
(Qinhibit_quit
, Qt
);
196 specbind
(Qdebug_on_next_call
, Qnil
);
197 record_unwind_save_match_data
();
198 if (NILP
(Voverriding_local_map_menu_flag
))
200 specbind
(Qoverriding_terminal_local_map
, Qnil
);
201 specbind
(Qoverriding_local_map
, Qnil
);
203 set_buffer_internal_1
(XBUFFER
(buffer
));
205 /* TODO: for some reason this is not needed in other terms,
206 but some menu updates call Info-extract-pointer which causes
207 abort-on-error if waiting-for-input. Needs further investigation. */
208 owfi
= waiting_for_input
;
209 waiting_for_input
= 0;
211 /* lucid hook and possible reset */
212 safe_run_hooks
(Qactivate_menubar_hook
);
213 if (! NILP
(Vlucid_menu_bar_dirty_flag
))
214 call0
(Qrecompute_lucid_menubar
);
215 safe_run_hooks
(Qmenu_bar_update_hook
);
216 fset_menu_bar_items
(f
, menu_bar_items
(FRAME_MENU_BAR_ITEMS
(f
)));
218 /* Now ready to go */
219 items
= FRAME_MENU_BAR_ITEMS
(f
);
221 /* Save the frame's previous menu bar contents data */
222 if (previous_menu_items_used
)
223 memcpy
(previous_items
, aref_addr
(f
->menu_bar_vector
, 0),
224 previous_menu_items_used
* sizeof
(Lisp_Object
));
226 /* parse stage 1: extract from lisp */
229 menu_items
= f
->menu_bar_vector
;
230 menu_items_allocated
= VECTORP
(menu_items
) ? ASIZE
(menu_items
) : 0;
231 submenu_start
= alloca
(ASIZE
(items
) * sizeof
*submenu_start
);
232 submenu_end
= alloca
(ASIZE
(items
) * sizeof
*submenu_end
);
233 submenu_n_panes
= alloca
(ASIZE
(items
) * sizeof
*submenu_n_panes
);
234 submenu_top_level_items
= alloca
(ASIZE
(items
)
235 * sizeof
*submenu_top_level_items
);
237 for (i
= 0; i
< ASIZE
(items
); i
+= 4)
239 Lisp_Object key
, string
, maps
;
241 key
= AREF
(items
, i
);
242 string
= AREF
(items
, i
+ 1);
243 maps
= AREF
(items
, i
+ 2);
247 /* FIXME: we'd like to only parse the needed submenu, but this
248 was causing crashes in the _common parsing code.. need to make
249 sure proper initialization done.. */
250 /* if (submenu && strcmp (submenuTitle, SSDATA (string)))
253 submenu_start
[i
] = menu_items_used
;
255 menu_items_n_panes
= 0;
256 submenu_top_level_items
[i
] = parse_single_submenu
(key
, string
, maps
);
257 submenu_n_panes
[i
] = menu_items_n_panes
;
258 submenu_end
[i
] = menu_items_used
;
262 finish_menu_items
();
263 waiting_for_input
= owfi
;
266 if (submenu
&& n
== 0)
268 /* should have found a menu for this one but didn't */
269 fprintf (stderr
, "
ERROR: did not
find lisp
menu for submenu
'%s'.
\n"
,
271 discard_menu_items
();
272 unbind_to
(specpdl_count
, Qnil
);
278 /* parse stage 2: insert into lucid 'widget_value' structures
279 [comments in other terms say not to evaluate lisp code here] */
280 wv
= xmalloc_widget_value
();
281 wv
->name
= "menubar"
;
284 wv
->button_type
= BUTTON_TYPE_NONE
;
288 for (i
= 0; i
< 4*n
; i
+= 4)
290 menu_items_n_panes
= submenu_n_panes
[i
];
291 wv
= digest_single_submenu
(submenu_start
[i
], submenu_end
[i
],
292 submenu_top_level_items
[i
]);
296 first_wv
->contents
= wv
;
297 /* Don't set wv->name here; GC during the loop might relocate it. */
299 wv
->button_type
= BUTTON_TYPE_NONE
;
303 set_buffer_internal_1
(prev
);
305 /* Compare the new menu items with previous, and leave off if no change */
306 /* FIXME: following other terms here, but seems like this should be
307 done before parse stage 2 above, since its results aren't used */
308 if (previous_menu_items_used
309 && (!submenu ||
(submenu
&& submenu
== last_submenu
))
310 && menu_items_used
== previous_menu_items_used
)
312 for (i
= 0; i
< previous_menu_items_used
; i
++)
313 /* FIXME: this ALWAYS fails on Buffers menu items.. something
314 about their strings causes them to change every time, so we
315 double-check failures */
316 if (!EQ
(previous_items
[i
], AREF
(menu_items
, i
)))
317 if (!(STRINGP
(previous_items
[i
])
318 && STRINGP
(AREF
(menu_items
, i
))
319 && !strcmp (SSDATA
(previous_items
[i
]),
320 SSDATA
(AREF
(menu_items
, i
)))))
322 if (i
== previous_menu_items_used
)
328 t
+= 1000*tb.time
+tb.millitm
;
329 fprintf (stderr
, "NO CHANGE
! CUTTING OUT after
%ld msec.\n", t);
332 free_menubar_widget_value_tree
(first_wv
);
333 discard_menu_items
();
334 unbind_to
(specpdl_count
, Qnil
);
340 /* The menu items are different, so store them in the frame */
341 /* FIXME: this is not correct for single-submenu case */
342 fset_menu_bar_vector
(f
, menu_items
);
343 f
->menu_bar_items_used
= menu_items_used
;
345 /* Calls restore_menu_items, etc., as they were outside */
346 unbind_to
(specpdl_count
, Qnil
);
348 /* Parse stage 2a: now GC cannot happen during the lifetime of the
349 widget_value, so it's safe to store data from a Lisp_String */
350 wv
= first_wv
->contents
;
351 for (i
= 0; i
< ASIZE
(items
); i
+= 4)
354 string
= AREF
(items
, i
+ 1);
357 /* if (submenu && strcmp (submenuTitle, SSDATA (string)))
360 wv
->name
= SSDATA
(string
);
361 update_submenu_strings
(wv
->contents
);
365 /* Now, update the NS menu; if we have a submenu, use that, otherwise
366 create a new menu for each sub and fill it. */
369 for (wv
= first_wv
->contents
; wv
; wv
= wv
->next
)
371 if (!strcmp (submenuTitle
, wv
->name
))
373 [submenu fillWithWidgetValue
: wv
->contents
];
374 last_submenu
= submenu
;
381 [menu fillWithWidgetValue
: first_wv
->contents
];
387 static int n_previous_strings
= 0;
388 static
char previous_strings
[100][10];
389 static
struct frame
*last_f
= NULL;
393 wv
= xmalloc_widget_value
();
394 wv
->name
= "menubar"
;
397 wv
->button_type
= BUTTON_TYPE_NONE
;
401 /* Make widget-value tree w/ just the top level menu bar strings */
402 items
= FRAME_MENU_BAR_ITEMS
(f
);
405 free_menubar_widget_value_tree
(first_wv
);
412 /* check if no change.. this mechanism is a bit rough, but ready */
413 n
= ASIZE
(items
) / 4;
414 if (f
== last_f
&& n_previous_strings
== n
)
416 for (i
= 0; i
<n
; i
++)
418 string
= AREF
(items
, 4*i
+1);
420 if (EQ
(string
, make_number
(0))) // FIXME
: Why???
--Stef
424 if (previous_strings
[i
][0])
429 else if (memcmp
(previous_strings
[i
], SDATA
(string
),
430 min (10, SBYTES
(string
) + 1)))
436 free_menubar_widget_value_tree
(first_wv
);
444 for (i
= 0; i
< ASIZE
(items
); i
+= 4)
446 string
= AREF
(items
, i
+ 1);
451 memcpy
(previous_strings
[i
/4], SDATA
(string
),
452 min (10, SBYTES
(string
) + 1));
454 wv
= xmalloc_widget_value
();
455 wv
->name
= SSDATA
(string
);
458 wv
->button_type
= BUTTON_TYPE_NONE
;
460 wv
->call_data
= (void
*) (intptr_t
) (-1);
463 /* we'll update the real copy under app menu when time comes */
464 if (!strcmp ("Services"
, wv
->name
))
466 /* but we need to make sure it will update on demand */
467 [svcsMenu setFrame
: f
];
471 [menu addSubmenuWithTitle
: wv
->name forFrame
: f
];
476 first_wv
->contents
= wv
;
482 n_previous_strings
= n
;
484 n_previous_strings
= 0;
487 free_menubar_widget_value_tree
(first_wv
);
492 t
+= 1000*tb.time
+tb.millitm
;
493 fprintf (stderr
, "
Menu update took
%ld msec.\n", t);
498 [NSApp setMainMenu
: menu];
506 /* Main emacs core entry point for menubar menus: called to indicate that the
507 frame's menus have changed, and the *step representation should be updated
510 set_frame_menubar
(struct frame
*f
, int first_time
, int deep_p
)
512 ns_update_menubar
(f
, deep_p
, nil
);
516 /* ==========================================================================
518 Menu: class implementation
520 ========================================================================== */
523 /* Menu that can define itself from Emacs "widget_value"s and will lazily
524 update itself when user clicked. Based on Carbon/AppKit implementation
525 by Yamamoto Mitsuharu. */
526 @implementation EmacsMenu
528 /* override designated initializer */
529 - initWithTitle
: (NSString
*)title
531 if ((self
= [super initWithTitle
: title]))
532 [self setAutoenablesItems
: NO
];
537 /* used for top-level */
538 - initWithTitle
: (NSString
*)title frame
: (struct frame
*)f
540 [self initWithTitle
: title];
543 [self setDelegate
: self
];
549 - (void
)setFrame
: (struct frame
*)f
555 #
if MAC_OS_X_VERSION_MAX_ALLOWED
< MAC_OS_X_VERSION_10_5
556 extern NSString
*NSMenuDidBeginTrackingNotification
;
561 -(void
)trackingNotification
:(NSNotification
*)notification
563 /* Update menu in menuNeedsUpdate only while tracking menus. */
564 trackingMenu
= ([notification name
] == NSMenuDidBeginTrackingNotification
569 /* delegate method called when a submenu is being opened: run a 'deep' call
570 to set_frame_menubar */
571 - (void
)menuNeedsUpdate
: (NSMenu
*)menu
573 if (!FRAME_LIVE_P
(frame
))
576 /* Cocoa/Carbon will request update on every keystroke
577 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
578 since key equivalents are handled through emacs.
579 On Leopard, even keystroke events generate SystemDefined event.
580 Third-party applications that enhance mouse / trackpad
581 interaction, or also VNC/Remote Desktop will send events
582 of type AppDefined rather than SysDefined.
583 Menus will fail to show up if they haven't been initialized.
584 AppDefined events may lack timing data.
586 Thus, we rely on the didBeginTrackingNotification notification
587 as above to indicate the need for updates.
588 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
589 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
591 if (trackingMenu
== 0
592 /* Also, don't try this if from an event picked up asynchronously,
593 as lots of lisp evaluation happens in ns_update_menubar. */
594 || handling_signal
!= 0)
596 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
597 ns_update_menubar
(frame
, 1, self
);
601 - (BOOL
)performKeyEquivalent
: (NSEvent
*)theEvent
603 if (SELECTED_FRAME
() && FRAME_NS_P
(SELECTED_FRAME
())
604 && FRAME_NS_VIEW
(SELECTED_FRAME
()))
605 [FRAME_NS_VIEW
(SELECTED_FRAME
()) keyDown
: theEvent
];
610 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
611 into an accelerator string. We are only able to display a single character
612 for an accelerator, together with an optional modifier combination. (Under
613 Carbon more control was possible, but in Cocoa multi-char strings passed to
614 NSMenuItem get ignored. For now we try to display a super-single letter
615 combo, and return the others as strings to be appended to the item title.
616 (This is signaled by setting keyEquivModMask to 0 for now.) */
617 -(NSString
*)parseKeyEquiv
: (const
char *)key
619 const
char *tpos
= key
;
620 keyEquivModMask
= NSCommandKeyMask
;
622 if (!key ||
!strlen
(key
))
625 while (*tpos
== ' ' ||
*tpos
== '(')
627 if ((*tpos
== 's') && (*(tpos
+1) == '-'))
629 return [NSString stringWithFormat
: @"
%c", tpos[2]];
631 keyEquivModMask
= 0; /* signal */
632 return [NSString stringWithUTF8String
: tpos
];
636 - (NSMenuItem
*)addItemWithWidgetValue
: (void
*)wvptr
639 widget_value
*wv
= (widget_value
*)wvptr
;
641 if (menu_separator_name_p
(wv
->name
))
643 item
= [NSMenuItem separatorItem
];
644 [self addItem
: item
];
648 NSString
*title, *keyEq
;
649 title = [NSString stringWithUTF8String
: wv
->name
];
651 title = @"
< ?
>"
; /* (get out in the open so we know about it) */
653 keyEq
= [self parseKeyEquiv
: wv
->key
];
655 /* OS X just ignores modifier strings longer than one character */
656 if (keyEquivModMask
== 0)
657 title = [title stringByAppendingFormat
: @"
(%@)", keyEq];
660 item
= [self addItemWithTitle
: (NSString
*)title
661 action
: @selector
(menuDown
:)
662 keyEquivalent
: keyEq
];
663 [item setKeyEquivalentModifierMask
: keyEquivModMask
];
665 [item setEnabled
: wv
->enabled
];
667 /* Draw radio buttons and tickboxes */
668 if (wv
->selected
&& (wv
->button_type
== BUTTON_TYPE_TOGGLE ||
669 wv
->button_type
== BUTTON_TYPE_RADIO
))
670 [item setState
: NSOnState
];
672 [item setState
: NSOffState
];
674 [item setTag
: (NSInteger
)wv
->call_data
];
686 for (n
= [self numberOfItems
]-1; n
>= 0; n
--)
688 NSMenuItem
*item
= [self itemAtIndex
: n
];
689 NSString
*title = [item
title];
690 if (([title length] == 0 /* OSX 10.5 */
691 ||
[ns_app_name isEqualToString
: title] /* from 10.6 on */
692 ||
[@"Apple" isEqualToString
: title]) /* older */
693 && ![item isSeparatorItem
])
695 [self removeItemAtIndex
: n
];
700 - (void
)fillWithWidgetValue
: (void
*)wvptr
702 widget_value
*wv
= (widget_value
*)wvptr
;
704 /* clear existing contents */
705 [self setMenuChangedMessagesEnabled
: NO
];
708 /* add new contents */
709 for (; wv
!= NULL; wv
= wv
->next
)
711 NSMenuItem
*item
= [self addItemWithWidgetValue
: wv
];
715 EmacsMenu
*submenu
= [[EmacsMenu alloc
] initWithTitle
: [item
title]];
717 [self setSubmenu
: submenu forItem
: item
];
718 [submenu fillWithWidgetValue
: wv
->contents
];
720 [item setAction
: nil
];
724 [self setMenuChangedMessagesEnabled
: YES
];
725 #ifdef NS_IMPL_GNUSTEP
726 if ([[self window
] isVisible
])
729 #
if MAC_OS_X_VERSION_MAX_ALLOWED
< MAC_OS_X_VERSION_10_2
730 if ([self supermenu
] == nil
)
737 /* adds an empty submenu and returns it */
738 - (EmacsMenu
*)addSubmenuWithTitle
: (const
char *)title forFrame
: (struct frame
*)f
740 NSString
*titleStr
= [NSString stringWithUTF8String
: title];
741 NSMenuItem
*item
= [self addItemWithTitle
: titleStr
742 action
: nil
/*@selector (menuDown:) */
744 EmacsMenu
*submenu
= [[EmacsMenu alloc
] initWithTitle
: titleStr frame
: f
];
745 [self setSubmenu
: submenu forItem
: item
];
750 /* run a menu in popup mode */
751 - (Lisp_Object
)runMenuAt
: (NSPoint
)p forFrame
: (struct frame
*)f
752 keymaps
: (int
)keymaps
754 EmacsView
*view = FRAME_NS_VIEW
(f
);
758 /* p = [view convertPoint:p fromView: nil]; */
759 p.y
= NSHeight
([view frame
]) - p.y
;
760 e
= [[view window
] currentEvent
];
761 event
= [NSEvent mouseEventWithType
: NSRightMouseDown
764 timestamp
: [e timestamp
]
765 windowNumber
: [[view window
] windowNumber
]
767 eventNumber
: 0/*[e eventNumber] */
771 context_menu_value
= -1;
772 [NSMenu popUpContextMenu
: self withEvent
: event forView
: view];
773 retVal
= context_menu_value
;
774 context_menu_value
= 0;
776 ? find_and_return_menu_selection
(f
, keymaps
, (void
*)retVal
)
784 /* ==========================================================================
786 Context Menu: implementing functions
788 ========================================================================== */
791 ns_menu_show
(FRAME_PTR f
, int x
, int y
, int for_click
, int keymaps
,
792 Lisp_Object
title, const
char **error)
797 ptrdiff_t specpdl_count
= SPECPDL_INDEX
();
798 widget_value
*wv
, *first_wv
= 0;
802 /* now parse stage 2 as in ns_update_menubar */
803 wv
= xmalloc_widget_value
();
804 wv
->name
= "contextmenu"
;
807 wv
->button_type
= BUTTON_TYPE_NONE
;
812 /* FIXME: a couple of one-line differences prevent reuse */
813 wv
= digest_single_submenu
(0, menu_items_used
, Qnil
);
816 widget_value
*save_wv
= 0, *prev_wv
= 0;
817 widget_value
**submenu_stack
818 = alloca
(menu_items_used
* sizeof
*submenu_stack
);
819 /* Lisp_Object *subprefix_stack
820 = alloca (menu_items_used * sizeof *subprefix_stack); */
821 int submenu_depth
= 0;
825 /* Loop over all panes and items, filling in the tree. */
827 while (i
< menu_items_used
)
829 if (EQ
(AREF
(menu_items
, i
), Qnil
))
831 submenu_stack
[submenu_depth
++] = save_wv
;
837 else if (EQ
(AREF
(menu_items
, i
), Qlambda
))
840 save_wv
= submenu_stack
[--submenu_depth
];
844 else if (EQ
(AREF
(menu_items
, i
), Qt
)
845 && submenu_depth
!= 0)
846 i
+= MENU_ITEMS_PANE_LENGTH
;
847 /* Ignore a nil in the item list.
848 It's meaningful only for dialog boxes. */
849 else if (EQ
(AREF
(menu_items
, i
), Qquote
))
851 else if (EQ
(AREF
(menu_items
, i
), Qt
))
853 /* Create a new pane. */
854 Lisp_Object pane_name
, prefix
;
855 const
char *pane_string
;
857 pane_name
= AREF
(menu_items
, i
+ MENU_ITEMS_PANE_NAME
);
858 prefix
= AREF
(menu_items
, i
+ MENU_ITEMS_PANE_PREFIX
);
860 #ifndef HAVE_MULTILINGUAL_MENU
861 if (STRINGP
(pane_name
) && STRING_MULTIBYTE
(pane_name
))
863 pane_name
= ENCODE_MENU_STRING
(pane_name
);
864 ASET
(menu_items
, i
+ MENU_ITEMS_PANE_NAME
, pane_name
);
867 pane_string
= (NILP
(pane_name
)
868 ? ""
: SSDATA
(pane_name
));
869 /* If there is just one top-level pane, put all its items directly
870 under the top-level menu. */
871 if (menu_items_n_panes
== 1)
874 /* If the pane has a meaningful name,
875 make the pane a top-level menu item
876 with its items as a submenu beneath it. */
877 if (!keymaps
&& strcmp (pane_string
, ""
))
879 wv
= xmalloc_widget_value
();
883 first_wv
->contents
= wv
;
884 wv
->name
= pane_string
;
885 if (keymaps
&& !NILP
(prefix
))
889 wv
->button_type
= BUTTON_TYPE_NONE
;
900 i
+= MENU_ITEMS_PANE_LENGTH
;
904 /* Create a new item within current pane. */
905 Lisp_Object item_name
, enable
, descrip
, def
, type, selected
, help;
906 item_name
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_NAME
);
907 enable
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_ENABLE
);
908 descrip
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
);
909 def
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_DEFINITION
);
910 type = AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_TYPE
);
911 selected
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_SELECTED
);
912 help = AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_HELP
);
914 #ifndef HAVE_MULTILINGUAL_MENU
915 if (STRINGP
(item_name
) && STRING_MULTIBYTE
(item_name
))
917 item_name
= ENCODE_MENU_STRING
(item_name
);
918 ASET
(menu_items
, i
+ MENU_ITEMS_ITEM_NAME
, item_name
);
921 if (STRINGP
(descrip
) && STRING_MULTIBYTE
(descrip
))
923 descrip
= ENCODE_MENU_STRING
(descrip
);
924 ASET
(menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
, descrip
);
926 #endif
/* not HAVE_MULTILINGUAL_MENU */
928 wv
= xmalloc_widget_value
();
932 save_wv
->contents
= wv
;
933 wv
->name
= SSDATA
(item_name
);
935 wv
->key
= SSDATA
(descrip
);
937 /* If this item has a null value,
938 make the call_data null so that it won't display a box
939 when the mouse is on it. */
940 wv
->call_data
= !NILP
(def
) ? aref_addr
(menu_items
, i
) : 0;
941 wv
->enabled
= !NILP
(enable
);
944 wv
->button_type
= BUTTON_TYPE_NONE
;
945 else if (EQ
(type, QCtoggle
))
946 wv
->button_type
= BUTTON_TYPE_TOGGLE
;
947 else if (EQ
(type, QCradio
))
948 wv
->button_type
= BUTTON_TYPE_RADIO
;
952 wv
->selected
= !NILP
(selected
);
954 if (! STRINGP
(help))
961 i
+= MENU_ITEMS_ITEM_LENGTH
;
969 widget_value
*wv_title
= xmalloc_widget_value
();
970 widget_value
*wv_sep
= xmalloc_widget_value
();
972 /* Maybe replace this separator with a bitmap or owner-draw item
973 so that it looks better. Having two separators looks odd. */
975 wv_sep
->next
= first_wv
->contents
;
978 #ifndef HAVE_MULTILINGUAL_MENU
979 if (STRING_MULTIBYTE
(title))
980 title = ENCODE_MENU_STRING
(title);
983 wv_title
->name
= SSDATA
(title);
984 wv_title
->enabled
= NO
;
985 wv_title
->button_type
= BUTTON_TYPE_NONE
;
986 wv_title
->help = Qnil
;
987 wv_title
->next
= wv_sep
;
988 first_wv
->contents
= wv_title
;
991 pmenu
= [[EmacsMenu alloc
] initWithTitle
:
992 [NSString stringWithUTF8String
: SSDATA
(title)]];
993 [pmenu fillWithWidgetValue
: first_wv
->contents
];
994 free_menubar_widget_value_tree
(first_wv
);
995 unbind_to
(specpdl_count
, Qnil
);
997 popup_activated_flag
= 1;
998 tem
= [pmenu runMenuAt
: p forFrame
: f keymaps
: keymaps
];
999 popup_activated_flag
= 0;
1000 [[FRAME_NS_VIEW
(SELECTED_FRAME
()) window
] makeKeyWindow
];
1006 /* ==========================================================================
1008 Toolbar: externally-called functions
1010 ========================================================================== */
1013 free_frame_tool_bar
(FRAME_PTR f
)
1014 /* --------------------------------------------------------------------------
1015 Under NS we just hide the toolbar until it might be needed again.
1016 -------------------------------------------------------------------------- */
1019 [[FRAME_NS_VIEW
(f
) toolbar
] setVisible
: NO
];
1020 FRAME_TOOLBAR_HEIGHT
(f
) = 0;
1025 update_frame_tool_bar
(FRAME_PTR f
)
1026 /* --------------------------------------------------------------------------
1027 Update toolbar contents
1028 -------------------------------------------------------------------------- */
1031 EmacsView
*view = FRAME_NS_VIEW
(f
);
1032 NSWindow
*window
= [view window
];
1033 EmacsToolbar
*toolbar
= [view toolbar
];
1036 [toolbar clearActive
];
1038 /* update EmacsToolbar as in GtkUtils, build items list */
1039 for (i
= 0; i
< f
->n_tool_bar_items
; ++i
)
1041 #define TOOLPROP
(IDX
) AREF
(f
->tool_bar_items
, \
1042 i
* TOOL_BAR_ITEM_NSLOTS
+ (IDX
))
1044 BOOL enabled_p
= !NILP
(TOOLPROP
(TOOL_BAR_ITEM_ENABLED_P
));
1049 Lisp_Object helpObj
;
1050 const
char *helpText
;
1052 /* If image is a vector, choose the image according to the
1054 image = TOOLPROP
(TOOL_BAR_ITEM_IMAGES
);
1055 if (VECTORP
(image))
1057 /* NS toolbar auto-computes disabled and selected images */
1058 idx
= TOOL_BAR_IMAGE_ENABLED_SELECTED
;
1059 eassert
(ASIZE
(image) >= idx
);
1060 image = AREF
(image, idx
);
1066 helpObj
= TOOLPROP
(TOOL_BAR_ITEM_HELP
);
1068 helpObj
= TOOLPROP
(TOOL_BAR_ITEM_CAPTION
);
1069 helpText
= NILP
(helpObj
) ? ""
: SSDATA
(helpObj
);
1071 /* Ignore invalid image specifications. */
1072 if (!valid_image_p
(image))
1074 /* Don't log anything, GNUS makes invalid images all the time. */
1078 img_id
= lookup_image
(f
, image);
1079 img
= IMAGE_FROM_ID
(f
, img_id
);
1080 prepare_image_for_display
(f
, img
);
1082 if (img
->load_failed_p || img
->pixmap
== nil
)
1084 NSLog
(@"Could not prepare toolbar
image for display."
);
1088 [toolbar addDisplayItemWithImage
: img
->pixmap idx
: i helpText
: helpText
1089 enabled
: enabled_p
];
1093 if (![toolbar isVisible
])
1094 [toolbar setVisible
: YES
];
1096 if ([toolbar changed
])
1098 /* inform app that toolbar has changed */
1099 NSDictionary
*dict
= [toolbar configurationDictionary
];
1100 NSMutableDictionary
*newDict
= [dict mutableCopy
];
1101 NSEnumerator
*keys
= [[dict allKeys
] objectEnumerator
];
1103 while ((key
= [keys nextObject
]) != nil
)
1105 NSObject
*val
= [dict objectForKey
: key
];
1106 if ([val isKindOfClass
: [NSArray
class]])
1109 [toolbar toolbarDefaultItemIdentifiers
: toolbar
]
1114 [toolbar setConfigurationFromDictionary
: newDict
];
1118 FRAME_TOOLBAR_HEIGHT
(f
) =
1119 NSHeight
([window frameRectForContentRect
: NSMakeRect
(0, 0, 0, 0)])
1120 - FRAME_NS_TITLEBAR_HEIGHT
(f
);
1125 /* ==========================================================================
1127 Toolbar: class implementation
1129 ========================================================================== */
1131 @implementation EmacsToolbar
1133 - initForView
: (EmacsView
*)view withIdentifier
: (NSString
*)identifier
1135 self
= [super initWithIdentifier
: identifier
];
1137 [self setDisplayMode
: NSToolbarDisplayModeIconOnly
];
1138 [self setSizeMode
: NSToolbarSizeModeSmall
];
1139 [self setDelegate
: self
];
1140 identifierToItem
= [[NSMutableDictionary alloc
] initWithCapacity
: 10];
1141 activeIdentifiers
= [[NSMutableArray alloc
] initWithCapacity
: 8];
1142 prevEnablement
= enablement
= 0L;
1148 [prevIdentifiers release
];
1149 [activeIdentifiers release
];
1150 [identifierToItem release
];
1154 - (void
) clearActive
1156 [prevIdentifiers release
];
1157 prevIdentifiers
= [activeIdentifiers copy
];
1158 [activeIdentifiers removeAllObjects
];
1159 prevEnablement
= enablement
;
1165 return [activeIdentifiers isEqualToArray
: prevIdentifiers
] &&
1166 enablement
== prevEnablement ? NO
: YES
;
1169 - (void
) addDisplayItemWithImage
: (EmacsImage
*)img idx
: (int
)idx
1170 helpText
: (const
char *)help enabled
: (BOOL
)enabled
1172 /* 1) come up w/identifier */
1173 NSString
*identifier
1174 = [NSString stringWithFormat
: @"
%u", [img hash]];
1176 /* 2) create / reuse item */
1177 NSToolbarItem
*item
= [identifierToItem objectForKey
: identifier
];
1180 item
= [[[NSToolbarItem alloc
] initWithItemIdentifier
: identifier
]
1182 [item setImage
: img
];
1183 [item setToolTip
: [NSString stringWithUTF8String
: help]];
1184 [item setTarget
: emacsView
];
1185 [item setAction
: @selector
(toolbarClicked
:)];
1189 [item setEnabled
: enabled
];
1191 /* 3) update state */
1192 [identifierToItem setObject
: item forKey
: identifier
];
1193 [activeIdentifiers addObject
: identifier
];
1194 enablement
= (enablement
<< 1) |
(enabled
== YES
);
1197 /* This overrides super's implementation, which automatically sets
1198 all items to enabled state (for some reason). */
1199 - (void
)validateVisibleItems
{ }
1202 /* delegate methods */
1204 - (NSToolbarItem
*)toolbar
: (NSToolbar
*)toolbar
1205 itemForItemIdentifier
: (NSString
*)itemIdentifier
1206 willBeInsertedIntoToolbar
: (BOOL
)flag
1208 /* look up NSToolbarItem by identifier and return... */
1209 return [identifierToItem objectForKey
: itemIdentifier
];
1212 - (NSArray
*)toolbarDefaultItemIdentifiers
: (NSToolbar
*)toolbar
1214 /* return entire set.. */
1215 return activeIdentifiers
;
1218 /* for configuration palette (not yet supported) */
1219 - (NSArray
*)toolbarAllowedItemIdentifiers
: (NSToolbar
*)toolbar
1221 /* return entire set... */
1222 return [identifierToItem allKeys
];
1225 /* optional and unneeded */
1226 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1227 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1228 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1230 @
end /* EmacsToolbar */
1234 /* ==========================================================================
1236 Tooltip: class implementation
1238 ========================================================================== */
1240 /* Needed because NeXTstep does not provide enough control over tooltip
1242 @implementation EmacsTooltip
1246 NSColor
*col
= [NSColor colorWithCalibratedRed
: 1.0 green
: 1.0
1247 blue
: 0.792 alpha
: 0.95];
1248 NSFont
*font
= [NSFont toolTipsFontOfSize
: 0];
1249 NSFont
*sfont
= [font screenFont
];
1250 int height
= [sfont ascender
] - [sfont descender
];
1251 /*[font boundingRectForFont].size.height; */
1252 NSRect r
= NSMakeRect
(0, 0, 100, height
+6);
1254 textField
= [[NSTextField alloc
] initWithFrame
: r
];
1255 [textField setFont
: font
];
1256 [textField setBackgroundColor
: col
];
1258 [textField setEditable
: NO
];
1259 [textField setSelectable
: NO
];
1260 [textField setBordered
: NO
];
1261 [textField setBezeled
: NO
];
1262 [textField setDrawsBackground
: YES
];
1264 win
= [[NSWindow alloc
]
1265 initWithContentRect
: [textField frame
]
1267 backing
: NSBackingStoreBuffered
1269 [win setHasShadow
: YES
];
1270 [win setReleasedWhenClosed
: NO
];
1271 [win setDelegate
: self
];
1272 [[win contentView
] addSubview
: textField
];
1273 /* [win setBackgroundColor: col]; */
1274 [win setOpaque
: NO
];
1283 [textField release
];
1287 - (void
) setText
: (char *)text
1289 NSString
*str
= [NSString stringWithUTF8String
: text];
1290 NSRect r
= [textField frame
];
1293 [textField setStringValue
: str
];
1294 tooltipDims
= [[textField
cell] cellSize
];
1296 r.
size.width
= tooltipDims.width
;
1297 r.
size.height
= tooltipDims.height
;
1298 [textField setFrame
: r
];
1301 - (void
) showAtX
: (int
)x Y
: (int
)y
for: (int
)seconds
1303 NSRect wr
= [win frame
];
1305 wr.origin
= NSMakePoint
(x
, y
);
1306 wr.
size = [textField frame
].
size;
1308 [win setFrame
: wr display
: YES
];
1309 [win orderFront
: self
];
1311 timer
= [NSTimer scheduledTimerWithTimeInterval
: (float
)seconds target
: self
1312 selector
: @selector
(hide
)
1313 userInfo
: nil repeats
: NO
];
1322 if ([timer isValid
])
1331 return timer
!= nil
;
1336 return [textField frame
];
1339 @
end /* EmacsTooltip */
1343 /* ==========================================================================
1345 Popup Dialog: implementing functions
1347 ========================================================================== */
1351 NSAutoreleasePool
*pool
;
1352 EmacsDialogPanel
*dialog;
1356 pop_down_menu
(Lisp_Object arg
)
1358 struct Lisp_Save_Value
*p
= XSAVE_VALUE
(arg
);
1359 struct Popdown_data
*unwind_data
= (struct Popdown_data
*) p
->pointer
;
1362 if (popup_activated_flag
)
1364 EmacsDialogPanel
*panel
= unwind_data
->dialog;
1365 popup_activated_flag
= 0;
1367 [unwind_data
->pool release
];
1368 [[FRAME_NS_VIEW
(SELECTED_FRAME
()) window
] makeKeyWindow
];
1371 xfree
(unwind_data
);
1379 ns_popup_dialog
(Lisp_Object position
, Lisp_Object contents
, Lisp_Object header
)
1382 Lisp_Object window
, tem
, title;
1386 NSAutoreleasePool
*pool
;
1388 NSTRACE
(x
-popup
-dialog);
1392 isQ
= NILP
(header
);
1394 if (EQ
(position
, Qt
)
1395 ||
(CONSP
(position
) && (EQ
(XCAR
(position
), Qmenu_bar
)
1396 || EQ
(XCAR
(position
), Qtool_bar
))))
1398 window
= selected_window
;
1400 else if (CONSP
(position
))
1403 tem
= Fcar
(position
);
1404 if (XTYPE
(tem
) == Lisp_Cons
)
1405 window
= Fcar
(Fcdr
(position
));
1408 tem
= Fcar
(Fcdr
(position
)); /* EVENT_START (position) */
1409 window
= Fcar
(tem
); /* POSN_WINDOW (tem) */
1412 else if (WINDOWP
(position
) || FRAMEP
(position
))
1419 if (FRAMEP
(window
))
1420 f
= XFRAME
(window
);
1421 else if (WINDOWP
(window
))
1423 CHECK_LIVE_WINDOW
(window
);
1424 f
= XFRAME
(WINDOW_FRAME
(XWINDOW
(window
)));
1427 CHECK_WINDOW
(window
);
1429 p.x
= (int
)f
->left_pos
+ ((int
)FRAME_COLUMN_WIDTH
(f
) * f
->text_cols
)/2;
1430 p.y
= (int
)f
->top_pos
+ (FRAME_LINE_HEIGHT
(f
) * f
->text_lines
)/2;
1432 title = Fcar
(contents
);
1433 CHECK_STRING
(title);
1435 if (NILP
(Fcar
(Fcdr
(contents
))))
1436 /* No buttons specified, add an "Ok" button so users can pop down
1438 contents
= Fcons
(title, Fcons
(Fcons
(build_string
("Ok"
), Qt
), Qnil
));
1441 pool
= [[NSAutoreleasePool alloc
] init
];
1442 dialog = [[EmacsDialogPanel alloc
] initFromContents
: contents
1446 ptrdiff_t specpdl_count
= SPECPDL_INDEX
();
1447 struct Popdown_data
*unwind_data
= xmalloc
(sizeof
(*unwind_data
));
1449 unwind_data
->pool
= pool
;
1450 unwind_data
->dialog = dialog;
1452 record_unwind_protect
(pop_down_menu
, make_save_value
(unwind_data
, 0));
1453 popup_activated_flag
= 1;
1454 tem
= [dialog runDialogAt
: p
];
1455 unbind_to
(specpdl_count
, Qnil
); /* calls pop_down_menu */
1464 /* ==========================================================================
1466 Popup Dialog: class implementation
1468 ========================================================================== */
1470 @interface FlippedView
: NSView
1475 @implementation FlippedView
1482 @implementation EmacsDialogPanel
1485 #define ICONSIZE
64.0
1486 #define TEXTHEIGHT
20.0
1487 #define MINCELLWIDTH
90.0
1489 - initWithContentRect
: (NSRect
)contentRect styleMask
: (NSUInteger
)aStyle
1490 backing
: (NSBackingStoreType
)backingType defer
: (BOOL
)flag
1492 NSSize spacing
= {SPACER
, SPACER
};
1495 NSImageView
*imgView
;
1496 FlippedView
*contentView
;
1499 dialog_return
= Qundefined
;
1500 button_values
= NULL;
1501 area.origin.x
= 3*SPACER
;
1502 area.origin.y
= 2*SPACER
;
1503 area.
size.width
= ICONSIZE
;
1504 area.
size.height
= ICONSIZE
;
1505 img
= [[NSImage imageNamed
: @"NSApplicationIcon"
] copy
];
1506 [img setScalesWhenResized
: YES
];
1507 [img setSize
: NSMakeSize
(ICONSIZE
, ICONSIZE
)];
1508 imgView
= [[NSImageView alloc
] initWithFrame
: area
];
1509 [imgView setImage
: img
];
1510 [imgView setEditable
: NO
];
1512 [imgView autorelease
];
1514 aStyle
= NSTitledWindowMask
;
1518 [super initWithContentRect
: contentRect styleMask
: aStyle
1519 backing
: backingType defer
: flag];
1520 contentView
= [[FlippedView alloc
] initWithFrame
: [[self contentView
] frame
]];
1521 [contentView autorelease
];
1523 [self setContentView
: contentView
];
1525 [[self contentView
] setAutoresizesSubviews
: YES
];
1527 [[self contentView
] addSubview
: imgView
];
1528 [self setTitle
: @""
];
1530 area.origin.x
+= ICONSIZE
+2*SPACER
;
1531 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1532 area.
size.width
= 400;
1533 area.
size.height
= TEXTHEIGHT
;
1534 command
= [[[NSTextField alloc
] initWithFrame
: area
] autorelease
];
1535 [[self contentView
] addSubview
: command
];
1536 [command setStringValue
: ns_app_name
];
1537 [command setDrawsBackground
: NO
];
1538 [command setBezeled
: NO
];
1539 [command setSelectable
: NO
];
1540 [command setFont
: [NSFont boldSystemFontOfSize
: 13.0]];
1542 /* area.origin.x = ICONSIZE+2*SPACER;
1543 area.origin.y = TEXTHEIGHT + 2*SPACER;
1544 area.size.width = 400;
1545 area.size.height= 2;
1546 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1547 [[self contentView] addSubview: tem];
1548 [tem setTitlePosition: NSNoTitle];
1549 [tem setAutoresizingMask: NSViewWidthSizable];*/
1551 /* area.origin.x = ICONSIZE+2*SPACER; */
1552 area.origin.y
+= TEXTHEIGHT
+SPACER
;
1553 area.
size.width
= 400;
1554 area.
size.height
= TEXTHEIGHT
;
1555 title = [[[NSTextField alloc
] initWithFrame
: area
] autorelease
];
1556 [[self contentView
] addSubview
: title];
1557 [title setDrawsBackground
: NO
];
1558 [title setBezeled
: NO
];
1559 [title setSelectable
: NO
];
1560 [title setFont
: [NSFont systemFontOfSize
: 11.0]];
1562 cell = [[[NSButtonCell alloc
] initTextCell
: @""
] autorelease
];
1563 [cell setBordered
: NO
];
1564 [cell setEnabled
: NO
];
1565 [cell setCellAttribute
: NSCellIsInsetButton to
: 8];
1566 [cell setBezelStyle
: NSRoundedBezelStyle
];
1568 matrix
= [[NSMatrix alloc
] initWithFrame
: contentRect
1569 mode
: NSHighlightModeMatrix
1572 numberOfColumns
: 1];
1573 [matrix setFrameOrigin
: NSMakePoint
(area.origin.x
,
1574 area.origin.y
+ (TEXTHEIGHT
+3*SPACER
))];
1575 [matrix setIntercellSpacing
: spacing
];
1576 [matrix autorelease
];
1578 [[self contentView
] addSubview
: matrix
];
1579 [self setOneShot
: YES
];
1580 [self setReleasedWhenClosed
: YES
];
1581 [self setHidesOnDeactivate
: YES
];
1583 NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask
];
1589 - (BOOL
)windowShouldClose
: (id
)sender
1591 window_closed
= YES
;
1598 xfree
(button_values
);
1602 - (void
)process_dialog
: (Lisp_Object
) list
1604 Lisp_Object item
, lst
= list
;
1606 int buttons
= 0, btnnr
= 0;
1608 for (; XTYPE
(lst
) == Lisp_Cons
; lst
= XCDR
(lst
))
1611 if (XTYPE
(item
) == Lisp_Cons
)
1616 button_values
= (Lisp_Object
*) xmalloc
(buttons
* sizeof
(*button_values
));
1618 for (; XTYPE
(list
) == Lisp_Cons
; list
= XCDR
(list
))
1621 if (XTYPE
(item
) == Lisp_String
)
1623 [self addString
: SSDATA
(item
) row
: row
++];
1625 else if (XTYPE
(item
) == Lisp_Cons
)
1627 button_values
[btnnr
] = XCDR
(item
);
1628 [self addButton
: SSDATA
(XCAR
(item
)) value
: btnnr row
: row
++];
1631 else if (NILP
(item
))
1640 - (void
)addButton
: (char *)str value
: (int
)tag row
: (int
)row
1649 cell = [matrix cellAtRow
: row column
: cols
-1];
1650 [cell setTarget
: self
];
1651 [cell setAction
: @selector
(clicked
: )];
1652 [cell setTitle
: [NSString stringWithUTF8String
: str
]];
1654 [cell setBordered
: YES
];
1655 [cell setEnabled
: YES
];
1659 - (void
)addString
: (char *)str row
: (int
)row
1668 cell = [matrix cellAtRow
: row column
: cols
-1];
1669 [cell setTitle
: [NSString stringWithUTF8String
: str
]];
1670 [cell setBordered
: YES
];
1671 [cell setEnabled
: NO
];
1682 - (void
)clicked
: sender
1684 NSArray
*sellist
= nil
;
1687 sellist
= [sender selectedCells
];
1688 if ([sellist count
] < 1)
1691 seltag
= [[sellist objectAtIndex
: 0] tag
];
1692 dialog_return
= button_values
[seltag
];
1697 - initFromContents
: (Lisp_Object
)contents isQuestion
: (BOOL
)isQ
1702 if (XTYPE
(contents
) == Lisp_Cons
)
1704 head
= Fcar
(contents
);
1705 [self process_dialog
: Fcdr
(contents
)];
1710 if (XTYPE
(head
) == Lisp_String
)
1711 [title setStringValue
:
1712 [NSString stringWithUTF8String
: SSDATA
(head
)]];
1713 else if (isQ
== YES
)
1714 [title setStringValue
: @"Question"
];
1716 [title setStringValue
: @"Information"
];
1722 if (cols
== 1 && rows
> 1) /* Never told where to split */
1725 for (i
= 0; i
< rows
/2; i
++)
1727 [matrix putCell
: [matrix cellAtRow
: (rows
+1)/2 column
: 0]
1728 atRow
: i column
: 1];
1729 [matrix removeRow
: (rows
+1)/2];
1735 NSSize csize
= [matrix cellSize
];
1736 if (csize.width
< MINCELLWIDTH
)
1738 csize.width
= MINCELLWIDTH
;
1739 [matrix setCellSize
: csize
];
1740 [matrix sizeToCells
];
1745 [command sizeToFit
];
1749 if (r.
size.width
+r.origin.x
> t.
size.width
+t.origin.x
)
1751 t.origin.x
= r.origin.x
;
1752 t.
size.width
= r.
size.width
;
1754 r
= [command frame
];
1755 if (r.
size.width
+r.origin.x
> t.
size.width
+t.origin.x
)
1757 t.origin.x
= r.origin.x
;
1758 t.
size.width
= r.
size.width
;
1762 s
= [(NSView
*)[self contentView
] frame
];
1763 r.
size.width
+= t.origin.x
+t.
size.width
+2*SPACER
-s.
size.width
;
1764 r.
size.height
+= t.origin.y
+t.
size.height
+SPACER
-s.
size.height
;
1765 [self setFrame
: r display
: NO
];
1773 - (void
)timeout_handler
: (NSTimer
*)timedEntry
1775 NSEvent
*nxev
= [NSEvent otherEventWithType
: NSApplicationDefined
1776 location
: NSMakePoint
(0, 0)
1779 windowNumber
: [[NSApp mainWindow
] windowNumber
]
1780 context
: [NSApp context
]
1786 /* We use sto because stopModal/abortModal out of the main loop does not
1787 seem to work in 10.6. But as we use stop we must send a real event so
1788 the stop is seen and acted upon. */
1790 [NSApp postEvent
: nxev atStart
: NO
];
1793 - (Lisp_Object
)runDialogAt
: (NSPoint
)p
1795 Lisp_Object ret
= Qundefined
;
1797 while (popup_activated_flag
)
1800 EMACS_TIME next_time
= timer_check
();
1802 if (EMACS_TIME_VALID_P
(next_time
))
1804 double time
= EMACS_TIME_TO_DOUBLE
(next_time
);
1805 tmo
= [NSTimer timerWithTimeInterval
: time
1807 selector
: @selector
(timeout_handler
:)
1810 [[NSRunLoop currentRunLoop
] addTimer
: tmo
1811 forMode
: NSModalPanelRunLoopMode
];
1814 dialog_return
= Qundefined
;
1815 [NSApp runModalForWindow
: self
];
1816 ret
= dialog_return
;
1819 if (tmo
!= nil
) [tmo invalidate
]; /* Cancels timer */
1824 if (EQ
(ret
, Qundefined
) && window_closed
)
1825 /* Make close button pressed equivalent to C-g. */
1826 Fsignal
(Qquit
, Qnil
);
1834 /* ==========================================================================
1838 ========================================================================== */
1840 DEFUN
("ns
-reset-menu"
, Fns_reset_menu
, Sns_reset_menu
, 0, 0, 0,
1841 doc: /* Cause the NS menu to be re-calculated. */)
1844 set_frame_menubar
(SELECTED_FRAME
(), 1, 0);
1849 DEFUN
("x
-popup
-dialog"
, Fx_popup_dialog
, Sx_popup_dialog
, 2, 3, 0,
1850 doc: /* Pop up a dialog box and return user's selection.
1851 POSITION specifies which frame to use.
1852 This is normally a mouse button event or a window or frame.
1853 If POSITION is t, it means to use the frame the mouse is on.
1854 The dialog box appears in the middle of the specified frame.
1856 CONTENTS specifies the alternatives to display in the dialog box.
1857 It is a list of the form (DIALOG ITEM1 ITEM2...).
1858 Each ITEM is a cons cell (STRING . VALUE).
1859 The return value is VALUE from the chosen item.
1861 An ITEM may also be just a string--that makes a nonselectable item.
1862 An ITEM may also be nil--that means to put all preceding items
1863 on the left of the dialog box and all following items on the right.
1864 \(By default, approximately half appear on each side.)
1866 If HEADER is non-nil, the frame title for the box is "Information",
1867 otherwise it is "Question".
1869 If the user gets rid of the dialog box without making a valid choice,
1870 for instance using the window manager, then this produces a quit and
1871 `x-popup-dialog' does not return. */)
1872 (Lisp_Object position
, Lisp_Object contents
, Lisp_Object header
)
1874 return ns_popup_dialog
(position
, contents
, header
);
1877 DEFUN
("
menu-or
-popup
-active
-p"
, Fmenu_or_popup_active_p
, Smenu_or_popup_active_p
, 0, 0, 0,
1878 doc: /* Return t if a menu or popup dialog is active. */)
1881 return popup_activated
() ? Qt
: Qnil
;
1884 /* ==========================================================================
1886 Lisp interface declaration
1888 ========================================================================== */
1891 syms_of_nsmenu
(void
)
1893 #ifndef NS_IMPL_COCOA
1894 /* Don't know how to keep track of this in Next/Open/Gnustep. Always
1895 update menus there. */
1898 defsubr
(&Sx_popup_dialog
);
1899 defsubr
(&Sns_reset_menu
);
1900 defsubr
(&Smenu_or_popup_active_p
);
1902 Qdebug_on_next_call
= intern_c_string
("
debug-on
-next
-call"
);
1903 staticpro
(&Qdebug_on_next_call
);