1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007-2013 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 ptrdiff_t specpdl_count
= SPECPDL_INDEX
();
178 int previous_menu_items_used
= f
->menu_bar_items_used
;
179 Lisp_Object
*previous_items
180 = alloca
(previous_menu_items_used
* sizeof
*previous_items
);
182 /* lisp preliminaries */
183 buffer
= XWINDOW
(FRAME_SELECTED_WINDOW
(f
))->contents
;
184 specbind
(Qinhibit_quit
, Qt
);
185 specbind
(Qdebug_on_next_call
, Qnil
);
186 record_unwind_save_match_data
();
187 if (NILP
(Voverriding_local_map_menu_flag
))
189 specbind
(Qoverriding_terminal_local_map
, Qnil
);
190 specbind
(Qoverriding_local_map
, Qnil
);
192 set_buffer_internal_1
(XBUFFER
(buffer
));
194 /* TODO: for some reason this is not needed in other terms,
195 but some menu updates call Info-extract-pointer which causes
196 abort-on-error if waiting-for-input. Needs further investigation. */
197 owfi
= waiting_for_input
;
198 waiting_for_input
= 0;
200 /* lucid hook and possible reset */
201 safe_run_hooks
(Qactivate_menubar_hook
);
202 if (! NILP
(Vlucid_menu_bar_dirty_flag
))
203 call0
(Qrecompute_lucid_menubar
);
204 safe_run_hooks
(Qmenu_bar_update_hook
);
205 fset_menu_bar_items
(f
, menu_bar_items
(FRAME_MENU_BAR_ITEMS
(f
)));
207 /* Now ready to go */
208 items
= FRAME_MENU_BAR_ITEMS
(f
);
210 /* Save the frame's previous menu bar contents data */
211 if (previous_menu_items_used
)
212 memcpy
(previous_items
, aref_addr
(f
->menu_bar_vector
, 0),
213 previous_menu_items_used
* sizeof
(Lisp_Object
));
215 /* parse stage 1: extract from lisp */
218 menu_items
= f
->menu_bar_vector
;
219 menu_items_allocated
= VECTORP
(menu_items
) ? ASIZE
(menu_items
) : 0;
220 submenu_start
= alloca
(ASIZE
(items
) * sizeof
*submenu_start
);
221 submenu_end
= alloca
(ASIZE
(items
) * sizeof
*submenu_end
);
222 submenu_n_panes
= alloca
(ASIZE
(items
) * sizeof
*submenu_n_panes
);
223 submenu_top_level_items
= alloca
(ASIZE
(items
)
224 * sizeof
*submenu_top_level_items
);
226 for (i
= 0; i
< ASIZE
(items
); i
+= 4)
228 Lisp_Object key
, string
, maps
;
230 key
= AREF
(items
, i
);
231 string
= AREF
(items
, i
+ 1);
232 maps
= AREF
(items
, i
+ 2);
236 /* FIXME: we'd like to only parse the needed submenu, but this
237 was causing crashes in the _common parsing code.. need to make
238 sure proper initialization done.. */
239 /* if (submenu && strcmp ([[submenu title] UTF8String], SSDATA (string)))
242 submenu_start
[i
] = menu_items_used
;
244 menu_items_n_panes
= 0;
245 submenu_top_level_items
[i
] = parse_single_submenu
(key
, string
, maps
);
246 submenu_n_panes
[i
] = menu_items_n_panes
;
247 submenu_end
[i
] = menu_items_used
;
251 finish_menu_items
();
252 waiting_for_input
= owfi
;
255 if (submenu
&& n
== 0)
257 /* should have found a menu for this one but didn't */
258 fprintf (stderr
, "
ERROR: did not
find lisp
menu for submenu
'%s'.
\n"
,
259 [[submenu
title] UTF8String
]);
260 discard_menu_items
();
261 unbind_to
(specpdl_count
, Qnil
);
267 /* parse stage 2: insert into lucid 'widget_value' structures
268 [comments in other terms say not to evaluate lisp code here] */
269 wv
= xmalloc_widget_value
();
270 wv
->name
= "menubar"
;
273 wv
->button_type
= BUTTON_TYPE_NONE
;
277 for (i
= 0; i
< 4*n
; i
+= 4)
279 menu_items_n_panes
= submenu_n_panes
[i
];
280 wv
= digest_single_submenu
(submenu_start
[i
], submenu_end
[i
],
281 submenu_top_level_items
[i
]);
285 first_wv
->contents
= wv
;
286 /* Don't set wv->name here; GC during the loop might relocate it. */
288 wv
->button_type
= BUTTON_TYPE_NONE
;
292 set_buffer_internal_1
(prev
);
294 /* Compare the new menu items with previous, and leave off if no change */
295 /* FIXME: following other terms here, but seems like this should be
296 done before parse stage 2 above, since its results aren't used */
297 if (previous_menu_items_used
298 && (!submenu ||
(submenu
&& submenu
== last_submenu
))
299 && menu_items_used
== previous_menu_items_used
)
301 for (i
= 0; i
< previous_menu_items_used
; i
++)
302 /* FIXME: this ALWAYS fails on Buffers menu items.. something
303 about their strings causes them to change every time, so we
304 double-check failures */
305 if (!EQ
(previous_items
[i
], AREF
(menu_items
, i
)))
306 if (!(STRINGP
(previous_items
[i
])
307 && STRINGP
(AREF
(menu_items
, i
))
308 && !strcmp (SSDATA
(previous_items
[i
]),
309 SSDATA
(AREF
(menu_items
, i
)))))
311 if (i
== previous_menu_items_used
)
317 t
+= 1000*tb.time
+tb.millitm
;
318 fprintf (stderr
, "NO CHANGE
! CUTTING OUT after
%ld msec.\n", t);
321 free_menubar_widget_value_tree
(first_wv
);
322 discard_menu_items
();
323 unbind_to
(specpdl_count
, Qnil
);
329 /* The menu items are different, so store them in the frame */
330 /* FIXME: this is not correct for single-submenu case */
331 fset_menu_bar_vector
(f
, menu_items
);
332 f
->menu_bar_items_used
= menu_items_used
;
334 /* Calls restore_menu_items, etc., as they were outside */
335 unbind_to
(specpdl_count
, Qnil
);
337 /* Parse stage 2a: now GC cannot happen during the lifetime of the
338 widget_value, so it's safe to store data from a Lisp_String */
339 wv
= first_wv
->contents
;
340 for (i
= 0; i
< ASIZE
(items
); i
+= 4)
343 string
= AREF
(items
, i
+ 1);
347 wv
->name
= SSDATA
(string
);
348 update_submenu_strings
(wv
->contents
);
352 /* Now, update the NS menu; if we have a submenu, use that, otherwise
353 create a new menu for each sub and fill it. */
356 const
char *submenuTitle
= [[submenu
title] UTF8String
];
357 for (wv
= first_wv
->contents
; wv
; wv
= wv
->next
)
359 if (!strcmp (submenuTitle
, wv
->name
))
361 [submenu fillWithWidgetValue
: wv
->contents
];
362 last_submenu
= submenu
;
369 [menu fillWithWidgetValue
: first_wv
->contents frame
: f
];
375 static int n_previous_strings
= 0;
376 static
char previous_strings
[100][10];
377 static
struct frame
*last_f
= NULL;
381 wv
= xmalloc_widget_value
();
382 wv
->name
= "menubar"
;
385 wv
->button_type
= BUTTON_TYPE_NONE
;
389 /* Make widget-value tree w/ just the top level menu bar strings */
390 items
= FRAME_MENU_BAR_ITEMS
(f
);
393 free_menubar_widget_value_tree
(first_wv
);
400 /* check if no change.. this mechanism is a bit rough, but ready */
401 n
= ASIZE
(items
) / 4;
402 if (f
== last_f
&& n_previous_strings
== n
)
404 for (i
= 0; i
<n
; i
++)
406 string
= AREF
(items
, 4*i
+1);
408 if (EQ
(string
, make_number
(0))) // FIXME
: Why???
--Stef
412 if (previous_strings
[i
][0])
417 else if (memcmp
(previous_strings
[i
], SDATA
(string
),
418 min (10, SBYTES
(string
) + 1)))
424 free_menubar_widget_value_tree
(first_wv
);
432 for (i
= 0; i
< ASIZE
(items
); i
+= 4)
434 string
= AREF
(items
, i
+ 1);
439 memcpy
(previous_strings
[i
/4], SDATA
(string
),
440 min (10, SBYTES
(string
) + 1));
442 wv
= xmalloc_widget_value
();
443 wv
->name
= SSDATA
(string
);
446 wv
->button_type
= BUTTON_TYPE_NONE
;
448 wv
->call_data
= (void
*) (intptr_t
) (-1);
451 /* we'll update the real copy under app menu when time comes */
452 if (!strcmp ("Services"
, wv
->name
))
454 /* but we need to make sure it will update on demand */
455 [svcsMenu setFrame
: f
];
459 [menu addSubmenuWithTitle
: wv
->name forFrame
: f
];
464 first_wv
->contents
= wv
;
470 n_previous_strings
= n
;
472 n_previous_strings
= 0;
475 free_menubar_widget_value_tree
(first_wv
);
480 t
+= 1000*tb.time
+tb.millitm
;
481 fprintf (stderr
, "
Menu update took
%ld msec.\n", t);
486 [NSApp setMainMenu
: menu];
494 /* Main emacs core entry point for menubar menus: called to indicate that the
495 frame's menus have changed, and the *step representation should be updated
498 set_frame_menubar
(struct frame
*f
, bool first_time
, bool deep_p
)
500 ns_update_menubar
(f
, deep_p
, nil
);
504 x_activate_menubar
(struct frame
*f
)
507 ns_update_menubar
(f
, true
, nil
);
508 ns_check_pending_open_menu
();
515 /* ==========================================================================
517 Menu: class implementation
519 ========================================================================== */
522 /* Menu that can define itself from Emacs "widget_value"s and will lazily
523 update itself when user clicked. Based on Carbon/AppKit implementation
524 by Yamamoto Mitsuharu. */
525 @implementation EmacsMenu
527 /* override designated initializer */
528 - 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
566 if (! trackingMenu
) ns_check_menu_open
(nil
);
569 #
if MAC_OS_X_VERSION_MAX_ALLOWED
>= MAC_OS_X_VERSION_10_5
570 - (void
)menuWillOpen
:(NSMenu
*)menu
574 #
if MAC_OS_X_VERSION_MAX_ALLOWED
<= MAC_OS_X_VERSION_10_6
575 // On
10.6 we
get repeated calls
, only the one
for NSSystemDefined is "
real".
576 if ([[NSApp currentEvent
] type] != NSSystemDefined
) return;
579 /* When dragging from one menu to another, we get willOpen followed by didClose,
580 i.e. trackingMenu == 3 in willOpen and then 2 after didClose.
581 We have updated all menus, so avoid doing it when trackingMenu == 3. */
582 if (trackingMenu
== 2)
583 ns_check_menu_open
(menu);
586 - (void
)menuDidClose
:(NSMenu
*)menu
590 #endif
/* OSX >= 10.5 */
592 #endif
/* NS_IMPL_COCOA */
594 /* delegate method called when a submenu is being opened: run a 'deep' call
595 to set_frame_menubar */
596 - (void
)menuNeedsUpdate
: (NSMenu
*)menu
598 if (!FRAME_LIVE_P
(frame
))
601 /* Cocoa/Carbon will request update on every keystroke
602 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
603 since key equivalents are handled through emacs.
604 On Leopard, even keystroke events generate SystemDefined event.
605 Third-party applications that enhance mouse / trackpad
606 interaction, or also VNC/Remote Desktop will send events
607 of type AppDefined rather than SysDefined.
608 Menus will fail to show up if they haven't been initialized.
609 AppDefined events may lack timing data.
611 Thus, we rely on the didBeginTrackingNotification notification
612 as above to indicate the need for updates.
613 From 10.6 on, we could also use -[NSMenu propertiesToUpdate]: In the
614 key press case, NSMenuPropertyItemImage (e.g.) won't be set.
616 if (trackingMenu
== 0)
618 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
619 #
if (! defined
(NS_IMPL_COCOA
) \
620 || MAC_OS_X_VERSION_MAX_ALLOWED
< MAC_OS_X_VERSION_10_5
)
621 /* Don't know how to do this for anything other than OSX >= 10.5
622 This is wrong, as it might run Lisp code in the event loop. */
623 ns_update_menubar
(frame
, true
, self
);
628 - (BOOL
)performKeyEquivalent
: (NSEvent
*)theEvent
630 if (SELECTED_FRAME
() && FRAME_NS_P
(SELECTED_FRAME
())
631 && FRAME_NS_VIEW
(SELECTED_FRAME
()))
632 [FRAME_NS_VIEW
(SELECTED_FRAME
()) keyDown
: theEvent
];
637 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
638 into an accelerator string. We are only able to display a single character
639 for an accelerator, together with an optional modifier combination. (Under
640 Carbon more control was possible, but in Cocoa multi-char strings passed to
641 NSMenuItem get ignored. For now we try to display a super-single letter
642 combo, and return the others as strings to be appended to the item title.
643 (This is signaled by setting keyEquivModMask to 0 for now.) */
644 -(NSString
*)parseKeyEquiv
: (const
char *)key
646 const
char *tpos
= key
;
647 keyEquivModMask
= NSCommandKeyMask
;
649 if (!key ||
!strlen
(key
))
652 while (*tpos
== ' ' ||
*tpos
== '(')
654 if ((*tpos
== 's') && (*(tpos
+1) == '-'))
656 return [NSString stringWithFormat
: @"
%c", tpos[2]];
658 keyEquivModMask
= 0; /* signal */
659 return [NSString stringWithUTF8String
: tpos
];
663 - (NSMenuItem
*)addItemWithWidgetValue
: (void
*)wvptr
666 widget_value
*wv
= (widget_value
*)wvptr
;
668 if (menu_separator_name_p
(wv
->name
))
670 item
= [NSMenuItem separatorItem
];
671 [self addItem
: item
];
675 NSString
*title, *keyEq
;
676 title = [NSString stringWithUTF8String
: wv
->name
];
678 title = @"
< ?
>"
; /* (get out in the open so we know about it) */
680 keyEq
= [self parseKeyEquiv
: wv
->key
];
682 /* OS X just ignores modifier strings longer than one character */
683 if (keyEquivModMask
== 0)
684 title = [title stringByAppendingFormat
: @"
(%@)", keyEq];
687 item
= [self addItemWithTitle
: (NSString
*)title
688 action
: @selector
(menuDown
:)
689 keyEquivalent
: keyEq
];
690 [item setKeyEquivalentModifierMask
: keyEquivModMask
];
692 [item setEnabled
: wv
->enabled
];
694 /* Draw radio buttons and tickboxes */
695 if (wv
->selected
&& (wv
->button_type
== BUTTON_TYPE_TOGGLE ||
696 wv
->button_type
== BUTTON_TYPE_RADIO
))
697 [item setState
: NSOnState
];
699 [item setState
: NSOffState
];
701 [item setTag
: (NSInteger
)wv
->call_data
];
713 for (n
= [self numberOfItems
]-1; n
>= 0; n
--)
715 NSMenuItem
*item
= [self itemAtIndex
: n
];
716 NSString
*title = [item
title];
717 if (([title length] == 0 /* OSX 10.5 */
718 ||
[ns_app_name isEqualToString
: title] /* from 10.6 on */
719 ||
[@"Apple" isEqualToString
: title]) /* older */
720 && ![item isSeparatorItem
])
722 [self removeItemAtIndex
: n
];
727 - (void
)fillWithWidgetValue
: (void
*)wvptr
729 [self fillWithWidgetValue
: wvptr frame
:nil
];
732 - (void
)fillWithWidgetValue
: (void
*)wvptr frame
: (struct frame
*)f
734 widget_value
*wv
= (widget_value
*)wvptr
;
736 /* clear existing contents */
737 [self setMenuChangedMessagesEnabled
: NO
];
740 /* add new contents */
741 for (; wv
!= NULL; wv
= wv
->next
)
743 NSMenuItem
*item
= [self addItemWithWidgetValue
: wv
];
750 submenu
= [[EmacsMenu alloc
] initWithTitle
: [item
title] frame
:f
];
752 submenu
= [[EmacsMenu alloc
] initWithTitle
: [item
title]];
754 [self setSubmenu
: submenu forItem
: item
];
755 [submenu fillWithWidgetValue
: wv
->contents
];
757 [item setAction
: (SEL
)nil
];
761 [self setMenuChangedMessagesEnabled
: YES
];
762 #ifdef NS_IMPL_GNUSTEP
763 if ([[self window
] isVisible
])
769 /* adds an empty submenu and returns it */
770 - (EmacsMenu
*)addSubmenuWithTitle
: (const
char *)title forFrame
: (struct frame
*)f
772 NSString
*titleStr
= [NSString stringWithUTF8String
: title];
773 NSMenuItem
*item
= [self addItemWithTitle
: titleStr
774 action
: (SEL
)nil
/*@selector (menuDown:) */
776 EmacsMenu
*submenu
= [[EmacsMenu alloc
] initWithTitle
: titleStr frame
: f
];
777 [self setSubmenu
: submenu forItem
: item
];
782 /* run a menu in popup mode */
783 - (Lisp_Object
)runMenuAt
: (NSPoint
)p forFrame
: (struct frame
*)f
784 keymaps
: (bool
)keymaps
786 EmacsView
*view = FRAME_NS_VIEW
(f
);
790 /* p = [view convertPoint:p fromView: nil]; */
791 p.y
= NSHeight
([view frame
]) - p.y
;
792 e
= [[view window
] currentEvent
];
793 event
= [NSEvent mouseEventWithType
: NSRightMouseDown
796 timestamp
: [e timestamp
]
797 windowNumber
: [[view window
] windowNumber
]
799 eventNumber
: 0/*[e eventNumber] */
803 context_menu_value
= -1;
804 [NSMenu popUpContextMenu
: self withEvent
: event forView
: view];
805 retVal
= context_menu_value
;
806 context_menu_value
= 0;
808 ? find_and_return_menu_selection
(f
, keymaps
, (void
*)retVal
)
816 /* ==========================================================================
818 Context Menu: implementing functions
820 ========================================================================== */
823 ns_menu_show
(struct frame
*f
, int x
, int y
, bool for_click
, bool keymaps
,
824 Lisp_Object
title, const
char **error)
829 ptrdiff_t specpdl_count
= SPECPDL_INDEX
();
830 widget_value
*wv
, *first_wv
= 0;
834 /* now parse stage 2 as in ns_update_menubar */
835 wv
= xmalloc_widget_value
();
836 wv
->name
= "contextmenu"
;
839 wv
->button_type
= BUTTON_TYPE_NONE
;
844 /* FIXME: a couple of one-line differences prevent reuse */
845 wv
= digest_single_submenu
(0, menu_items_used
, 0);
848 widget_value
*save_wv
= 0, *prev_wv
= 0;
849 widget_value
**submenu_stack
850 = alloca
(menu_items_used
* sizeof
*submenu_stack
);
851 /* Lisp_Object *subprefix_stack
852 = alloca (menu_items_used * sizeof *subprefix_stack); */
853 int submenu_depth
= 0;
857 /* Loop over all panes and items, filling in the tree. */
859 while (i
< menu_items_used
)
861 if (EQ
(AREF
(menu_items
, i
), Qnil
))
863 submenu_stack
[submenu_depth
++] = save_wv
;
869 else if (EQ
(AREF
(menu_items
, i
), Qlambda
))
872 save_wv
= submenu_stack
[--submenu_depth
];
876 else if (EQ
(AREF
(menu_items
, i
), Qt
)
877 && submenu_depth
!= 0)
878 i
+= MENU_ITEMS_PANE_LENGTH
;
879 /* Ignore a nil in the item list.
880 It's meaningful only for dialog boxes. */
881 else if (EQ
(AREF
(menu_items
, i
), Qquote
))
883 else if (EQ
(AREF
(menu_items
, i
), Qt
))
885 /* Create a new pane. */
886 Lisp_Object pane_name
, prefix
;
887 const
char *pane_string
;
889 pane_name
= AREF
(menu_items
, i
+ MENU_ITEMS_PANE_NAME
);
890 prefix
= AREF
(menu_items
, i
+ MENU_ITEMS_PANE_PREFIX
);
892 #ifndef HAVE_MULTILINGUAL_MENU
893 if (STRINGP
(pane_name
) && STRING_MULTIBYTE
(pane_name
))
895 pane_name
= ENCODE_MENU_STRING
(pane_name
);
896 ASET
(menu_items
, i
+ MENU_ITEMS_PANE_NAME
, pane_name
);
899 pane_string
= (NILP
(pane_name
)
900 ? ""
: SSDATA
(pane_name
));
901 /* If there is just one top-level pane, put all its items directly
902 under the top-level menu. */
903 if (menu_items_n_panes
== 1)
906 /* If the pane has a meaningful name,
907 make the pane a top-level menu item
908 with its items as a submenu beneath it. */
909 if (!keymaps
&& strcmp (pane_string
, ""
))
911 wv
= xmalloc_widget_value
();
915 first_wv
->contents
= wv
;
916 wv
->name
= pane_string
;
917 if (keymaps
&& !NILP
(prefix
))
921 wv
->button_type
= BUTTON_TYPE_NONE
;
932 i
+= MENU_ITEMS_PANE_LENGTH
;
936 /* Create a new item within current pane. */
937 Lisp_Object item_name
, enable
, descrip
, def
, type, selected
, help;
938 item_name
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_NAME
);
939 enable
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_ENABLE
);
940 descrip
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
);
941 def
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_DEFINITION
);
942 type = AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_TYPE
);
943 selected
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_SELECTED
);
944 help = AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_HELP
);
946 #ifndef HAVE_MULTILINGUAL_MENU
947 if (STRINGP
(item_name
) && STRING_MULTIBYTE
(item_name
))
949 item_name
= ENCODE_MENU_STRING
(item_name
);
950 ASET
(menu_items
, i
+ MENU_ITEMS_ITEM_NAME
, item_name
);
953 if (STRINGP
(descrip
) && STRING_MULTIBYTE
(descrip
))
955 descrip
= ENCODE_MENU_STRING
(descrip
);
956 ASET
(menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
, descrip
);
958 #endif
/* not HAVE_MULTILINGUAL_MENU */
960 wv
= xmalloc_widget_value
();
964 save_wv
->contents
= wv
;
965 wv
->name
= SSDATA
(item_name
);
967 wv
->key
= SSDATA
(descrip
);
969 /* If this item has a null value,
970 make the call_data null so that it won't display a box
971 when the mouse is on it. */
972 wv
->call_data
= !NILP
(def
) ? aref_addr
(menu_items
, i
) : 0;
973 wv
->enabled
= !NILP
(enable
);
976 wv
->button_type
= BUTTON_TYPE_NONE
;
977 else if (EQ
(type, QCtoggle
))
978 wv
->button_type
= BUTTON_TYPE_TOGGLE
;
979 else if (EQ
(type, QCradio
))
980 wv
->button_type
= BUTTON_TYPE_RADIO
;
984 wv
->selected
= !NILP
(selected
);
986 if (! STRINGP
(help))
993 i
+= MENU_ITEMS_ITEM_LENGTH
;
1001 widget_value
*wv_title
= xmalloc_widget_value
();
1002 widget_value
*wv_sep
= xmalloc_widget_value
();
1004 /* Maybe replace this separator with a bitmap or owner-draw item
1005 so that it looks better. Having two separators looks odd. */
1006 wv_sep
->name
= "
--"
;
1007 wv_sep
->next
= first_wv
->contents
;
1008 wv_sep
->help = Qnil
;
1010 #ifndef HAVE_MULTILINGUAL_MENU
1011 if (STRING_MULTIBYTE
(title))
1012 title = ENCODE_MENU_STRING
(title);
1015 wv_title
->name
= SSDATA
(title);
1016 wv_title
->enabled
= NO
;
1017 wv_title
->button_type
= BUTTON_TYPE_NONE
;
1018 wv_title
->help = Qnil
;
1019 wv_title
->next
= wv_sep
;
1020 first_wv
->contents
= wv_title
;
1023 pmenu
= [[EmacsMenu alloc
] initWithTitle
:
1024 [NSString stringWithUTF8String
: SSDATA
(title)]];
1025 [pmenu fillWithWidgetValue
: first_wv
->contents
];
1026 free_menubar_widget_value_tree
(first_wv
);
1027 unbind_to
(specpdl_count
, Qnil
);
1029 popup_activated_flag
= 1;
1030 tem
= [pmenu runMenuAt
: p forFrame
: f keymaps
: keymaps
];
1031 popup_activated_flag
= 0;
1032 [[FRAME_NS_VIEW
(SELECTED_FRAME
()) window
] makeKeyWindow
];
1038 /* ==========================================================================
1040 Toolbar: externally-called functions
1042 ========================================================================== */
1045 free_frame_tool_bar
(struct frame
*f
)
1046 /* --------------------------------------------------------------------------
1047 Under NS we just hide the toolbar until it might be needed again.
1048 -------------------------------------------------------------------------- */
1051 [[FRAME_NS_VIEW
(f
) toolbar
] setVisible
: NO
];
1052 FRAME_TOOLBAR_HEIGHT
(f
) = 0;
1057 update_frame_tool_bar
(struct frame
*f
)
1058 /* --------------------------------------------------------------------------
1059 Update toolbar contents
1060 -------------------------------------------------------------------------- */
1063 EmacsView
*view = FRAME_NS_VIEW
(f
);
1064 NSWindow
*window
= [view window
];
1065 EmacsToolbar
*toolbar
= [view toolbar
];
1069 #ifdef NS_IMPL_COCOA
1070 [toolbar clearActive
];
1075 /* update EmacsToolbar as in GtkUtils, build items list */
1076 for (i
= 0; i
< f
->n_tool_bar_items
; ++i
)
1078 #define TOOLPROP
(IDX
) AREF
(f
->tool_bar_items
, \
1079 i
* TOOL_BAR_ITEM_NSLOTS
+ (IDX
))
1081 BOOL enabled_p
= !NILP
(TOOLPROP
(TOOL_BAR_ITEM_ENABLED_P
));
1086 Lisp_Object helpObj
;
1087 const
char *helpText
;
1089 /* Check if this is a separator. */
1090 if (EQ
(TOOLPROP
(TOOL_BAR_ITEM_TYPE
), Qt
))
1092 /* Skip separators. Newer OSX don't show them, and on GNUStep they
1093 are wide as a button, thus overflowing the toolbar most of
1098 /* If image is a vector, choose the image according to the
1100 image = TOOLPROP
(TOOL_BAR_ITEM_IMAGES
);
1101 if (VECTORP
(image))
1103 /* NS toolbar auto-computes disabled and selected images */
1104 idx
= TOOL_BAR_IMAGE_ENABLED_SELECTED
;
1105 eassert
(ASIZE
(image) >= idx
);
1106 image = AREF
(image, idx
);
1112 helpObj
= TOOLPROP
(TOOL_BAR_ITEM_HELP
);
1114 helpObj
= TOOLPROP
(TOOL_BAR_ITEM_CAPTION
);
1115 helpText
= NILP
(helpObj
) ? ""
: SSDATA
(helpObj
);
1117 /* Ignore invalid image specifications. */
1118 if (!valid_image_p
(image))
1120 /* Don't log anything, GNUS makes invalid images all the time. */
1124 img_id
= lookup_image
(f
, image);
1125 img
= IMAGE_FROM_ID
(f
, img_id
);
1126 prepare_image_for_display
(f
, img
);
1128 if (img
->load_failed_p || img
->pixmap
== nil
)
1130 NSLog
(@"Could not prepare toolbar
image for display."
);
1134 [toolbar addDisplayItemWithImage
: img
->pixmap
1138 enabled
: enabled_p
];
1142 if (![toolbar isVisible
])
1143 [toolbar setVisible
: YES
];
1145 #ifdef NS_IMPL_COCOA
1146 if ([toolbar changed
])
1148 /* inform app that toolbar has changed */
1149 NSDictionary
*dict
= [toolbar configurationDictionary
];
1150 NSMutableDictionary
*newDict
= [dict mutableCopy
];
1151 NSEnumerator
*keys
= [[dict allKeys
] objectEnumerator
];
1153 while ((key
= [keys nextObject
]) != nil
)
1155 NSObject
*val
= [dict objectForKey
: key
];
1156 if ([val isKindOfClass
: [NSArray
class]])
1159 [toolbar toolbarDefaultItemIdentifiers
: toolbar
]
1164 [toolbar setConfigurationFromDictionary
: newDict
];
1169 FRAME_TOOLBAR_HEIGHT
(f
) =
1170 NSHeight
([window frameRectForContentRect
: NSMakeRect
(0, 0, 0, 0)])
1171 - FRAME_NS_TITLEBAR_HEIGHT
(f
);
1172 if (FRAME_TOOLBAR_HEIGHT
(f
) < 0) // happens
if frame is fullscreen.
1173 FRAME_TOOLBAR_HEIGHT
(f
) = 0;
1178 /* ==========================================================================
1180 Toolbar: class implementation
1182 ========================================================================== */
1184 @implementation EmacsToolbar
1186 - initForView
: (EmacsView
*)view withIdentifier
: (NSString
*)identifier
1188 self
= [super initWithIdentifier
: identifier
];
1190 [self setDisplayMode
: NSToolbarDisplayModeIconOnly
];
1191 [self setSizeMode
: NSToolbarSizeModeSmall
];
1192 [self setDelegate
: self
];
1193 identifierToItem
= [[NSMutableDictionary alloc
] initWithCapacity
: 10];
1194 activeIdentifiers
= [[NSMutableArray alloc
] initWithCapacity
: 8];
1195 prevIdentifiers
= nil
;
1196 prevEnablement
= enablement
= 0L;
1202 [prevIdentifiers release
];
1203 [activeIdentifiers release
];
1204 [identifierToItem release
];
1208 - (void
) clearActive
1210 [prevIdentifiers release
];
1211 prevIdentifiers
= [activeIdentifiers copy
];
1212 [activeIdentifiers removeAllObjects
];
1213 prevEnablement
= enablement
;
1220 while ([[self items
] count
] > 0)
1221 [self removeItemAtIndex
: 0];
1226 return [activeIdentifiers isEqualToArray
: prevIdentifiers
] &&
1227 enablement
== prevEnablement ? NO
: YES
;
1230 - (void
) addDisplayItemWithImage
: (EmacsImage
*)img
1233 helpText
: (const
char *)help
1234 enabled
: (BOOL
)enabled
1236 /* 1) come up w/identifier */
1237 NSString
*identifier
1238 = [NSString stringWithFormat
: @"
%u", [img hash]];
1239 [activeIdentifiers addObject
: identifier
];
1241 /* 2) create / reuse item */
1242 NSToolbarItem
*item
= [identifierToItem objectForKey
: identifier
];
1245 item
= [[[NSToolbarItem alloc
] initWithItemIdentifier
: identifier
]
1247 [item setImage
: img
];
1248 [item setToolTip
: [NSString stringWithUTF8String
: help]];
1249 [item setTarget
: emacsView
];
1250 [item setAction
: @selector
(toolbarClicked
:)];
1251 [identifierToItem setObject
: item forKey
: identifier
];
1254 #ifdef NS_IMPL_GNUSTEP
1255 [self insertItemWithItemIdentifier
: identifier atIndex
: idx
];
1259 [item setEnabled
: enabled
];
1261 /* 3) update state */
1262 enablement
= (enablement
<< 1) |
(enabled
== YES
);
1265 /* This overrides super's implementation, which automatically sets
1266 all items to enabled state (for some reason). */
1267 - (void
)validateVisibleItems
1272 /* delegate methods */
1274 - (NSToolbarItem
*)toolbar
: (NSToolbar
*)toolbar
1275 itemForItemIdentifier
: (NSString
*)itemIdentifier
1276 willBeInsertedIntoToolbar
: (BOOL
)flag
1278 /* look up NSToolbarItem by identifier and return... */
1279 return [identifierToItem objectForKey
: itemIdentifier
];
1282 - (NSArray
*)toolbarDefaultItemIdentifiers
: (NSToolbar
*)toolbar
1284 /* return entire set.. */
1285 return activeIdentifiers
;
1288 /* for configuration palette (not yet supported) */
1289 - (NSArray
*)toolbarAllowedItemIdentifiers
: (NSToolbar
*)toolbar
1291 /* return entire set... */
1292 return activeIdentifiers
;
1293 //return [identifierToItem allKeys
];
1296 /* optional and unneeded */
1297 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1298 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1299 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1301 @
end /* EmacsToolbar */
1305 /* ==========================================================================
1307 Tooltip: class implementation
1309 ========================================================================== */
1311 /* Needed because NeXTstep does not provide enough control over tooltip
1313 @implementation EmacsTooltip
1317 NSColor
*col
= [NSColor colorWithCalibratedRed
: 1.0 green
: 1.0
1318 blue
: 0.792 alpha
: 0.95];
1319 NSFont
*font
= [NSFont toolTipsFontOfSize
: 0];
1320 NSFont
*sfont
= [font screenFont
];
1321 int height
= [sfont ascender
] - [sfont descender
];
1322 /*[font boundingRectForFont].size.height; */
1323 NSRect r
= NSMakeRect
(0, 0, 100, height
+6);
1325 textField
= [[NSTextField alloc
] initWithFrame
: r
];
1326 [textField setFont
: font
];
1327 [textField setBackgroundColor
: col
];
1329 [textField setEditable
: NO
];
1330 [textField setSelectable
: NO
];
1331 [textField setBordered
: NO
];
1332 [textField setBezeled
: NO
];
1333 [textField setDrawsBackground
: YES
];
1335 win
= [[NSWindow alloc
]
1336 initWithContentRect
: [textField frame
]
1338 backing
: NSBackingStoreBuffered
1340 [win setHasShadow
: YES
];
1341 [win setReleasedWhenClosed
: NO
];
1342 [win setDelegate
: self
];
1343 [[win contentView
] addSubview
: textField
];
1344 /* [win setBackgroundColor: col]; */
1345 [win setOpaque
: NO
];
1354 [textField release
];
1358 - (void
) setText
: (char *)text
1360 NSString
*str
= [NSString stringWithUTF8String
: text];
1361 NSRect r
= [textField frame
];
1364 [textField setStringValue
: str
];
1365 tooltipDims
= [[textField
cell] cellSize
];
1367 r.
size.width
= tooltipDims.width
;
1368 r.
size.height
= tooltipDims.height
;
1369 [textField setFrame
: r
];
1372 - (void
) showAtX
: (int
)x Y
: (int
)y
for: (int
)seconds
1374 NSRect wr
= [win frame
];
1376 wr.origin
= NSMakePoint
(x
, y
);
1377 wr.
size = [textField frame
].
size;
1379 [win setFrame
: wr display
: YES
];
1380 [win setLevel
: NSPopUpMenuWindowLevel
];
1381 [win orderFront
: self
];
1383 timer
= [NSTimer scheduledTimerWithTimeInterval
: (float
)seconds target
: self
1384 selector
: @selector
(hide
)
1385 userInfo
: nil repeats
: NO
];
1394 if ([timer isValid
])
1403 return timer
!= nil
;
1408 return [textField frame
];
1411 @
end /* EmacsTooltip */
1415 /* ==========================================================================
1417 Popup Dialog: implementing functions
1419 ========================================================================== */
1423 NSAutoreleasePool
*pool
;
1424 EmacsDialogPanel
*dialog;
1428 pop_down_menu
(void
*arg
)
1430 struct Popdown_data
*unwind_data
= arg
;
1433 if (popup_activated_flag
)
1435 EmacsDialogPanel
*panel
= unwind_data
->dialog;
1436 popup_activated_flag
= 0;
1438 [unwind_data
->pool release
];
1439 [[FRAME_NS_VIEW
(SELECTED_FRAME
()) window
] makeKeyWindow
];
1442 xfree
(unwind_data
);
1448 ns_popup_dialog
(Lisp_Object position
, Lisp_Object contents
, Lisp_Object header
)
1451 Lisp_Object window
, tem
, title;
1455 NSAutoreleasePool
*pool
;
1457 NSTRACE
(x
-popup
-dialog);
1459 isQ
= NILP
(header
);
1461 if (EQ
(position
, Qt
)
1462 ||
(CONSP
(position
) && (EQ
(XCAR
(position
), Qmenu_bar
)
1463 || EQ
(XCAR
(position
), Qtool_bar
))))
1465 window
= selected_window
;
1467 else if (CONSP
(position
))
1470 tem
= Fcar
(position
);
1471 if (XTYPE
(tem
) == Lisp_Cons
)
1472 window
= Fcar
(Fcdr
(position
));
1475 tem
= Fcar
(Fcdr
(position
)); /* EVENT_START (position) */
1476 window
= Fcar
(tem
); /* POSN_WINDOW (tem) */
1479 else if (WINDOWP
(position
) || FRAMEP
(position
))
1486 if (FRAMEP
(window
))
1487 f
= XFRAME
(window
);
1488 else if (WINDOWP
(window
))
1490 CHECK_LIVE_WINDOW
(window
);
1491 f
= XFRAME
(WINDOW_FRAME
(XWINDOW
(window
)));
1494 CHECK_WINDOW
(window
);
1496 check_window_system
(f
);
1498 p.x
= (int
)f
->left_pos
+ ((int
)FRAME_COLUMN_WIDTH
(f
) * f
->text_cols
)/2;
1499 p.y
= (int
)f
->top_pos
+ (FRAME_LINE_HEIGHT
(f
) * f
->text_lines
)/2;
1501 title = Fcar
(contents
);
1502 CHECK_STRING
(title);
1504 if (NILP
(Fcar
(Fcdr
(contents
))))
1505 /* No buttons specified, add an "Ok" button so users can pop down
1507 contents
= list2
(title, Fcons
(build_string
("Ok"
), Qt
));
1510 pool
= [[NSAutoreleasePool alloc
] init
];
1511 dialog = [[EmacsDialogPanel alloc
] initFromContents
: contents
1515 ptrdiff_t specpdl_count
= SPECPDL_INDEX
();
1516 struct Popdown_data
*unwind_data
= xmalloc
(sizeof
(*unwind_data
));
1518 unwind_data
->pool
= pool
;
1519 unwind_data
->dialog = dialog;
1521 record_unwind_protect_ptr
(pop_down_menu
, unwind_data
);
1522 popup_activated_flag
= 1;
1523 tem
= [dialog runDialogAt
: p
];
1524 unbind_to
(specpdl_count
, Qnil
); /* calls pop_down_menu */
1533 /* ==========================================================================
1535 Popup Dialog: class implementation
1537 ========================================================================== */
1539 @interface FlippedView
: NSView
1544 @implementation FlippedView
1551 @implementation EmacsDialogPanel
1554 #define ICONSIZE
64.0
1555 #define TEXTHEIGHT
20.0
1556 #define MINCELLWIDTH
90.0
1558 - initWithContentRect
: (NSRect
)contentRect styleMask
: (NSUInteger
)aStyle
1559 backing
: (NSBackingStoreType
)backingType defer
: (BOOL
)flag
1561 NSSize spacing
= {SPACER
, SPACER
};
1564 NSImageView
*imgView
;
1565 FlippedView
*contentView
;
1568 dialog_return
= Qundefined
;
1569 button_values
= NULL;
1570 area.origin.x
= 3*SPACER
;
1571 area.origin.y
= 2*SPACER
;
1572 area.
size.width
= ICONSIZE
;
1573 area.
size.height
= ICONSIZE
;
1574 img
= [[NSImage imageNamed
: @"NSApplicationIcon"
] copy
];
1575 [img setScalesWhenResized
: YES
];
1576 [img setSize
: NSMakeSize
(ICONSIZE
, ICONSIZE
)];
1577 imgView
= [[NSImageView alloc
] initWithFrame
: area
];
1578 [imgView setImage
: img
];
1579 [imgView setEditable
: NO
];
1581 [imgView autorelease
];
1583 aStyle
= NSTitledWindowMask|NSClosableWindowMask|NSUtilityWindowMask
;
1587 [super initWithContentRect
: contentRect styleMask
: aStyle
1588 backing
: backingType defer
: flag];
1589 contentView
= [[FlippedView alloc
] initWithFrame
: [[self contentView
] frame
]];
1590 [contentView autorelease
];
1592 [self setContentView
: contentView
];
1594 [[self contentView
] setAutoresizesSubviews
: YES
];
1596 [[self contentView
] addSubview
: imgView
];
1597 [self setTitle
: @""
];
1599 area.origin.x
+= ICONSIZE
+2*SPACER
;
1600 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1601 area.
size.width
= 400;
1602 area.
size.height
= TEXTHEIGHT
;
1603 command
= [[[NSTextField alloc
] initWithFrame
: area
] autorelease
];
1604 [[self contentView
] addSubview
: command
];
1605 [command setStringValue
: ns_app_name
];
1606 [command setDrawsBackground
: NO
];
1607 [command setBezeled
: NO
];
1608 [command setSelectable
: NO
];
1609 [command setFont
: [NSFont boldSystemFontOfSize
: 13.0]];
1611 /* area.origin.x = ICONSIZE+2*SPACER;
1612 area.origin.y = TEXTHEIGHT + 2*SPACER;
1613 area.size.width = 400;
1614 area.size.height= 2;
1615 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1616 [[self contentView] addSubview: tem];
1617 [tem setTitlePosition: NSNoTitle];
1618 [tem setAutoresizingMask: NSViewWidthSizable];*/
1620 /* area.origin.x = ICONSIZE+2*SPACER; */
1621 area.origin.y
+= TEXTHEIGHT
+SPACER
;
1622 area.
size.width
= 400;
1623 area.
size.height
= TEXTHEIGHT
;
1624 title = [[[NSTextField alloc
] initWithFrame
: area
] autorelease
];
1625 [[self contentView
] addSubview
: title];
1626 [title setDrawsBackground
: NO
];
1627 [title setBezeled
: NO
];
1628 [title setSelectable
: NO
];
1629 [title setFont
: [NSFont systemFontOfSize
: 11.0]];
1631 cell = [[[NSButtonCell alloc
] initTextCell
: @""
] autorelease
];
1632 [cell setBordered
: NO
];
1633 [cell setEnabled
: NO
];
1634 [cell setCellAttribute
: NSCellIsInsetButton to
: 8];
1635 [cell setBezelStyle
: NSRoundedBezelStyle
];
1637 matrix
= [[NSMatrix alloc
] initWithFrame
: contentRect
1638 mode
: NSHighlightModeMatrix
1641 numberOfColumns
: 1];
1642 [matrix setFrameOrigin
: NSMakePoint
(area.origin.x
,
1643 area.origin.y
+ (TEXTHEIGHT
+3*SPACER
))];
1644 [matrix setIntercellSpacing
: spacing
];
1645 [matrix autorelease
];
1647 [[self contentView
] addSubview
: matrix
];
1648 [self setOneShot
: YES
];
1649 [self setReleasedWhenClosed
: YES
];
1650 [self setHidesOnDeactivate
: YES
];
1655 - (BOOL
)windowShouldClose
: (id
)sender
1657 window_closed
= YES
;
1664 xfree
(button_values
);
1668 - (void
)process_dialog
: (Lisp_Object
) list
1670 Lisp_Object item
, lst
= list
;
1672 int buttons
= 0, btnnr
= 0;
1674 for (; XTYPE
(lst
) == Lisp_Cons
; lst
= XCDR
(lst
))
1677 if (XTYPE
(item
) == Lisp_Cons
)
1682 button_values
= xmalloc
(buttons
* sizeof
*button_values
);
1684 for (; XTYPE
(list
) == Lisp_Cons
; list
= XCDR
(list
))
1687 if (XTYPE
(item
) == Lisp_String
)
1689 [self addString
: SSDATA
(item
) row
: row
++];
1691 else if (XTYPE
(item
) == Lisp_Cons
)
1693 button_values
[btnnr
] = XCDR
(item
);
1694 [self addButton
: SSDATA
(XCAR
(item
)) value
: btnnr row
: row
++];
1697 else if (NILP
(item
))
1706 - (void
)addButton
: (char *)str value
: (int
)tag row
: (int
)row
1715 cell = [matrix cellAtRow
: row column
: cols
-1];
1716 [cell setTarget
: self
];
1717 [cell setAction
: @selector
(clicked
: )];
1718 [cell setTitle
: [NSString stringWithUTF8String
: str
]];
1720 [cell setBordered
: YES
];
1721 [cell setEnabled
: YES
];
1725 - (void
)addString
: (char *)str row
: (int
)row
1734 cell = [matrix cellAtRow
: row column
: cols
-1];
1735 [cell setTitle
: [NSString stringWithUTF8String
: str
]];
1736 [cell setBordered
: YES
];
1737 [cell setEnabled
: NO
];
1748 - (void
)clicked
: sender
1750 NSArray
*sellist
= nil
;
1753 sellist
= [sender selectedCells
];
1754 if ([sellist count
] < 1)
1757 seltag
= [[sellist objectAtIndex
: 0] tag
];
1758 dialog_return
= button_values
[seltag
];
1763 - initFromContents
: (Lisp_Object
)contents isQuestion
: (BOOL
)isQ
1768 if (XTYPE
(contents
) == Lisp_Cons
)
1770 head
= Fcar
(contents
);
1771 [self process_dialog
: Fcdr
(contents
)];
1776 if (XTYPE
(head
) == Lisp_String
)
1777 [title setStringValue
:
1778 [NSString stringWithUTF8String
: SSDATA
(head
)]];
1779 else if (isQ
== YES
)
1780 [title setStringValue
: @"Question"
];
1782 [title setStringValue
: @"Information"
];
1788 if (cols
== 1 && rows
> 1) /* Never told where to split */
1791 for (i
= 0; i
< rows
/2; i
++)
1793 [matrix putCell
: [matrix cellAtRow
: (rows
+1)/2 column
: 0]
1794 atRow
: i column
: 1];
1795 [matrix removeRow
: (rows
+1)/2];
1801 NSSize csize
= [matrix cellSize
];
1802 if (csize.width
< MINCELLWIDTH
)
1804 csize.width
= MINCELLWIDTH
;
1805 [matrix setCellSize
: csize
];
1806 [matrix sizeToCells
];
1811 [command sizeToFit
];
1815 if (r.
size.width
+r.origin.x
> t.
size.width
+t.origin.x
)
1817 t.origin.x
= r.origin.x
;
1818 t.
size.width
= r.
size.width
;
1820 r
= [command frame
];
1821 if (r.
size.width
+r.origin.x
> t.
size.width
+t.origin.x
)
1823 t.origin.x
= r.origin.x
;
1824 t.
size.width
= r.
size.width
;
1828 s
= [(NSView
*)[self contentView
] frame
];
1829 r.
size.width
+= t.origin.x
+t.
size.width
+2*SPACER
-s.
size.width
;
1830 r.
size.height
+= t.origin.y
+t.
size.height
+SPACER
-s.
size.height
;
1831 [self setFrame
: r display
: NO
];
1839 - (void
)timeout_handler
: (NSTimer
*)timedEntry
1841 NSEvent
*nxev
= [NSEvent otherEventWithType
: NSApplicationDefined
1842 location
: NSMakePoint
(0, 0)
1845 windowNumber
: [[NSApp mainWindow
] windowNumber
]
1846 context
: [NSApp context
]
1852 /* We use sto because stopModal/abortModal out of the main loop does not
1853 seem to work in 10.6. But as we use stop we must send a real event so
1854 the stop is seen and acted upon. */
1856 [NSApp postEvent
: nxev atStart
: NO
];
1859 - (Lisp_Object
)runDialogAt
: (NSPoint
)p
1861 Lisp_Object ret
= Qundefined
;
1863 while (popup_activated_flag
)
1866 EMACS_TIME next_time
= timer_check
();
1868 if (EMACS_TIME_VALID_P
(next_time
))
1870 double time
= EMACS_TIME_TO_DOUBLE
(next_time
);
1871 tmo
= [NSTimer timerWithTimeInterval
: time
1873 selector
: @selector
(timeout_handler
:)
1876 [[NSRunLoop currentRunLoop
] addTimer
: tmo
1877 forMode
: NSModalPanelRunLoopMode
];
1880 dialog_return
= Qundefined
;
1881 [NSApp runModalForWindow
: self
];
1882 ret
= dialog_return
;
1885 if (tmo
!= nil
) [tmo invalidate
]; /* Cancels timer */
1890 if (EQ
(ret
, Qundefined
) && window_closed
)
1891 /* Make close button pressed equivalent to C-g. */
1892 Fsignal
(Qquit
, Qnil
);
1900 /* ==========================================================================
1904 ========================================================================== */
1906 DEFUN
("ns
-reset-menu"
, Fns_reset_menu
, Sns_reset_menu
, 0, 0, 0,
1907 doc: /* Cause the NS menu to be re-calculated. */)
1910 set_frame_menubar
(SELECTED_FRAME
(), 1, 0);
1915 DEFUN
("x
-popup
-dialog"
, Fx_popup_dialog
, Sx_popup_dialog
, 2, 3, 0,
1916 doc: /* Pop up a dialog box and return user's selection.
1917 POSITION specifies which frame to use.
1918 This is normally a mouse button event or a window or frame.
1919 If POSITION is t, it means to use the frame the mouse is on.
1920 The dialog box appears in the middle of the specified frame.
1922 CONTENTS specifies the alternatives to display in the dialog box.
1923 It is a list of the form (DIALOG ITEM1 ITEM2...).
1924 Each ITEM is a cons cell (STRING . VALUE).
1925 The return value is VALUE from the chosen item.
1927 An ITEM may also be just a string--that makes a nonselectable item.
1928 An ITEM may also be nil--that means to put all preceding items
1929 on the left of the dialog box and all following items on the right.
1930 \(By default, approximately half appear on each side.)
1932 If HEADER is non-nil, the frame title for the box is "Information",
1933 otherwise it is "Question".
1935 If the user gets rid of the dialog box without making a valid choice,
1936 for instance using the window manager, then this produces a quit and
1937 `x-popup-dialog' does not return. */)
1938 (Lisp_Object position
, Lisp_Object contents
, Lisp_Object header
)
1940 return ns_popup_dialog
(position
, contents
, header
);
1943 DEFUN
("
menu-or
-popup
-active
-p"
, Fmenu_or_popup_active_p
, Smenu_or_popup_active_p
, 0, 0, 0,
1944 doc: /* Return t if a menu or popup dialog is active. */)
1947 return popup_activated
() ? Qt
: Qnil
;
1950 /* ==========================================================================
1952 Lisp interface declaration
1954 ========================================================================== */
1957 syms_of_nsmenu
(void
)
1959 #ifndef NS_IMPL_COCOA
1960 /* Don't know how to keep track of this in Next/Open/Gnustep. Always
1961 update menus there. */
1964 defsubr
(&Sx_popup_dialog
);
1965 defsubr
(&Sns_reset_menu
);
1966 defsubr
(&Smenu_or_popup_active_p
);
1968 Qdebug_on_next_call
= intern_c_string
("
debug-on
-next
-call"
);
1969 staticpro
(&Qdebug_on_next_call
);