1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007-2014 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
>
49 int menu_trace_num
= 0;
50 #define NSTRACE
(x
) fprintf (stderr
, "
%s:%d: [%d] " #x "\n", \
51 __FILE__
, __LINE__
, ++menu_trace_num
)
57 /* Include lisp -> C common menu parsing code */
58 #define ENCODE_MENU_STRING
(str
) ENCODE_UTF_8
(str
)
59 #include "nsmenu_common.c"
62 extern Lisp_Object Qundefined
, Qmenu_enable
, Qmenu_bar_update_hook
;
63 extern Lisp_Object QCtoggle
, QCradio
;
65 Lisp_Object Qdebug_on_next_call
;
66 extern Lisp_Object Qoverriding_local_map
, Qoverriding_terminal_local_map
;
68 extern
long context_menu_value
;
69 EmacsMenu
*mainMenu
, *svcsMenu
, *dockMenu
;
71 /* Nonzero means a menu is currently active. */
72 static int popup_activated_flag
;
74 /* Nonzero means we are tracking and updating menus. */
75 static int trackingMenu
;
78 /* NOTE: toolbar implementation is at end,
79 following complete menu implementation. */
82 /* ==========================================================================
84 Menu: Externally-called functions
86 ========================================================================== */
89 /* Supposed to discard menubar and free storage. Since we share the
90 menubar among frames and update its context for the focused window,
91 there is nothing to do here. */
93 free_frame_menubar
(struct frame
*f
)
100 popup_activated
(void
)
102 return popup_activated_flag
;
106 /* --------------------------------------------------------------------------
107 Update menubar. Three cases:
108 1) ! deep_p, submenu = nil: Fresh switch onto a frame -- either set up
109 just top-level menu strings (OS X), or goto case (2) (GNUstep).
110 2) deep_p, submenu = nil: Recompute all submenus.
111 3) deep_p, submenu = non-nil: Update contents of a single submenu.
112 -------------------------------------------------------------------------- */
114 ns_update_menubar
(struct frame
*f
, bool deep_p
, EmacsMenu
*submenu
)
116 NSAutoreleasePool
*pool
;
117 id
menu = [NSApp mainMenu
];
118 static EmacsMenu
*last_submenu
= nil
;
122 widget_value
*wv
, *first_wv
, *prev_wv
= 0;
130 NSTRACE
(ns_update_menubar
);
132 if (f
!= SELECTED_FRAME
())
134 XSETFRAME
(Vmenu_updating_frame
, f
);
135 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
138 pool
= [[NSAutoreleasePool alloc
] init
];
140 /* Menu may have been created automatically; if so, discard it. */
141 if ([menu isKindOfClass
: [EmacsMenu
class]] == NO
)
149 menu = [[EmacsMenu alloc
] initWithTitle
: ns_app_name
];
153 { /* close up anything on there */
154 id attMenu
= [menu attachedMenu
];
161 t
= -(1000*tb.time
+tb.millitm
);
164 #ifdef NS_IMPL_GNUSTEP
165 deep_p
= 1; /* until GNUstep NSMenu implements the Panther delegation model */
170 /* Fully parse one or more of the submenus. */
172 int
*submenu_start
, *submenu_end
;
173 bool
*submenu_top_level_items
;
174 int
*submenu_n_panes
;
175 struct buffer
*prev
= current_buffer
;
177 int previous_menu_items_used
= f
->menu_bar_items_used
;
178 Lisp_Object
*previous_items
179 = alloca
(previous_menu_items_used
* sizeof
*previous_items
);
183 /* lisp preliminaries */
184 buffer
= XWINDOW
(FRAME_SELECTED_WINDOW
(f
))->contents
;
185 specbind
(Qinhibit_quit
, Qt
);
186 specbind
(Qdebug_on_next_call
, Qnil
);
187 record_unwind_save_match_data
();
188 if (NILP
(Voverriding_local_map_menu_flag
))
190 specbind
(Qoverriding_terminal_local_map
, Qnil
);
191 specbind
(Qoverriding_local_map
, Qnil
);
193 set_buffer_internal_1
(XBUFFER
(buffer
));
195 /* TODO: for some reason this is not needed in other terms,
196 but some menu updates call Info-extract-pointer which causes
197 abort-on-error if waiting-for-input. Needs further investigation. */
198 owfi
= waiting_for_input
;
199 waiting_for_input
= 0;
201 /* lucid hook and possible reset */
202 safe_run_hooks
(Qactivate_menubar_hook
);
203 if (! NILP
(Vlucid_menu_bar_dirty_flag
))
204 call0
(Qrecompute_lucid_menubar
);
205 safe_run_hooks
(Qmenu_bar_update_hook
);
206 fset_menu_bar_items
(f
, menu_bar_items
(FRAME_MENU_BAR_ITEMS
(f
)));
208 /* Now ready to go */
209 items
= FRAME_MENU_BAR_ITEMS
(f
);
211 /* Save the frame's previous menu bar contents data */
212 if (previous_menu_items_used
)
213 memcpy
(previous_items
, aref_addr
(f
->menu_bar_vector
, 0),
214 previous_menu_items_used
* sizeof
(Lisp_Object
));
216 /* parse stage 1: extract from lisp */
219 menu_items
= f
->menu_bar_vector
;
220 menu_items_allocated
= VECTORP
(menu_items
) ? ASIZE
(menu_items
) : 0;
221 submenu_start
= alloca
(ASIZE
(items
) * sizeof
*submenu_start
);
222 submenu_end
= alloca
(ASIZE
(items
) * sizeof
*submenu_end
);
223 submenu_n_panes
= alloca
(ASIZE
(items
) * sizeof
*submenu_n_panes
);
224 submenu_top_level_items
= alloca
(ASIZE
(items
)
225 * sizeof
*submenu_top_level_items
);
227 for (i
= 0; i
< ASIZE
(items
); i
+= 4)
229 Lisp_Object key
, string
, maps
;
231 key
= AREF
(items
, i
);
232 string
= AREF
(items
, i
+ 1);
233 maps
= AREF
(items
, i
+ 2);
237 /* FIXME: we'd like to only parse the needed submenu, but this
238 was causing crashes in the _common parsing code.. need to make
239 sure proper initialization done.. */
240 /* if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
243 submenu_start
[i
] = menu_items_used
;
245 menu_items_n_panes
= 0;
246 submenu_top_level_items
[i
] = parse_single_submenu
(key
, string
, maps
);
247 submenu_n_panes
[i
] = menu_items_n_panes
;
248 submenu_end
[i
] = menu_items_used
;
252 finish_menu_items
();
253 waiting_for_input
= owfi
;
256 if (submenu
&& n
== 0)
258 /* should have found a menu for this one but didn't */
259 fprintf (stderr
, "
ERROR: did not
find lisp
menu for submenu
'%s'.
\n"
,
260 [[submenu
title] UTF8String
]);
261 discard_menu_items
();
268 /* parse stage 2: insert into lucid 'widget_value' structures
269 [comments in other terms say not to evaluate lisp code here] */
270 wv
= make_widget_value
("menubar"
, NULL, true
, Qnil
);
271 wv
->button_type
= BUTTON_TYPE_NONE
;
274 for (i
= 0; i
< 4*n
; i
+= 4)
276 menu_items_n_panes
= submenu_n_panes
[i
];
277 wv
= digest_single_submenu
(submenu_start
[i
], submenu_end
[i
],
278 submenu_top_level_items
[i
]);
282 first_wv
->contents
= wv
;
283 /* Don't set wv->name here; GC during the loop might relocate it. */
285 wv
->button_type
= BUTTON_TYPE_NONE
;
289 set_buffer_internal_1
(prev
);
291 /* Compare the new menu items with previous, and leave off if no change */
292 /* FIXME: following other terms here, but seems like this should be
293 done before parse stage 2 above, since its results aren't used */
294 if (previous_menu_items_used
295 && (!submenu ||
(submenu
&& submenu
== last_submenu
))
296 && menu_items_used
== previous_menu_items_used
)
298 for (i
= 0; i
< previous_menu_items_used
; i
++)
299 /* FIXME: this ALWAYS fails on Buffers menu items.. something
300 about their strings causes them to change every time, so we
301 double-check failures */
302 if (!EQ
(previous_items
[i
], AREF
(menu_items
, i
)))
303 if (!(STRINGP
(previous_items
[i
])
304 && STRINGP
(AREF
(menu_items
, i
))
305 && !strcmp (SSDATA
(previous_items
[i
]),
306 SSDATA
(AREF
(menu_items
, i
)))))
308 if (i
== previous_menu_items_used
)
314 t
+= 1000*tb.time
+tb.millitm
;
315 fprintf (stderr
, "NO CHANGE
! CUTTING OUT after
%ld msec.\n", t);
318 free_menubar_widget_value_tree
(first_wv
);
319 discard_menu_items
();
326 /* The menu items are different, so store them in the frame */
327 /* FIXME: this is not correct for single-submenu case */
328 fset_menu_bar_vector
(f
, menu_items
);
329 f
->menu_bar_items_used
= menu_items_used
;
331 /* Calls restore_menu_items, etc., as they were outside */
334 /* Parse stage 2a: now GC cannot happen during the lifetime of the
335 widget_value, so it's safe to store data from a Lisp_String */
336 wv
= first_wv
->contents
;
337 for (i
= 0; i
< ASIZE
(items
); i
+= 4)
340 string
= AREF
(items
, i
+ 1);
344 wv
->name
= SSDATA
(string
);
345 update_submenu_strings
(wv
->contents
);
349 /* Now, update the NS menu; if we have a submenu, use that, otherwise
350 create a new menu for each sub and fill it. */
353 const
char *submenuTitle
= [[submenu
title] UTF8String
];
354 for (wv
= first_wv
->contents
; wv
; wv
= wv
->next
)
356 if (!strcmp (submenuTitle
, wv
->name
))
358 [submenu fillWithWidgetValue
: wv
->contents
];
359 last_submenu
= submenu
;
366 [menu fillWithWidgetValue
: first_wv
->contents frame
: f
];
372 static int n_previous_strings
= 0;
373 static
char previous_strings
[100][10];
374 static
struct frame
*last_f
= NULL;
378 wv
= make_widget_value
("menubar"
, NULL, true
, Qnil
);
379 wv
->button_type
= BUTTON_TYPE_NONE
;
382 /* Make widget-value tree w/ just the top level menu bar strings */
383 items
= FRAME_MENU_BAR_ITEMS
(f
);
386 free_menubar_widget_value_tree
(first_wv
);
393 /* check if no change.. this mechanism is a bit rough, but ready */
394 n
= ASIZE
(items
) / 4;
395 if (f
== last_f
&& n_previous_strings
== n
)
397 for (i
= 0; i
<n
; i
++)
399 string
= AREF
(items
, 4*i
+1);
401 if (EQ
(string
, make_number
(0))) // FIXME
: Why???
--Stef
405 if (previous_strings
[i
][0])
410 else if (memcmp
(previous_strings
[i
], SDATA
(string
),
411 min (10, SBYTES
(string
) + 1)))
417 free_menubar_widget_value_tree
(first_wv
);
425 for (i
= 0; i
< ASIZE
(items
); i
+= 4)
427 string
= AREF
(items
, i
+ 1);
432 memcpy
(previous_strings
[i
/4], SDATA
(string
),
433 min (10, SBYTES
(string
) + 1));
435 wv
= make_widget_value
(SSDATA
(string
), NULL, true
, Qnil
);
436 wv
->button_type
= BUTTON_TYPE_NONE
;
437 wv
->call_data
= (void
*) (intptr_t
) (-1);
440 /* we'll update the real copy under app menu when time comes */
441 if (!strcmp ("Services"
, wv
->name
))
443 /* but we need to make sure it will update on demand */
444 [svcsMenu setFrame
: f
];
448 [menu addSubmenuWithTitle
: wv
->name forFrame
: f
];
453 first_wv
->contents
= wv
;
459 n_previous_strings
= n
;
461 n_previous_strings
= 0;
464 free_menubar_widget_value_tree
(first_wv
);
469 t
+= 1000*tb.time
+tb.millitm
;
470 fprintf (stderr
, "
Menu update took
%ld msec.\n", t);
475 [NSApp setMainMenu
: menu];
483 /* Main emacs core entry point for menubar menus: called to indicate that the
484 frame's menus have changed, and the *step representation should be updated
487 set_frame_menubar
(struct frame
*f
, bool first_time
, bool deep_p
)
489 ns_update_menubar
(f
, deep_p
, nil
);
493 x_activate_menubar
(struct frame
*f
)
496 #
if MAC_OS_X_VERSION_MAX_ALLOWED
>= MAC_OS_X_VERSION_10_5
497 ns_update_menubar
(f
, true
, nil
);
498 ns_check_pending_open_menu
();
506 /* ==========================================================================
508 Menu: class implementation
510 ========================================================================== */
513 /* Menu that can define itself from Emacs "widget_value"s and will lazily
514 update itself when user clicked. Based on Carbon/AppKit implementation
515 by Yamamoto Mitsuharu. */
516 @implementation EmacsMenu
518 /* override designated initializer */
519 - initWithTitle
: (NSString
*)title
522 if ((self
= [super initWithTitle
: title]))
523 [self setAutoenablesItems
: NO
];
528 /* used for top-level */
529 - initWithTitle
: (NSString
*)title frame
: (struct frame
*)f
531 [self initWithTitle
: title];
534 [self setDelegate
: self
];
540 - (void
)setFrame
: (struct frame
*)f
546 #
if MAC_OS_X_VERSION_MAX_ALLOWED
< MAC_OS_X_VERSION_10_5
547 extern NSString
*NSMenuDidBeginTrackingNotification
;
552 -(void
)trackingNotification
:(NSNotification
*)notification
554 /* Update menu in menuNeedsUpdate only while tracking menus. */
555 trackingMenu
= ([notification name
] == NSMenuDidBeginTrackingNotification
557 #
if MAC_OS_X_VERSION_MAX_ALLOWED
>= MAC_OS_X_VERSION_10_5
558 if (! trackingMenu
) ns_check_menu_open
(nil
);
562 #
if MAC_OS_X_VERSION_MAX_ALLOWED
>= MAC_OS_X_VERSION_10_5
563 - (void
)menuWillOpen
:(NSMenu
*)menu
567 #
if MAC_OS_X_VERSION_MAX_ALLOWED
< MAC_OS_X_VERSION_10_7
568 // On
10.6 we
get repeated calls
, only the one
for NSSystemDefined is "
real".
569 if ([[NSApp currentEvent
] type] != NSSystemDefined
) return;
572 /* When dragging from one menu to another, we get willOpen followed by didClose,
573 i.e. trackingMenu == 3 in willOpen and then 2 after didClose.
574 We have updated all menus, so avoid doing it when trackingMenu == 3. */
575 if (trackingMenu
== 2)
576 ns_check_menu_open
(menu);
579 - (void
)menuDidClose
:(NSMenu
*)menu
583 #endif
/* OSX >= 10.5 */
585 #endif
/* NS_IMPL_COCOA */
587 /* delegate method called when a submenu is being opened: run a 'deep' call
588 to set_frame_menubar */
589 - (void
)menuNeedsUpdate
: (NSMenu
*)menu
591 if (!FRAME_LIVE_P
(frame
))
594 /* Cocoa/Carbon will request update on every keystroke
595 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
596 since key equivalents are handled through emacs.
597 On Leopard, even keystroke events generate SystemDefined event.
598 Third-party applications that enhance mouse / trackpad
599 interaction, or also VNC/Remote Desktop will send events
600 of type AppDefined rather than SysDefined.
601 Menus will fail to show up if they haven't been initialized.
602 AppDefined events may lack timing data.
604 Thus, we rely on the didBeginTrackingNotification notification
605 as above to indicate the need for updates.
606 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
607 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
609 if (trackingMenu
== 0)
611 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
612 #
if (! defined
(NS_IMPL_COCOA
) \
613 || MAC_OS_X_VERSION_MAX_ALLOWED
< MAC_OS_X_VERSION_10_5
)
614 /* Don't know how to do this for anything other than OSX >= 10.5
615 This is wrong, as it might run Lisp code in the event loop. */
616 ns_update_menubar
(frame
, true
, self
);
621 - (BOOL
)performKeyEquivalent
: (NSEvent
*)theEvent
623 if (SELECTED_FRAME
() && FRAME_NS_P
(SELECTED_FRAME
())
624 && FRAME_NS_VIEW
(SELECTED_FRAME
()))
625 [FRAME_NS_VIEW
(SELECTED_FRAME
()) keyDown
: theEvent
];
630 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
631 into an accelerator string. We are only able to display a single character
632 for an accelerator, together with an optional modifier combination. (Under
633 Carbon more control was possible, but in Cocoa multi-char strings passed to
634 NSMenuItem get ignored. For now we try to display a super-single letter
635 combo, and return the others as strings to be appended to the item title.
636 (This is signaled by setting keyEquivModMask to 0 for now.) */
637 -(NSString
*)parseKeyEquiv
: (const
char *)key
639 const
char *tpos
= key
;
640 keyEquivModMask
= NSCommandKeyMask
;
642 if (!key ||
!strlen
(key
))
645 while (*tpos
== ' ' ||
*tpos
== '(')
647 if ((*tpos
== 's') && (*(tpos
+1) == '-'))
649 return [NSString stringWithFormat
: @"
%c", tpos[2]];
651 keyEquivModMask
= 0; /* signal */
652 return [NSString stringWithUTF8String
: tpos
];
656 - (NSMenuItem
*)addItemWithWidgetValue
: (void
*)wvptr
659 widget_value
*wv
= (widget_value
*)wvptr
;
661 if (menu_separator_name_p
(wv
->name
))
663 item
= [NSMenuItem separatorItem
];
664 [self addItem
: item
];
668 NSString
*title, *keyEq
;
669 title = [NSString stringWithUTF8String
: wv
->name
];
671 title = @"
< ?
>"
; /* (get out in the open so we know about it) */
673 keyEq
= [self parseKeyEquiv
: wv
->key
];
675 /* OS X just ignores modifier strings longer than one character */
676 if (keyEquivModMask
== 0)
677 title = [title stringByAppendingFormat
: @"
(%@)", keyEq];
680 item
= [self addItemWithTitle
: (NSString
*)title
681 action
: @selector
(menuDown
:)
682 keyEquivalent
: keyEq
];
683 [item setKeyEquivalentModifierMask
: keyEquivModMask
];
685 [item setEnabled
: wv
->enabled
];
687 /* Draw radio buttons and tickboxes */
688 if (wv
->selected
&& (wv
->button_type
== BUTTON_TYPE_TOGGLE ||
689 wv
->button_type
== BUTTON_TYPE_RADIO
))
690 [item setState
: NSOnState
];
692 [item setState
: NSOffState
];
694 [item setTag
: (NSInteger
)wv
->call_data
];
706 for (n
= [self numberOfItems
]-1; n
>= 0; n
--)
708 NSMenuItem
*item
= [self itemAtIndex
: n
];
709 NSString
*title = [item
title];
710 if (([title length] == 0 /* OSX 10.5 */
711 ||
[ns_app_name isEqualToString
: title] /* from 10.6 on */
712 ||
[@"Apple" isEqualToString
: title]) /* older */
713 && ![item isSeparatorItem
])
715 [self removeItemAtIndex
: n
];
720 - (void
)fillWithWidgetValue
: (void
*)wvptr
722 [self fillWithWidgetValue
: wvptr frame
: (struct frame
*)nil
];
725 - (void
)fillWithWidgetValue
: (void
*)wvptr frame
: (struct frame
*)f
727 widget_value
*wv
= (widget_value
*)wvptr
;
729 /* clear existing contents */
730 [self setMenuChangedMessagesEnabled
: NO
];
733 /* add new contents */
734 for (; wv
!= NULL; wv
= wv
->next
)
736 NSMenuItem
*item
= [self addItemWithWidgetValue
: wv
];
743 submenu
= [[EmacsMenu alloc
] initWithTitle
: [item
title] frame
:f
];
745 submenu
= [[EmacsMenu alloc
] initWithTitle
: [item
title]];
747 [self setSubmenu
: submenu forItem
: item
];
748 [submenu fillWithWidgetValue
: wv
->contents
];
750 [item setAction
: (SEL
)nil
];
754 [self setMenuChangedMessagesEnabled
: YES
];
755 #ifdef NS_IMPL_GNUSTEP
756 if ([[self window
] isVisible
])
762 /* adds an empty submenu and returns it */
763 - (EmacsMenu
*)addSubmenuWithTitle
: (const
char *)title forFrame
: (struct frame
*)f
765 NSString
*titleStr
= [NSString stringWithUTF8String
: title];
766 NSMenuItem
*item
= [self addItemWithTitle
: titleStr
767 action
: (SEL
)nil
/*@selector (menuDown:) */
769 EmacsMenu
*submenu
= [[EmacsMenu alloc
] initWithTitle
: titleStr frame
: f
];
770 [self setSubmenu
: submenu forItem
: item
];
775 /* run a menu in popup mode */
776 - (Lisp_Object
)runMenuAt
: (NSPoint
)p forFrame
: (struct frame
*)f
777 keymaps
: (bool
)keymaps
779 EmacsView
*view = FRAME_NS_VIEW
(f
);
783 /* p = [view convertPoint:p fromView: nil]; */
784 p.y
= NSHeight
([view frame
]) - p.y
;
785 e
= [[view window
] currentEvent
];
786 event
= [NSEvent mouseEventWithType
: NSRightMouseDown
789 timestamp
: [e timestamp
]
790 windowNumber
: [[view window
] windowNumber
]
792 eventNumber
: 0/*[e eventNumber] */
796 context_menu_value
= -1;
797 [NSMenu popUpContextMenu
: self withEvent
: event forView
: view];
798 retVal
= context_menu_value
;
799 context_menu_value
= 0;
801 ? find_and_return_menu_selection
(f
, keymaps
, (void
*)retVal
)
809 /* ==========================================================================
811 Context Menu: implementing functions
813 ========================================================================== */
816 ns_menu_show
(struct frame
*f
, int x
, int y
, int menuflags
,
817 Lisp_Object
title, const
char **error)
822 widget_value
*wv
, *first_wv
= 0;
823 bool keymaps
= (menuflags
& MENU_KEYMAPS
);
831 /* now parse stage 2 as in ns_update_menubar */
832 wv
= make_widget_value
("contextmenu"
, NULL, true
, Qnil
);
833 wv
->button_type
= BUTTON_TYPE_NONE
;
837 /* FIXME: a couple of one-line differences prevent reuse */
838 wv
= digest_single_submenu
(0, menu_items_used
, 0);
841 widget_value
*save_wv
= 0, *prev_wv
= 0;
842 widget_value
**submenu_stack
843 = alloca
(menu_items_used
* sizeof
*submenu_stack
);
844 /* Lisp_Object *subprefix_stack
845 = alloca (menu_items_used * sizeof *subprefix_stack); */
846 int submenu_depth
= 0;
850 /* Loop over all panes and items, filling in the tree. */
852 while (i
< menu_items_used
)
854 if (EQ
(AREF
(menu_items
, i
), Qnil
))
856 submenu_stack
[submenu_depth
++] = save_wv
;
862 else if (EQ
(AREF
(menu_items
, i
), Qlambda
))
865 save_wv
= submenu_stack
[--submenu_depth
];
869 else if (EQ
(AREF
(menu_items
, i
), Qt
)
870 && submenu_depth
!= 0)
871 i
+= MENU_ITEMS_PANE_LENGTH
;
872 /* Ignore a nil in the item list.
873 It's meaningful only for dialog boxes. */
874 else if (EQ
(AREF
(menu_items
, i
), Qquote
))
876 else if (EQ
(AREF
(menu_items
, i
), Qt
))
878 /* Create a new pane. */
879 Lisp_Object pane_name
, prefix
;
880 const
char *pane_string
;
882 pane_name
= AREF
(menu_items
, i
+ MENU_ITEMS_PANE_NAME
);
883 prefix
= AREF
(menu_items
, i
+ MENU_ITEMS_PANE_PREFIX
);
885 #ifndef HAVE_MULTILINGUAL_MENU
886 if (STRINGP
(pane_name
) && STRING_MULTIBYTE
(pane_name
))
888 pane_name
= ENCODE_MENU_STRING
(pane_name
);
889 ASET
(menu_items
, i
+ MENU_ITEMS_PANE_NAME
, pane_name
);
892 pane_string
= (NILP
(pane_name
)
893 ? ""
: SSDATA
(pane_name
));
894 /* If there is just one top-level pane, put all its items directly
895 under the top-level menu. */
896 if (menu_items_n_panes
== 1)
899 /* If the pane has a meaningful name,
900 make the pane a top-level menu item
901 with its items as a submenu beneath it. */
902 if (!keymaps
&& strcmp (pane_string
, ""
))
904 wv
= make_widget_value
(pane_string
, NULL, true
, Qnil
);
908 first_wv
->contents
= wv
;
909 if (keymaps
&& !NILP
(prefix
))
911 wv
->button_type
= BUTTON_TYPE_NONE
;
921 i
+= MENU_ITEMS_PANE_LENGTH
;
925 /* Create a new item within current pane. */
926 Lisp_Object item_name
, enable
, descrip
, def
, type, selected
, help;
927 item_name
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_NAME
);
928 enable
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_ENABLE
);
929 descrip
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
);
930 def
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_DEFINITION
);
931 type = AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_TYPE
);
932 selected
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_SELECTED
);
933 help = AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_HELP
);
935 #ifndef HAVE_MULTILINGUAL_MENU
936 if (STRINGP
(item_name
) && STRING_MULTIBYTE
(item_name
))
938 item_name
= ENCODE_MENU_STRING
(item_name
);
939 ASET
(menu_items
, i
+ MENU_ITEMS_ITEM_NAME
, item_name
);
942 if (STRINGP
(descrip
) && STRING_MULTIBYTE
(descrip
))
944 descrip
= ENCODE_MENU_STRING
(descrip
);
945 ASET
(menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
, descrip
);
947 #endif
/* not HAVE_MULTILINGUAL_MENU */
949 wv
= make_widget_value
(SSDATA
(item_name
), NULL, !NILP
(enable
),
950 STRINGP
(help) ?
help : Qnil
);
954 save_wv
->contents
= wv
;
956 wv
->key
= SSDATA
(descrip
);
957 /* If this item has a null value,
958 make the call_data null so that it won't display a box
959 when the mouse is on it. */
960 wv
->call_data
= !NILP
(def
) ? aref_addr
(menu_items
, i
) : 0;
963 wv
->button_type
= BUTTON_TYPE_NONE
;
964 else if (EQ
(type, QCtoggle
))
965 wv
->button_type
= BUTTON_TYPE_TOGGLE
;
966 else if (EQ
(type, QCradio
))
967 wv
->button_type
= BUTTON_TYPE_RADIO
;
971 wv
->selected
= !NILP
(selected
);
975 i
+= MENU_ITEMS_ITEM_LENGTH
;
983 widget_value
*wv_title
;
984 widget_value
*wv_sep
= make_widget_value
("
--"
, NULL, false
, Qnil
);
986 /* Maybe replace this separator with a bitmap or owner-draw item
987 so that it looks better. Having two separators looks odd. */
988 wv_sep
->next
= first_wv
->contents
;
990 #ifndef HAVE_MULTILINGUAL_MENU
991 if (STRING_MULTIBYTE
(title))
992 title = ENCODE_MENU_STRING
(title);
994 wv_title
= make_widget_value
(SSDATA
(title), NULL, false
, Qnil
);
995 wv_title
->button_type
= BUTTON_TYPE_NONE
;
996 wv_title
->next
= wv_sep
;
997 first_wv
->contents
= wv_title
;
1000 pmenu
= [[EmacsMenu alloc
] initWithTitle
:
1001 [NSString stringWithUTF8String
: SSDATA
(title)]];
1002 [pmenu fillWithWidgetValue
: first_wv
->contents
];
1003 free_menubar_widget_value_tree
(first_wv
);
1006 popup_activated_flag
= 1;
1007 tem
= [pmenu runMenuAt
: p forFrame
: f keymaps
: keymaps
];
1008 popup_activated_flag
= 0;
1009 [[FRAME_NS_VIEW
(SELECTED_FRAME
()) window
] makeKeyWindow
];
1016 /* ==========================================================================
1018 Toolbar: externally-called functions
1020 ========================================================================== */
1023 free_frame_tool_bar
(struct frame
*f
)
1024 /* --------------------------------------------------------------------------
1025 Under NS we just hide the toolbar until it might be needed again.
1026 -------------------------------------------------------------------------- */
1028 EmacsView
*view = FRAME_NS_VIEW
(f
);
1030 view->wait_for_tool_bar
= NO
;
1031 [[view toolbar
] setVisible
: NO
];
1032 FRAME_TOOLBAR_HEIGHT
(f
) = 0;
1037 update_frame_tool_bar
(struct frame
*f
)
1038 /* --------------------------------------------------------------------------
1039 Update toolbar contents
1040 -------------------------------------------------------------------------- */
1043 EmacsView
*view = FRAME_NS_VIEW
(f
);
1044 NSWindow
*window
= [view window
];
1045 EmacsToolbar
*toolbar
= [view toolbar
];
1047 if (view == nil || toolbar
== nil
) return;
1050 #ifdef NS_IMPL_COCOA
1051 [toolbar clearActive
];
1056 /* update EmacsToolbar as in GtkUtils, build items list */
1057 for (i
= 0; i
< f
->n_tool_bar_items
; ++i
)
1059 #define TOOLPROP
(IDX
) AREF
(f
->tool_bar_items
, \
1060 i
* TOOL_BAR_ITEM_NSLOTS
+ (IDX
))
1062 BOOL enabled_p
= !NILP
(TOOLPROP
(TOOL_BAR_ITEM_ENABLED_P
));
1067 Lisp_Object helpObj
;
1068 const
char *helpText
;
1070 /* Check if this is a separator. */
1071 if (EQ
(TOOLPROP
(TOOL_BAR_ITEM_TYPE
), Qt
))
1073 /* Skip separators. Newer OSX don't show them, and on GNUstep they
1074 are wide as a button, thus overflowing the toolbar most of
1079 /* If image is a vector, choose the image according to the
1081 image = TOOLPROP
(TOOL_BAR_ITEM_IMAGES
);
1082 if (VECTORP
(image))
1084 /* NS toolbar auto-computes disabled and selected images */
1085 idx
= TOOL_BAR_IMAGE_ENABLED_SELECTED
;
1086 eassert
(ASIZE
(image) >= idx
);
1087 image = AREF
(image, idx
);
1093 helpObj
= TOOLPROP
(TOOL_BAR_ITEM_HELP
);
1095 helpObj
= TOOLPROP
(TOOL_BAR_ITEM_CAPTION
);
1096 helpText
= NILP
(helpObj
) ? ""
: SSDATA
(helpObj
);
1098 /* Ignore invalid image specifications. */
1099 if (!valid_image_p
(image))
1101 /* Don't log anything, GNUS makes invalid images all the time. */
1105 img_id
= lookup_image
(f
, image);
1106 img
= IMAGE_FROM_ID
(f
, img_id
);
1107 prepare_image_for_display
(f
, img
);
1109 if (img
->load_failed_p || img
->pixmap
== nil
)
1111 NSLog
(@"Could not prepare toolbar
image for display."
);
1115 [toolbar addDisplayItemWithImage
: img
->pixmap
1119 enabled
: enabled_p
];
1123 if (![toolbar isVisible
])
1124 [toolbar setVisible
: YES
];
1126 #ifdef NS_IMPL_COCOA
1127 if ([toolbar changed
])
1129 /* inform app that toolbar has changed */
1130 NSDictionary
*dict
= [toolbar configurationDictionary
];
1131 NSMutableDictionary
*newDict
= [dict mutableCopy
];
1132 NSEnumerator
*keys
= [[dict allKeys
] objectEnumerator
];
1134 while ((key
= [keys nextObject
]) != nil
)
1136 NSObject
*val
= [dict objectForKey
: key
];
1137 if ([val isKindOfClass
: [NSArray
class]])
1140 [toolbar toolbarDefaultItemIdentifiers
: toolbar
]
1145 [toolbar setConfigurationFromDictionary
: newDict
];
1150 FRAME_TOOLBAR_HEIGHT
(f
) =
1151 NSHeight
([window frameRectForContentRect
: NSMakeRect
(0, 0, 0, 0)])
1152 - FRAME_NS_TITLEBAR_HEIGHT
(f
);
1153 if (FRAME_TOOLBAR_HEIGHT
(f
) < 0) // happens
if frame is fullscreen.
1154 FRAME_TOOLBAR_HEIGHT
(f
) = 0;
1156 if (view->wait_for_tool_bar
&& FRAME_TOOLBAR_HEIGHT
(f
) > 0)
1157 [view setNeedsDisplay
: YES
];
1163 /* ==========================================================================
1165 Toolbar: class implementation
1167 ========================================================================== */
1169 @implementation EmacsToolbar
1171 - initForView
: (EmacsView
*)view withIdentifier
: (NSString
*)identifier
1173 self
= [super initWithIdentifier
: identifier
];
1175 [self setDisplayMode
: NSToolbarDisplayModeIconOnly
];
1176 [self setSizeMode
: NSToolbarSizeModeSmall
];
1177 [self setDelegate
: self
];
1178 identifierToItem
= [[NSMutableDictionary alloc
] initWithCapacity
: 10];
1179 activeIdentifiers
= [[NSMutableArray alloc
] initWithCapacity
: 8];
1180 prevIdentifiers
= nil
;
1181 prevEnablement
= enablement
= 0L;
1187 [prevIdentifiers release
];
1188 [activeIdentifiers release
];
1189 [identifierToItem release
];
1193 - (void
) clearActive
1195 [prevIdentifiers release
];
1196 prevIdentifiers
= [activeIdentifiers copy
];
1197 [activeIdentifiers removeAllObjects
];
1198 prevEnablement
= enablement
;
1205 while ([[self items
] count
] > 0)
1206 [self removeItemAtIndex
: 0];
1211 return [activeIdentifiers isEqualToArray
: prevIdentifiers
] &&
1212 enablement
== prevEnablement ? NO
: YES
;
1215 - (void
) addDisplayItemWithImage
: (EmacsImage
*)img
1218 helpText
: (const
char *)help
1219 enabled
: (BOOL
)enabled
1221 /* 1) come up w/identifier */
1222 NSString
*identifier
1223 = [NSString stringWithFormat
: @"
%lu", (unsigned long)[img hash]];
1224 [activeIdentifiers addObject
: identifier
];
1226 /* 2) create / reuse item */
1227 NSToolbarItem
*item
= [identifierToItem objectForKey
: identifier
];
1230 item
= [[[NSToolbarItem alloc
] initWithItemIdentifier
: identifier
]
1232 [item setImage
: img
];
1233 [item setToolTip
: [NSString stringWithUTF8String
: help]];
1234 [item setTarget
: emacsView
];
1235 [item setAction
: @selector
(toolbarClicked
:)];
1236 [identifierToItem setObject
: item forKey
: identifier
];
1239 #ifdef NS_IMPL_GNUSTEP
1240 [self insertItemWithItemIdentifier
: identifier atIndex
: idx
];
1244 [item setEnabled
: enabled
];
1246 /* 3) update state */
1247 enablement
= (enablement
<< 1) |
(enabled
== YES
);
1250 /* This overrides super's implementation, which automatically sets
1251 all items to enabled state (for some reason). */
1252 - (void
)validateVisibleItems
1257 /* delegate methods */
1259 - (NSToolbarItem
*)toolbar
: (NSToolbar
*)toolbar
1260 itemForItemIdentifier
: (NSString
*)itemIdentifier
1261 willBeInsertedIntoToolbar
: (BOOL
)flag
1263 /* look up NSToolbarItem by identifier and return... */
1264 return [identifierToItem objectForKey
: itemIdentifier
];
1267 - (NSArray
*)toolbarDefaultItemIdentifiers
: (NSToolbar
*)toolbar
1269 /* return entire set.. */
1270 return activeIdentifiers
;
1273 /* for configuration palette (not yet supported) */
1274 - (NSArray
*)toolbarAllowedItemIdentifiers
: (NSToolbar
*)toolbar
1276 /* return entire set... */
1277 return activeIdentifiers
;
1278 //return [identifierToItem allKeys
];
1281 /* optional and unneeded */
1282 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1283 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1284 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1286 @
end /* EmacsToolbar */
1290 /* ==========================================================================
1292 Tooltip: class implementation
1294 ========================================================================== */
1296 /* Needed because NeXTstep does not provide enough control over tooltip
1298 @implementation EmacsTooltip
1302 NSColor
*col
= [NSColor colorWithCalibratedRed
: 1.0 green
: 1.0
1303 blue
: 0.792 alpha
: 0.95];
1304 NSFont
*font
= [NSFont toolTipsFontOfSize
: 0];
1305 NSFont
*sfont
= [font screenFont
];
1306 int height
= [sfont ascender
] - [sfont descender
];
1307 /*[font boundingRectForFont].size.height; */
1308 NSRect r
= NSMakeRect
(0, 0, 100, height
+6);
1310 textField
= [[NSTextField alloc
] initWithFrame
: r
];
1311 [textField setFont
: font
];
1312 [textField setBackgroundColor
: col
];
1314 [textField setEditable
: NO
];
1315 [textField setSelectable
: NO
];
1316 [textField setBordered
: NO
];
1317 [textField setBezeled
: NO
];
1318 [textField setDrawsBackground
: YES
];
1320 win
= [[NSWindow alloc
]
1321 initWithContentRect
: [textField frame
]
1323 backing
: NSBackingStoreBuffered
1325 [win setHasShadow
: YES
];
1326 [win setReleasedWhenClosed
: NO
];
1327 [win setDelegate
: self
];
1328 [[win contentView
] addSubview
: textField
];
1329 /* [win setBackgroundColor: col]; */
1330 [win setOpaque
: NO
];
1339 [textField release
];
1343 - (void
) setText
: (char *)text
1345 NSString
*str
= [NSString stringWithUTF8String
: text];
1346 NSRect r
= [textField frame
];
1349 [textField setStringValue
: str
];
1350 tooltipDims
= [[textField
cell] cellSize
];
1352 r.
size.width
= tooltipDims.width
;
1353 r.
size.height
= tooltipDims.height
;
1354 [textField setFrame
: r
];
1357 - (void
) showAtX
: (int
)x Y
: (int
)y
for: (int
)seconds
1359 NSRect wr
= [win frame
];
1361 wr.origin
= NSMakePoint
(x
, y
);
1362 wr.
size = [textField frame
].
size;
1364 [win setFrame
: wr display
: YES
];
1365 [win setLevel
: NSPopUpMenuWindowLevel
];
1366 [win orderFront
: self
];
1368 timer
= [NSTimer scheduledTimerWithTimeInterval
: (float
)seconds target
: self
1369 selector
: @selector
(hide
)
1370 userInfo
: nil repeats
: NO
];
1379 if ([timer isValid
])
1388 return timer
!= nil
;
1393 return [textField frame
];
1396 @
end /* EmacsTooltip */
1400 /* ==========================================================================
1402 Popup Dialog: implementing functions
1404 ========================================================================== */
1408 NSAutoreleasePool
*pool
;
1409 EmacsDialogPanel
*dialog;
1413 pop_down_menu
(void
*arg
)
1415 struct Popdown_data
*unwind_data
= arg
;
1418 if (popup_activated_flag
)
1420 EmacsDialogPanel
*panel
= unwind_data
->dialog;
1421 popup_activated_flag
= 0;
1423 [unwind_data
->pool release
];
1424 [[FRAME_NS_VIEW
(SELECTED_FRAME
()) window
] makeKeyWindow
];
1427 xfree
(unwind_data
);
1433 ns_popup_dialog
(struct frame
*f
, Lisp_Object header
, Lisp_Object contents
)
1436 Lisp_Object window
, tem
, title;
1439 NSAutoreleasePool
*pool
;
1441 NSTRACE
(x
-popup
-dialog);
1443 isQ
= NILP
(header
);
1445 check_window_system
(f
);
1447 p.x
= (int
)f
->left_pos
+ ((int
)FRAME_COLUMN_WIDTH
(f
) * f
->text_cols
)/2;
1448 p.y
= (int
)f
->top_pos
+ (FRAME_LINE_HEIGHT
(f
) * f
->text_lines
)/2;
1450 title = Fcar
(contents
);
1451 CHECK_STRING
(title);
1453 if (NILP
(Fcar
(Fcdr
(contents
))))
1454 /* No buttons specified, add an "Ok" button so users can pop down
1456 contents
= list2
(title, Fcons
(build_string
("Ok"
), Qt
));
1459 pool
= [[NSAutoreleasePool alloc
] init
];
1460 dialog = [[EmacsDialogPanel alloc
] initFromContents
: contents
1464 struct Popdown_data
*unwind_data
= xmalloc
(sizeof
(*unwind_data
));
1466 unwind_data
->pool
= pool
;
1467 unwind_data
->dialog = dialog;
1470 record_unwind_protect_ptr
(pop_down_menu
, unwind_data
);
1471 popup_activated_flag
= 1;
1472 tem
= [dialog runDialogAt
: p
];
1482 /* ==========================================================================
1484 Popup Dialog: class implementation
1486 ========================================================================== */
1488 @interface FlippedView
: NSView
1493 @implementation FlippedView
1500 @implementation EmacsDialogPanel
1503 #define ICONSIZE
64.0
1504 #define TEXTHEIGHT
20.0
1505 #define MINCELLWIDTH
90.0
1507 - initWithContentRect
: (NSRect
)contentRect styleMask
: (NSUInteger
)aStyle
1508 backing
: (NSBackingStoreType
)backingType defer
: (BOOL
)flag
1510 NSSize spacing
= {SPACER
, SPACER
};
1513 NSImageView
*imgView
;
1514 FlippedView
*contentView
;
1517 dialog_return
= Qundefined
;
1518 button_values
= NULL;
1519 area.origin.x
= 3*SPACER
;
1520 area.origin.y
= 2*SPACER
;
1521 area.
size.width
= ICONSIZE
;
1522 area.
size.height
= ICONSIZE
;
1523 img
= [[NSImage imageNamed
: @"NSApplicationIcon"
] copy
];
1524 [img setScalesWhenResized
: YES
];
1525 [img setSize
: NSMakeSize
(ICONSIZE
, ICONSIZE
)];
1526 imgView
= [[NSImageView alloc
] initWithFrame
: area
];
1527 [imgView setImage
: img
];
1528 [imgView setEditable
: NO
];
1530 [imgView autorelease
];
1532 aStyle
= NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask
;
1536 [super initWithContentRect
: contentRect styleMask
: aStyle
1537 backing
: backingType defer
: flag];
1538 contentView
= [[FlippedView alloc
] initWithFrame
: [[self contentView
] frame
]];
1539 [contentView autorelease
];
1541 [self setContentView
: contentView
];
1543 [[self contentView
] setAutoresizesSubviews
: YES
];
1545 [[self contentView
] addSubview
: imgView
];
1546 [self setTitle
: @""
];
1548 area.origin.x
+= ICONSIZE
+2*SPACER
;
1549 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1550 area.
size.width
= 400;
1551 area.
size.height
= TEXTHEIGHT
;
1552 command
= [[[NSTextField alloc
] initWithFrame
: area
] autorelease
];
1553 [[self contentView
] addSubview
: command
];
1554 [command setStringValue
: ns_app_name
];
1555 [command setDrawsBackground
: NO
];
1556 [command setBezeled
: NO
];
1557 [command setSelectable
: NO
];
1558 [command setFont
: [NSFont boldSystemFontOfSize
: 13.0]];
1560 /* area.origin.x = ICONSIZE+2*SPACER;
1561 area.origin.y = TEXTHEIGHT + 2*SPACER;
1562 area.size.width = 400;
1563 area.size.height= 2;
1564 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1565 [[self contentView] addSubview: tem];
1566 [tem setTitlePosition: NSNoTitle];
1567 [tem setAutoresizingMask: NSViewWidthSizable];*/
1569 /* area.origin.x = ICONSIZE+2*SPACER; */
1570 area.origin.y
+= TEXTHEIGHT
+SPACER
;
1571 area.
size.width
= 400;
1572 area.
size.height
= TEXTHEIGHT
;
1573 title = [[[NSTextField alloc
] initWithFrame
: area
] autorelease
];
1574 [[self contentView
] addSubview
: title];
1575 [title setDrawsBackground
: NO
];
1576 [title setBezeled
: NO
];
1577 [title setSelectable
: NO
];
1578 [title setFont
: [NSFont systemFontOfSize
: 11.0]];
1580 cell = [[[NSButtonCell alloc
] initTextCell
: @""
] autorelease
];
1581 [cell setBordered
: NO
];
1582 [cell setEnabled
: NO
];
1583 [cell setCellAttribute
: NSCellIsInsetButton to
: 8];
1584 [cell setBezelStyle
: NSRoundedBezelStyle
];
1586 matrix
= [[NSMatrix alloc
] initWithFrame
: contentRect
1587 mode
: NSHighlightModeMatrix
1590 numberOfColumns
: 1];
1591 [matrix setFrameOrigin
: NSMakePoint
(area.origin.x
,
1592 area.origin.y
+ (TEXTHEIGHT
+3*SPACER
))];
1593 [matrix setIntercellSpacing
: spacing
];
1594 [matrix autorelease
];
1596 [[self contentView
] addSubview
: matrix
];
1597 [self setOneShot
: YES
];
1598 [self setReleasedWhenClosed
: YES
];
1599 [self setHidesOnDeactivate
: YES
];
1604 - (BOOL
)windowShouldClose
: (id
)sender
1606 window_closed
= YES
;
1613 xfree
(button_values
);
1617 - (void
)process_dialog
: (Lisp_Object
) list
1619 Lisp_Object item
, lst
= list
;
1621 int buttons
= 0, btnnr
= 0;
1623 for (; CONSP
(lst
); lst
= XCDR
(lst
))
1631 button_values
= xmalloc
(buttons
* sizeof
*button_values
);
1633 for (; CONSP
(list
); list
= XCDR
(list
))
1638 [self addString
: SSDATA
(item
) row
: row
++];
1640 else if (CONSP
(item
))
1642 button_values
[btnnr
] = XCDR
(item
);
1643 [self addButton
: SSDATA
(XCAR
(item
)) value
: btnnr row
: row
++];
1646 else if (NILP
(item
))
1655 - (void
)addButton
: (char *)str value
: (int
)tag row
: (int
)row
1664 cell = [matrix cellAtRow
: row column
: cols
-1];
1665 [cell setTarget
: self
];
1666 [cell setAction
: @selector
(clicked
: )];
1667 [cell setTitle
: [NSString stringWithUTF8String
: str
]];
1669 [cell setBordered
: YES
];
1670 [cell setEnabled
: YES
];
1674 - (void
)addString
: (char *)str row
: (int
)row
1683 cell = [matrix cellAtRow
: row column
: cols
-1];
1684 [cell setTitle
: [NSString stringWithUTF8String
: str
]];
1685 [cell setBordered
: YES
];
1686 [cell setEnabled
: NO
];
1697 - (void
)clicked
: sender
1699 NSArray
*sellist
= nil
;
1702 sellist
= [sender selectedCells
];
1703 if ([sellist count
] < 1)
1706 seltag
= [[sellist objectAtIndex
: 0] tag
];
1707 dialog_return
= button_values
[seltag
];
1712 - initFromContents
: (Lisp_Object
)contents isQuestion
: (BOOL
)isQ
1717 if (CONSP
(contents
))
1719 head
= Fcar
(contents
);
1720 [self process_dialog
: Fcdr
(contents
)];
1726 [title setStringValue
:
1727 [NSString stringWithUTF8String
: SSDATA
(head
)]];
1728 else if (isQ
== YES
)
1729 [title setStringValue
: @"Question"
];
1731 [title setStringValue
: @"Information"
];
1737 if (cols
== 1 && rows
> 1) /* Never told where to split */
1740 for (i
= 0; i
< rows
/2; i
++)
1742 [matrix putCell
: [matrix cellAtRow
: (rows
+1)/2 column
: 0]
1743 atRow
: i column
: 1];
1744 [matrix removeRow
: (rows
+1)/2];
1750 NSSize csize
= [matrix cellSize
];
1751 if (csize.width
< MINCELLWIDTH
)
1753 csize.width
= MINCELLWIDTH
;
1754 [matrix setCellSize
: csize
];
1755 [matrix sizeToCells
];
1760 [command sizeToFit
];
1764 if (r.
size.width
+r.origin.x
> t.
size.width
+t.origin.x
)
1766 t.origin.x
= r.origin.x
;
1767 t.
size.width
= r.
size.width
;
1769 r
= [command frame
];
1770 if (r.
size.width
+r.origin.x
> t.
size.width
+t.origin.x
)
1772 t.origin.x
= r.origin.x
;
1773 t.
size.width
= r.
size.width
;
1777 s
= [(NSView
*)[self contentView
] frame
];
1778 r.
size.width
+= t.origin.x
+t.
size.width
+2*SPACER
-s.
size.width
;
1779 r.
size.height
+= t.origin.y
+t.
size.height
+SPACER
-s.
size.height
;
1780 [self setFrame
: r display
: NO
];
1788 - (void
)timeout_handler
: (NSTimer
*)timedEntry
1790 NSEvent
*nxev
= [NSEvent otherEventWithType
: NSApplicationDefined
1791 location
: NSMakePoint
(0, 0)
1794 windowNumber
: [[NSApp mainWindow
] windowNumber
]
1795 context
: [NSApp context
]
1801 /* We use sto because stopModal/abortModal out of the main loop does not
1802 seem to work in 10.6. But as we use stop we must send a real event so
1803 the stop is seen and acted upon. */
1805 [NSApp postEvent
: nxev atStart
: NO
];
1808 - (Lisp_Object
)runDialogAt
: (NSPoint
)p
1810 Lisp_Object ret
= Qundefined
;
1812 while (popup_activated_flag
)
1815 struct timespec next_time
= timer_check
();
1817 if (timespec_valid_p
(next_time
))
1819 double time
= timespectod
(next_time
);
1820 tmo
= [NSTimer timerWithTimeInterval
: time
1822 selector
: @selector
(timeout_handler
:)
1825 [[NSRunLoop currentRunLoop
] addTimer
: tmo
1826 forMode
: NSModalPanelRunLoopMode
];
1829 dialog_return
= Qundefined
;
1830 [NSApp runModalForWindow
: self
];
1831 ret
= dialog_return
;
1834 if (tmo
!= nil
) [tmo invalidate
]; /* Cancels timer */
1839 if (EQ
(ret
, Qundefined
) && window_closed
)
1840 /* Make close button pressed equivalent to C-g. */
1841 Fsignal
(Qquit
, Qnil
);
1849 /* ==========================================================================
1853 ========================================================================== */
1855 DEFUN
("ns
-reset-menu"
, Fns_reset_menu
, Sns_reset_menu
, 0, 0, 0,
1856 doc: /* Cause the NS menu to be re-calculated. */)
1859 set_frame_menubar
(SELECTED_FRAME
(), 1, 0);
1864 DEFUN
("
menu-or
-popup
-active
-p"
, Fmenu_or_popup_active_p
, Smenu_or_popup_active_p
, 0, 0, 0,
1865 doc: /* Return t if a menu or popup dialog is active. */)
1868 return popup_activated
() ? Qt
: Qnil
;
1871 /* ==========================================================================
1873 Lisp interface declaration
1875 ========================================================================== */
1878 syms_of_nsmenu
(void
)
1882 #ifndef NS_IMPL_COCOA
1883 /* Don't know how to keep track of this in Next/Open/GNUstep. Always
1884 update menus there. */
1888 Qdebug_on_next_call
= intern_c_string
("
debug-on
-next
-call"
);
1889 staticpro
(&Qdebug_on_next_call
);