1 /* NeXT/Open/GNUstep and MacOSX Cocoa menu and toolbar module.
2 Copyright (C) 2007, 2008, 2009 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. */
34 #include "blockinput.h"
36 #include "termhooks.h"
39 #define NSMENUPROFILE
0
42 #include
<sys
/timeb.h
>
43 #include
<sys
/types.h
>
46 #define MenuStagger
10.0
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 extern Lisp_Object Vmenu_updating_frame
;
67 Lisp_Object Qdebug_on_next_call
;
68 extern Lisp_Object Voverriding_local_map
, Voverriding_local_map_menu_flag
,
69 Qoverriding_local_map
, Qoverriding_terminal_local_map
;
71 extern
long context_menu_value
;
72 EmacsMenu
*mainMenu
, *svcsMenu
, *dockMenu
;
74 /* Nonzero means a menu is currently active. */
75 static int popup_activated_flag
;
76 static NSModalSession popupSession
;
78 /* NOTE: toolbar implementation is at end,
79 following complete menu implementation. */
82 /* ==========================================================================
84 Menu: Externally-called functions
86 ========================================================================== */
89 /* FIXME: not currently used, but should normalize with other terms. */
91 x_activate_menubar
(struct frame
*f
)
93 fprintf (stderr
, "XXX
: Received x_activate_menubar event.
\n"
);
97 /* Supposed to discard menubar and free storage. Since we share the
98 menubar among frames and update its context for the focused window,
99 there is nothing to do here. */
101 free_frame_menubar
(struct frame
*f
)
110 return popup_activated_flag
;
114 /* --------------------------------------------------------------------------
115 Update menubar. Three cases:
116 1) deep_p = 0, submenu = nil: Fresh switch onto a frame -- either set up
117 just top-level menu strings (OS X), or goto case (2) (GNUstep).
118 2) deep_p = 1, submenu = nil: Recompute all submenus.
119 3) deep_p = 1, submenu = non-nil: Update contents of a single submenu.
120 -------------------------------------------------------------------------- */
122 ns_update_menubar
(struct frame
*f
, int deep_p
, EmacsMenu
*submenu
)
124 NSAutoreleasePool
*pool
;
125 id
menu = [NSApp mainMenu
];
126 static EmacsMenu
*last_submenu
= nil
;
128 const
char *submenuTitle
= [[submenu
title] UTF8String
];
129 extern int waiting_for_input
;
132 widget_value
*wv
, *first_wv
, *prev_wv
= 0;
140 NSTRACE
(set_frame_menubar
);
142 if (f
!= SELECTED_FRAME
())
144 XSETFRAME
(Vmenu_updating_frame
, f
);
145 /*fprintf (stderr, "ns_update_menubar: frame: %p\tdeep: %d\tsub: %p\n", f, deep_p, submenu); */
148 pool
= [[NSAutoreleasePool alloc
] init
];
150 /* Menu may have been created automatically; if so, discard it. */
151 if ([menu isKindOfClass
: [EmacsMenu
class]] == NO
)
159 menu = [[EmacsMenu alloc
] initWithTitle
: @"Emacs"
];
163 { /* close up anything on there */
164 id attMenu
= [menu attachedMenu
];
171 t
= -(1000*tb.time
+tb.millitm
);
174 /* widget_value is a straightforward object translation of emacs's
175 Byzantine lisp menu structures */
176 wv
= xmalloc_widget_value
();
180 wv
->button_type
= BUTTON_TYPE_NONE
;
184 #ifdef NS_IMPL_GNUSTEP
185 deep_p
= 1; /* until GNUstep NSMenu implements the Panther delegation model */
190 /* Fully parse one or more of the submenus. */
192 int
*submenu_start
, *submenu_end
;
193 int
*submenu_top_level_items
, *submenu_n_panes
;
194 struct buffer
*prev
= current_buffer
;
196 int specpdl_count
= SPECPDL_INDEX
();
197 int previous_menu_items_used
= f
->menu_bar_items_used
;
198 Lisp_Object
*previous_items
199 = (Lisp_Object
*) alloca
(previous_menu_items_used
200 * sizeof
(Lisp_Object
));
202 /* lisp preliminaries */
203 buffer
= XWINDOW
(FRAME_SELECTED_WINDOW
(f
))->buffer
;
204 specbind
(Qinhibit_quit
, Qt
);
205 specbind
(Qdebug_on_next_call
, Qnil
);
206 record_unwind_save_match_data
();
207 if (NILP
(Voverriding_local_map_menu_flag
))
209 specbind
(Qoverriding_terminal_local_map
, Qnil
);
210 specbind
(Qoverriding_local_map
, Qnil
);
212 set_buffer_internal_1
(XBUFFER
(buffer
));
214 /* TODO: for some reason this is not needed in other terms,
215 but some menu updates call Info-extract-pointer which causes
216 abort-on-error if waiting-for-input. Needs further investigation. */
217 owfi
= waiting_for_input
;
218 waiting_for_input
= 0;
220 /* lucid hook and possible reset */
221 safe_run_hooks
(Qactivate_menubar_hook
);
222 if (! NILP
(Vlucid_menu_bar_dirty_flag
))
223 call0
(Qrecompute_lucid_menubar
);
224 safe_run_hooks
(Qmenu_bar_update_hook
);
225 FRAME_MENU_BAR_ITEMS
(f
) = menu_bar_items
(FRAME_MENU_BAR_ITEMS
(f
));
227 /* Now ready to go */
228 items
= FRAME_MENU_BAR_ITEMS
(f
);
230 /* Save the frame's previous menu bar contents data */
231 if (previous_menu_items_used
)
232 bcopy
(XVECTOR
(f
->menu_bar_vector
)->contents
, previous_items
,
233 previous_menu_items_used
* sizeof
(Lisp_Object
));
235 /* parse stage 1: extract from lisp */
238 menu_items
= f
->menu_bar_vector
;
239 menu_items_allocated
= VECTORP
(menu_items
) ? ASIZE
(menu_items
) : 0;
240 submenu_start
= (int
*) alloca
(XVECTOR
(items
)->size * sizeof
(int
*));
241 submenu_end
= (int
*) alloca
(XVECTOR
(items
)->size * sizeof
(int
*));
242 submenu_n_panes
= (int
*) alloca
(XVECTOR
(items
)->size * sizeof
(int
));
243 submenu_top_level_items
244 = (int
*) alloca
(XVECTOR
(items
)->size * sizeof
(int
*));
246 for (i
= 0; i
< XVECTOR
(items
)->size; i
+= 4)
248 Lisp_Object key
, string
, maps
;
250 key
= XVECTOR
(items
)->contents
[i
];
251 string
= XVECTOR
(items
)->contents
[i
+ 1];
252 maps
= XVECTOR
(items
)->contents
[i
+ 2];
256 /* FIXME: we'd like to only parse the needed submenu, but this
257 was causing crashes in the _common parsing code.. need to make
258 sure proper initialization done.. */
259 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
262 submenu_start
[i
] = menu_items_used
;
264 menu_items_n_panes
= 0;
265 submenu_top_level_items
[i
] = parse_single_submenu
(key
, string
, maps
);
266 submenu_n_panes
[i
] = menu_items_n_panes
;
267 submenu_end
[i
] = menu_items_used
;
271 finish_menu_items
();
272 waiting_for_input
= owfi
;
275 if (submenu
&& n
== 0)
277 /* should have found a menu for this one but didn't */
278 fprintf (stderr
, "
ERROR: did not
find lisp
menu for submenu
'%s'.
\n"
,
280 discard_menu_items
();
281 unbind_to
(specpdl_count
, Qnil
);
287 /* parse stage 2: insert into lucid 'widget_value' structures
288 [comments in other terms say not to evaluate lisp code here] */
289 wv
= xmalloc_widget_value
();
290 wv
->name
= "menubar"
;
293 wv
->button_type
= BUTTON_TYPE_NONE
;
297 for (i
= 0; i
< 4*n
; i
+= 4)
299 menu_items_n_panes
= submenu_n_panes
[i
];
300 wv
= digest_single_submenu
(submenu_start
[i
], submenu_end
[i
],
301 submenu_top_level_items
[i
]);
305 first_wv
->contents
= wv
;
306 /* Don't set wv->name here; GC during the loop might relocate it. */
308 wv
->button_type
= BUTTON_TYPE_NONE
;
312 set_buffer_internal_1
(prev
);
314 /* Compare the new menu items with previous, and leave off if no change */
315 /* FIXME: following other terms here, but seems like this should be
316 done before parse stage 2 above, since its results aren't used */
317 if (previous_menu_items_used
318 && (!submenu ||
(submenu
&& submenu
== last_submenu
))
319 && menu_items_used
== previous_menu_items_used
)
321 for (i
= 0; i
< previous_menu_items_used
; i
++)
322 /* FIXME: this ALWAYS fails on Buffers menu items.. something
323 about their strings causes them to change every time, so we
324 double-check failures */
325 if (!EQ
(previous_items
[i
], XVECTOR
(menu_items
)->contents
[i
]))
326 if (!(STRINGP
(previous_items
[i
])
327 && STRINGP
(XVECTOR
(menu_items
)->contents
[i
])
328 && !strcmp (SDATA
(previous_items
[i
]),
329 SDATA
(XVECTOR
(menu_items
)->contents
[i
]))))
331 if (i
== previous_menu_items_used
)
337 t
+= 1000*tb.time
+tb.millitm
;
338 fprintf (stderr
, "NO CHANGE
! CUTTING OUT after
%ld msec.\n", t);
341 free_menubar_widget_value_tree
(first_wv
);
342 discard_menu_items
();
343 unbind_to
(specpdl_count
, Qnil
);
349 /* The menu items are different, so store them in the frame */
350 /* FIXME: this is not correct for single-submenu case */
351 f
->menu_bar_vector
= menu_items
;
352 f
->menu_bar_items_used
= menu_items_used
;
354 /* Calls restore_menu_items, etc., as they were outside */
355 unbind_to
(specpdl_count
, Qnil
);
357 /* Parse stage 2a: now GC cannot happen during the lifetime of the
358 widget_value, so it's safe to store data from a Lisp_String */
359 wv
= first_wv
->contents
;
360 for (i
= 0; i
< XVECTOR
(items
)->size; i
+= 4)
363 string
= XVECTOR
(items
)->contents
[i
+ 1];
366 /* if (submenu && strcmp (submenuTitle, SDATA (string)))
369 wv
->name
= (char *) SDATA
(string
);
370 update_submenu_strings
(wv
->contents
);
374 /* Now, update the NS menu; if we have a submenu, use that, otherwise
375 create a new menu for each sub and fill it. */
378 for (wv
= first_wv
->contents
; wv
; wv
= wv
->next
)
380 if (!strcmp (submenuTitle
, wv
->name
))
382 [submenu fillWithWidgetValue
: wv
->contents
];
383 last_submenu
= submenu
;
390 [menu fillWithWidgetValue
: first_wv
->contents
];
396 static int n_previous_strings
= 0;
397 static
char previous_strings
[100][10];
398 static
struct frame
*last_f
= NULL;
402 /* Make widget-value tree w/ just the top level menu bar strings */
403 items
= FRAME_MENU_BAR_ITEMS
(f
);
412 /* check if no change.. this mechanism is a bit rough, but ready */
413 n
= XVECTOR
(items
)->size / 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
423 if (previous_strings
[i
][0])
427 if (strncmp
(previous_strings
[i
], SDATA
(string
), 10))
440 for (i
= 0; i
< XVECTOR
(items
)->size; i
+= 4)
442 string
= XVECTOR
(items
)->contents
[i
+ 1];
447 strncpy
(previous_strings
[i
/4], SDATA
(string
), 10);
449 wv
= xmalloc_widget_value
();
450 wv
->name
= (char *) SDATA
(string
);
453 wv
->button_type
= BUTTON_TYPE_NONE
;
455 wv
->call_data
= (void
*) (EMACS_INT
) (-1);
458 /* we'll update the real copy under app menu when time comes */
459 if (!strcmp ("Services"
, wv
->name
))
461 /* but we need to make sure it will update on demand */
462 [svcsMenu setFrame
: f
];
463 [svcsMenu setDelegate
: svcsMenu
];
467 [menu addSubmenuWithTitle
: wv
->name forFrame
: f
];
472 first_wv
->contents
= wv
;
478 n_previous_strings
= n
;
480 n_previous_strings
= 0;
483 free_menubar_widget_value_tree
(first_wv
);
488 t
+= 1000*tb.time
+tb.millitm
;
489 fprintf (stderr
, "
Menu update took
%ld msec.\n", t);
494 [NSApp setMainMenu
: menu];
502 /* Main emacs core entry point for menubar menus: called to indicate that the
503 frame's menus have changed, and the *step representation should be updated
506 set_frame_menubar
(struct frame
*f
, int first_time
, int deep_p
)
508 ns_update_menubar
(f
, deep_p
, nil
);
512 /* Utility (from macmenu.c): is this item a separator? */
514 name_is_separator
(name
)
517 const
char *start
= name
;
519 /* Check if name string consists of only dashes ('-'). */
520 while (*name
== '-') name
++;
521 /* Separators can also be of the form "--:TripleSuperMegaEtched"
522 or "--deep-shadow". We don't implement them yet, se we just treat
523 them like normal separators. */
524 return (*name
== '\0' || start
+ 2 == name
);
528 /* ==========================================================================
530 Menu: class implementation
532 ========================================================================== */
535 /* Menu that can define itself from Emacs "widget_value"s and will lazily
536 update itself when user clicked. Based on Carbon/AppKit implementation
537 by Yamamoto Mitsuharu. */
538 @implementation EmacsMenu
540 /* override designated initializer */
541 - initWithTitle
: (NSString
*)title
543 if (self
= [super initWithTitle
: title])
544 [self setAutoenablesItems
: NO
];
549 /* used for top-level */
550 - initWithTitle
: (NSString
*)title frame
: (struct frame
*)f
552 [self initWithTitle
: title];
555 [self setDelegate
: self
];
561 - (void
)setFrame
: (struct frame
*)f
567 /* delegate method called when a submenu is being opened: run a 'deep' call
568 to set_frame_menubar */
569 - (void
)menuNeedsUpdate
: (NSMenu
*)menu
571 NSEvent
*event
= [[FRAME_NS_VIEW
(frame
) window
] currentEvent
];
572 /* HACK: Cocoa/Carbon will request update on every keystroke
573 via IsMenuKeyEvent -> CheckMenusForKeyEvent. These are not needed
574 since key equivalents are handled through emacs.
575 On Leopard, even keystroke events generate SystemDefined events, but
576 their subtype is 8. */
577 if ([event
type] != NSSystemDefined ||
[event subtype
] == 8
578 /* Also, don't try this if from an event picked up asynchronously,
579 as lots of lisp evaluation happens in ns_update_menubar. */
580 || handling_signal
!= 0)
582 /*fprintf (stderr, "Updating menu '%s'\n", [[self title] UTF8String]); NSLog (@"%@\n", event); */
583 ns_update_menubar
(frame
, 1, self
);
587 - (BOOL
)performKeyEquivalent
: (NSEvent
*)theEvent
589 if (SELECTED_FRAME
() && FRAME_NS_P
(SELECTED_FRAME
())
590 && FRAME_NS_VIEW
(SELECTED_FRAME
()))
591 [FRAME_NS_VIEW
(SELECTED_FRAME
()) keyDown
: theEvent
];
596 /* Parse a widget_value's key rep (examples: 's-p', 's-S', '(C-x C-s)', '<f13>')
597 into an accelerator string. We are only able to display a single character
598 for an accelerator, together with an optional modifier combination. (Under
599 Carbon more control was possible, but in Cocoa multi-char strings passed to
600 NSMenuItem get ignored. For now we try to display a super-single letter
601 combo, and return the others as strings to be appended to the item title.
602 (This is signaled by setting keyEquivModMask to 0 for now.) */
603 -(NSString
*)parseKeyEquiv
: (char *)key
606 keyEquivModMask
= NSCommandKeyMask
;
608 if (!key ||
!strlen
(key
))
611 while (*tpos
== ' ' ||
*tpos
== '(')
613 if ((*tpos
== 's') && (*(tpos
+1) == '-'))
615 return [NSString stringWithFormat
: @"
%c", tpos[2]];
617 keyEquivModMask
= 0; /* signal */
618 return [NSString stringWithUTF8String
: tpos
];
622 - (NSMenuItem
*)addItemWithWidgetValue
: (void
*)wvptr
625 widget_value
*wv
= (widget_value
*)wvptr
;
627 if (name_is_separator
(wv
->name
))
629 item
= [NSMenuItem separatorItem
];
630 [self addItem
: item
];
634 NSString
*title, *keyEq
;
635 title = [NSString stringWithUTF8String
: wv
->name
];
637 title = @"
< ?
>"
; /* (get out in the open so we know about it) */
639 keyEq
= [self parseKeyEquiv
: wv
->key
];
641 /* OS X just ignores modifier strings longer than one character */
642 if (keyEquivModMask
== 0)
643 title = [title stringByAppendingFormat
: @"
(%@)", keyEq];
646 item
= [self addItemWithTitle
: (NSString
*)title
647 action
: @selector
(menuDown
:)
648 keyEquivalent
: keyEq
];
649 [item setKeyEquivalentModifierMask
: keyEquivModMask
];
651 [item setEnabled
: wv
->enabled
];
653 /* Draw radio buttons and tickboxes */
654 if (wv
->selected
&& (wv
->button_type
== BUTTON_TYPE_TOGGLE ||
655 wv
->button_type
== BUTTON_TYPE_RADIO
))
656 [item setState
: NSOnState
];
658 [item setState
: NSOffState
];
660 [item setTag
: (int
)wv
->call_data
];
672 for (n
= [self numberOfItems
]-1; n
>= 0; n
--)
674 NSMenuItem
*item
= [self itemAtIndex
: n
];
675 NSString
*title = [item
title];
676 if (([title length] == 0 ||
[@"Apple" isEqualToString
: title])
677 && ![item isSeparatorItem
])
679 [self removeItemAtIndex
: n
];
684 - (void
)fillWithWidgetValue
: (void
*)wvptr
686 widget_value
*wv
= (widget_value
*)wvptr
;
688 /* clear existing contents */
689 [self setMenuChangedMessagesEnabled
: NO
];
692 /* add new contents */
693 for (; wv
!= NULL; wv
= wv
->next
)
695 NSMenuItem
*item
= [self addItemWithWidgetValue
: wv
];
699 EmacsMenu
*submenu
= [[EmacsMenu alloc
] initWithTitle
: [item
title]];
701 [self setSubmenu
: submenu forItem
: item
];
702 [submenu fillWithWidgetValue
: wv
->contents
];
704 [item setAction
: nil
];
708 [self setMenuChangedMessagesEnabled
: YES
];
709 #ifdef NS_IMPL_GNUSTEP
710 if ([[self window
] isVisible
])
713 if ([self supermenu
] == nil
)
719 /* adds an empty submenu and returns it */
720 - (EmacsMenu
*)addSubmenuWithTitle
: (char *)title forFrame
: (struct frame
*)f
722 NSString
*titleStr
= [NSString stringWithUTF8String
: title];
723 NSMenuItem
*item
= [self addItemWithTitle
: titleStr
724 action
: nil
/*@selector (menuDown:) */
726 EmacsMenu
*submenu
= [[EmacsMenu alloc
] initWithTitle
: titleStr frame
: f
];
727 [self setSubmenu
: submenu forItem
: item
];
732 /* run a menu in popup mode */
733 - (Lisp_Object
)runMenuAt
: (NSPoint
)p forFrame
: (struct frame
*)f
734 keymaps
: (int
)keymaps
736 EmacsView
*view = FRAME_NS_VIEW
(f
);
737 /* p = [view convertPoint:p fromView: nil]; */
738 p.y
= NSHeight
([view frame
]) - p.y
;
739 NSEvent
*e
= [[view window
] currentEvent
];
740 NSEvent
*event
= [NSEvent mouseEventWithType
: NSRightMouseDown
743 timestamp
: [e timestamp
]
744 windowNumber
: [[view window
] windowNumber
]
746 eventNumber
: 0/*[e eventNumber] */
751 context_menu_value
= -1;
752 [NSMenu popUpContextMenu
: self withEvent
: event forView
: view];
753 retVal
= context_menu_value
;
754 context_menu_value
= 0;
756 ? find_and_return_menu_selection
(f
, keymaps
, (void
*)retVal
)
764 /* ==========================================================================
766 Context Menu: implementing functions
768 ========================================================================== */
771 cleanup_popup_menu
(Lisp_Object arg
)
773 discard_menu_items
();
779 ns_popup_menu
(Lisp_Object position
, Lisp_Object
menu)
782 struct frame
*f
= NULL;
784 Lisp_Object window
, x
, y
, tem
, keymap
, title;
786 int specpdl_count
= SPECPDL_INDEX
(), specpdl_count2
;
787 char *error_name
= NULL;
789 widget_value
*wv
, *first_wv
= 0;
791 NSTRACE
(ns_popup_menu
);
793 if (!NILP
(position
))
797 if (EQ
(position
, Qt
)
798 ||
(CONSP
(position
) && (EQ
(XCAR
(position
), Qmenu_bar
)
799 || EQ
(XCAR
(position
), Qtool_bar
))))
801 /* Use the mouse's current position. */
802 struct frame
*new_f
= SELECTED_FRAME
();
804 if (FRAME_TERMINAL
(new_f
)->mouse_position_hook
)
805 (*FRAME_TERMINAL
(new_f
)->mouse_position_hook
)
806 (&new_f
, 0, 0, 0, &x
, &y
, 0);
808 XSETFRAME
(window
, new_f
);
811 window
= selected_window
;
818 CHECK_CONS
(position
);
819 tem
= Fcar
(position
);
820 if (XTYPE
(tem
) == Lisp_Cons
)
822 window
= Fcar
(Fcdr
(position
));
824 y
= Fcar
(Fcdr
(tem
));
828 tem
= Fcar
(Fcdr
(position
));
830 tem
= Fcar
(Fcdr
(Fcdr
(tem
)));
848 struct window
*win
= XWINDOW
(window
);
849 CHECK_LIVE_WINDOW
(window
);
850 f
= XFRAME
(WINDOW_FRAME
(win
));
851 p.x
= FRAME_COLUMN_WIDTH
(f
) * WINDOW_LEFT_EDGE_COL
(win
);
852 p.y
= FRAME_LINE_HEIGHT
(f
) * WINDOW_TOP_EDGE_LINE
(win
);
855 p.x
+= XINT
(x
); p.y
+= XINT
(y
);
857 XSETFRAME
(Vmenu_updating_frame
, f
);
860 { /* no position given */
861 /* FIXME: if called during dump, we need to stop precomputation of
862 key equivalents (see below) because the keydefs in ns-win.el have
863 not been loaded yet. */
866 Vmenu_updating_frame
= Qnil
;
869 /* now parse the lisp menus */
870 record_unwind_protect
(unuse_menu_items
, Qnil
);
874 /* Decode the menu items from what was specified. */
876 keymap
= get_keymap
(menu, 0, 0);
879 /* We were given a keymap. Extract menu info from the keymap. */
882 /* Extract the detailed info to make one pane. */
883 keymap_panes
(&menu, 1, NILP
(position
));
885 /* Search for a string appearing directly as an element of the keymap.
886 That string is the title of the menu. */
887 prompt
= Fkeymap_prompt
(keymap
);
888 title = NILP
(prompt
) ? build_string
("Select"
) : prompt
;
890 /* Make that be the pane title of the first pane. */
891 if (!NILP
(prompt
) && menu_items_n_panes
>= 0)
892 XVECTOR
(menu_items
)->contents
[MENU_ITEMS_PANE_NAME
] = prompt
;
896 else if (CONSP
(menu) && KEYMAPP
(XCAR
(menu)))
898 /* We were given a list of keymaps. */
899 int nmaps
= XFASTINT
(Flength
(menu));
901 = (Lisp_Object
*) alloca
(nmaps
* sizeof
(Lisp_Object
));
906 /* The first keymap that has a prompt string
907 supplies the menu title. */
908 for (tem
= menu, i
= 0; CONSP
(tem
); tem
= XCDR
(tem
))
912 maps
[i
++] = keymap
= get_keymap
(XCAR
(tem
), 1, 0);
914 prompt
= Fkeymap_prompt
(keymap
);
915 if (NILP
(title) && !NILP
(prompt
))
919 /* Extract the detailed info to make one pane. */
920 keymap_panes
(maps
, nmaps
, NILP
(position
));
922 /* Make the title be the pane title of the first pane. */
923 if (!NILP
(title) && menu_items_n_panes
>= 0)
924 XVECTOR
(menu_items
)->contents
[MENU_ITEMS_PANE_NAME
] = title;
930 /* We were given an old-fashioned menu. */
932 CHECK_STRING
(title);
934 list_of_panes
(Fcdr
(menu));
939 unbind_to
(specpdl_count
, Qnil
);
941 /* If no position given, that was a signal to just precompute and cache
942 key equivalents, which was a side-effect of what we just did. */
945 discard_menu_items
();
950 record_unwind_protect
(cleanup_popup_menu
, Qnil
);
953 /* now parse stage 2 as in ns_update_menubar */
954 wv
= xmalloc_widget_value
();
955 wv
->name
= "contextmenu"
;
958 wv
->button_type
= BUTTON_TYPE_NONE
;
962 specpdl_count2
= SPECPDL_INDEX
();
965 /* FIXME: a couple of one-line differences prevent reuse */
966 wv
= digest_single_submenu
(0, menu_items_used
, Qnil
);
969 widget_value
*save_wv
= 0, *prev_wv
= 0;
970 widget_value
**submenu_stack
971 = (widget_value
**) alloca
(menu_items_used
* sizeof
(widget_value
*));
972 /* Lisp_Object *subprefix_stack
973 = (Lisp_Object *) alloca (menu_items_used * sizeof (Lisp_Object)); */
974 int submenu_depth
= 0;
978 /* Loop over all panes and items, filling in the tree. */
980 while (i
< menu_items_used
)
982 if (EQ
(XVECTOR
(menu_items
)->contents
[i
], Qnil
))
984 submenu_stack
[submenu_depth
++] = save_wv
;
990 else if (EQ
(XVECTOR
(menu_items
)->contents
[i
], Qlambda
))
993 save_wv
= submenu_stack
[--submenu_depth
];
997 else if (EQ
(XVECTOR
(menu_items
)->contents
[i
], Qt
)
998 && submenu_depth
!= 0)
999 i
+= MENU_ITEMS_PANE_LENGTH
;
1000 /* Ignore a nil in the item list.
1001 It's meaningful only for dialog boxes. */
1002 else if (EQ
(XVECTOR
(menu_items
)->contents
[i
], Qquote
))
1004 else if (EQ
(XVECTOR
(menu_items
)->contents
[i
], Qt
))
1006 /* Create a new pane. */
1007 Lisp_Object pane_name
, prefix
;
1010 pane_name
= AREF
(menu_items
, i
+ MENU_ITEMS_PANE_NAME
);
1011 prefix
= AREF
(menu_items
, i
+ MENU_ITEMS_PANE_PREFIX
);
1013 #ifndef HAVE_MULTILINGUAL_MENU
1014 if (STRINGP
(pane_name
) && STRING_MULTIBYTE
(pane_name
))
1016 pane_name
= ENCODE_MENU_STRING
(pane_name
);
1017 ASET
(menu_items
, i
+ MENU_ITEMS_PANE_NAME
, pane_name
);
1020 pane_string
= (NILP
(pane_name
)
1021 ? ""
: (char *) SDATA
(pane_name
));
1022 /* If there is just one top-level pane, put all its items directly
1023 under the top-level menu. */
1024 if (menu_items_n_panes
== 1)
1027 /* If the pane has a meaningful name,
1028 make the pane a top-level menu item
1029 with its items as a submenu beneath it. */
1030 if (!keymaps
&& strcmp (pane_string
, ""
))
1032 wv
= xmalloc_widget_value
();
1036 first_wv
->contents
= wv
;
1037 wv
->name
= pane_string
;
1038 if (keymaps
&& !NILP
(prefix
))
1042 wv
->button_type
= BUTTON_TYPE_NONE
;
1047 else if (first_pane
)
1053 i
+= MENU_ITEMS_PANE_LENGTH
;
1057 /* Create a new item within current pane. */
1058 Lisp_Object item_name
, enable
, descrip
, def
, type, selected
, help;
1059 item_name
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_NAME
);
1060 enable
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_ENABLE
);
1061 descrip
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
);
1062 def
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_DEFINITION
);
1063 type = AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_TYPE
);
1064 selected
= AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_SELECTED
);
1065 help = AREF
(menu_items
, i
+ MENU_ITEMS_ITEM_HELP
);
1067 #ifndef HAVE_MULTILINGUAL_MENU
1068 if (STRINGP
(item_name
) && STRING_MULTIBYTE
(item_name
))
1070 item_name
= ENCODE_MENU_STRING
(item_name
);
1071 ASET
(menu_items
, i
+ MENU_ITEMS_ITEM_NAME
, item_name
);
1074 if (STRINGP
(descrip
) && STRING_MULTIBYTE
(descrip
))
1076 descrip
= ENCODE_MENU_STRING
(descrip
);
1077 ASET
(menu_items
, i
+ MENU_ITEMS_ITEM_EQUIV_KEY
, descrip
);
1079 #endif
/* not HAVE_MULTILINGUAL_MENU */
1081 wv
= xmalloc_widget_value
();
1085 save_wv
->contents
= wv
;
1086 wv
->name
= (char *) SDATA
(item_name
);
1087 if (!NILP
(descrip
))
1088 wv
->key
= (char *) SDATA
(descrip
);
1090 /* If this item has a null value,
1091 make the call_data null so that it won't display a box
1092 when the mouse is on it. */
1094 = !NILP
(def
) ?
(void
*) &XVECTOR
(menu_items
)->contents
[i
] : 0;
1095 wv
->enabled
= !NILP
(enable
);
1098 wv
->button_type
= BUTTON_TYPE_NONE
;
1099 else if (EQ
(type, QCtoggle
))
1100 wv
->button_type
= BUTTON_TYPE_TOGGLE
;
1101 else if (EQ
(type, QCradio
))
1102 wv
->button_type
= BUTTON_TYPE_RADIO
;
1106 wv
->selected
= !NILP
(selected
);
1108 if (! STRINGP
(help))
1115 i
+= MENU_ITEMS_ITEM_LENGTH
;
1123 widget_value
*wv_title
= xmalloc_widget_value
();
1124 widget_value
*wv_sep
= xmalloc_widget_value
();
1126 /* Maybe replace this separator with a bitmap or owner-draw item
1127 so that it looks better. Having two separators looks odd. */
1128 wv_sep
->name
= "
--"
;
1129 wv_sep
->next
= first_wv
->contents
;
1130 wv_sep
->help = Qnil
;
1132 #ifndef HAVE_MULTILINGUAL_MENU
1133 if (STRING_MULTIBYTE
(title))
1134 title = ENCODE_MENU_STRING
(title);
1137 wv_title
->name
= (char *) SDATA
(title);
1138 wv_title
->enabled
= NO
;
1139 wv_title
->button_type
= BUTTON_TYPE_NONE
;
1140 wv_title
->help = Qnil
;
1141 wv_title
->next
= wv_sep
;
1142 first_wv
->contents
= wv_title
;
1145 pmenu
= [[EmacsMenu alloc
] initWithTitle
:
1146 [NSString stringWithUTF8String
: SDATA
(title)]];
1147 [pmenu fillWithWidgetValue
: first_wv
->contents
];
1148 free_menubar_widget_value_tree
(first_wv
);
1149 unbind_to
(specpdl_count2
, Qnil
);
1151 popup_activated_flag
= 1;
1152 tem
= [pmenu runMenuAt
: p forFrame
: f keymaps
: keymaps
];
1153 popup_activated_flag
= 0;
1154 [[FRAME_NS_VIEW
(SELECTED_FRAME
()) window
] makeKeyWindow
];
1157 discard_menu_items
();
1158 unbind_to
(specpdl_count
, Qnil
);
1161 if (error_name
) error (error_name
);
1168 /* ==========================================================================
1170 Toolbar: externally-called functions
1172 ========================================================================== */
1175 free_frame_tool_bar
(FRAME_PTR f
)
1176 /* --------------------------------------------------------------------------
1177 Under NS we just hide the toolbar until it might be needed again.
1178 -------------------------------------------------------------------------- */
1180 [[FRAME_NS_VIEW
(f
) toolbar
] setVisible
: NO
];
1184 update_frame_tool_bar
(FRAME_PTR f
)
1185 /* --------------------------------------------------------------------------
1186 Update toolbar contents
1187 -------------------------------------------------------------------------- */
1190 EmacsToolbar
*toolbar
= [FRAME_NS_VIEW
(f
) toolbar
];
1192 [toolbar clearActive
];
1194 /* update EmacsToolbar as in GtkUtils, build items list */
1195 for (i
= 0; i
< f
->n_tool_bar_items
; ++i
)
1197 #define TOOLPROP
(IDX
) AREF
(f
->tool_bar_items
, \
1198 i
* TOOL_BAR_ITEM_NSLOTS
+ (IDX
))
1200 BOOL enabled_p
= !NILP
(TOOLPROP
(TOOL_BAR_ITEM_ENABLED_P
));
1201 BOOL selected_p
= !NILP
(TOOLPROP
(TOOL_BAR_ITEM_SELECTED_P
));
1206 Lisp_Object helpObj
;
1209 /* If image is a vector, choose the image according to the
1211 image = TOOLPROP
(TOOL_BAR_ITEM_IMAGES
);
1212 if (VECTORP
(image))
1214 /* NS toolbar auto-computes disabled and selected images */
1215 idx
= TOOL_BAR_IMAGE_ENABLED_SELECTED
;
1216 xassert
(ASIZE
(image) >= idx
);
1217 image = AREF
(image, idx
);
1223 /* Ignore invalid image specifications. */
1224 if (!valid_image_p
(image))
1226 NSLog
(@"Invalid
image for toolbar item"
);
1230 img_id
= lookup_image
(f
, image);
1231 img
= IMAGE_FROM_ID
(f
, img_id
);
1232 prepare_image_for_display
(f
, img
);
1234 if (img
->load_failed_p || img
->pixmap
== nil
)
1236 NSLog
(@"Could not prepare toolbar
image for display."
);
1240 helpObj
= TOOLPROP
(TOOL_BAR_ITEM_HELP
);
1242 helpObj
= TOOLPROP
(TOOL_BAR_ITEM_CAPTION
);
1243 helpText
= NILP
(helpObj
) ? ""
: (char *)SDATA
(helpObj
);
1245 [toolbar addDisplayItemWithImage
: img
->pixmap idx
: i helpText
: helpText
1246 enabled
: enabled_p
];
1250 if (![toolbar isVisible
])
1251 [toolbar setVisible
: YES
];
1253 if ([toolbar changed
])
1255 /* inform app that toolbar has changed */
1256 NSDictionary
*dict
= [toolbar configurationDictionary
];
1257 NSMutableDictionary
*newDict
= [dict mutableCopy
];
1258 NSEnumerator
*keys
= [[dict allKeys
] objectEnumerator
];
1260 while ((key
= [keys nextObject
]) != nil
)
1262 NSObject
*val
= [dict objectForKey
: key
];
1263 if ([val isKindOfClass
: [NSArray
class]])
1266 [toolbar toolbarDefaultItemIdentifiers
: toolbar
]
1271 [toolbar setConfigurationFromDictionary
: newDict
];
1278 /* ==========================================================================
1280 Toolbar: class implementation
1282 ========================================================================== */
1284 @implementation EmacsToolbar
1286 - initForView
: (EmacsView
*)view withIdentifier
: (NSString
*)identifier
1288 self
= [super initWithIdentifier
: identifier
];
1290 [self setDisplayMode
: NSToolbarDisplayModeIconOnly
];
1291 [self setSizeMode
: NSToolbarSizeModeSmall
];
1292 [self setDelegate
: self
];
1293 identifierToItem
= [[NSMutableDictionary alloc
] initWithCapacity
: 10];
1294 activeIdentifiers
= [[NSMutableArray alloc
] initWithCapacity
: 8];
1295 prevEnablement
= enablement
= 0L;
1301 [prevIdentifiers release
];
1302 [activeIdentifiers release
];
1303 [identifierToItem release
];
1307 - (void
) clearActive
1309 [prevIdentifiers release
];
1310 prevIdentifiers
= [activeIdentifiers copy
];
1311 [activeIdentifiers removeAllObjects
];
1312 prevEnablement
= enablement
;
1318 return [activeIdentifiers isEqualToArray
: prevIdentifiers
] &&
1319 enablement
== prevEnablement ? NO
: YES
;
1322 - (void
) addDisplayItemWithImage
: (EmacsImage
*)img idx
: (int
)idx
1323 helpText
: (char *)help enabled
: (BOOL
)enabled
1325 /* 1) come up w/identifier */
1326 NSString
*identifier
1327 = [NSString stringWithFormat
: @"
%u", [img hash]];
1329 /* 2) create / reuse item */
1330 NSToolbarItem
*item
= [identifierToItem objectForKey
: identifier
];
1333 item
= [[[NSToolbarItem alloc
] initWithItemIdentifier
: identifier
]
1335 [item setImage
: img
];
1336 [item setToolTip
: [NSString stringWithCString
: help]];
1337 [item setTarget
: emacsView
];
1338 [item setAction
: @selector
(toolbarClicked
:)];
1342 [item setEnabled
: enabled
];
1344 /* 3) update state */
1345 [identifierToItem setObject
: item forKey
: identifier
];
1346 [activeIdentifiers addObject
: identifier
];
1347 enablement
= (enablement
<< 1) |
(enabled
== YES
);
1350 /* This overrides super's implementation, which automatically sets
1351 all items to enabled state (for some reason). */
1352 - (void
)validateVisibleItems
{ }
1355 /* delegate methods */
1357 - (NSToolbarItem
*)toolbar
: (NSToolbar
*)toolbar
1358 itemForItemIdentifier
: (NSString
*)itemIdentifier
1359 willBeInsertedIntoToolbar
: (BOOL
)flag
1361 /* look up NSToolbarItem by identifier and return... */
1362 return [identifierToItem objectForKey
: itemIdentifier
];
1365 - (NSArray
*)toolbarDefaultItemIdentifiers
: (NSToolbar
*)toolbar
1367 /* return entire set.. */
1368 return activeIdentifiers
;
1371 /* for configuration palette (not yet supported) */
1372 - (NSArray
*)toolbarAllowedItemIdentifiers
: (NSToolbar
*)toolbar
1374 /* return entire set... */
1375 return [identifierToItem allKeys
];
1378 /* optional and unneeded */
1379 /* - toolbarWillAddItem: (NSNotification *)notification { } */
1380 /* - toolbarDidRemoveItem: (NSNotification *)notification { } */
1381 /* - (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar */
1383 @
end /* EmacsToolbar */
1387 /* ==========================================================================
1389 Tooltip: class implementation
1391 ========================================================================== */
1393 /* Needed because NeXTstep does not provide enough control over tooltip
1395 @implementation EmacsTooltip
1399 NSColor
*col
= [NSColor colorWithCalibratedRed
: 1.0 green
: 1.0
1400 blue
: 0.792 alpha
: 0.95];
1401 NSFont
*font
= [NSFont toolTipsFontOfSize
: 0];
1402 NSFont
*sfont
= [font screenFont
];
1403 int height
= [sfont ascender
] - [sfont descender
];
1404 /*[font boundingRectForFont].size.height; */
1405 NSRect r
= NSMakeRect
(0, 0, 100, height
+6);
1407 textField
= [[NSTextField alloc
] initWithFrame
: r
];
1408 [textField setFont
: font
];
1409 [textField setBackgroundColor
: col
];
1411 [textField setEditable
: NO
];
1412 [textField setSelectable
: NO
];
1413 [textField setBordered
: YES
];
1414 [textField setBezeled
: YES
];
1415 [textField setDrawsBackground
: YES
];
1417 win
= [[NSWindow alloc
]
1418 initWithContentRect
: [textField frame
]
1420 backing
: NSBackingStoreBuffered
1422 [win setReleasedWhenClosed
: NO
];
1423 [win setDelegate
: self
];
1424 [[win contentView
] addSubview
: textField
];
1425 /* [win setBackgroundColor: col]; */
1426 [win setOpaque
: NO
];
1435 [textField release
];
1439 - (void
) setText
: (char *)text
1441 NSString
*str
= [NSString stringWithUTF8String
: text];
1442 NSRect r
= [textField frame
];
1443 NSSize textSize
= [str sizeWithAttributes
:
1444 [NSDictionary dictionaryWithObject
: [[textField font
] screenFont
]
1445 forKey
: NSFontAttributeName
]];
1446 NSSize padSize
= [[[textField font
] screenFont
]
1447 boundingRectForFont
].
size;
1449 r.
size.width
= textSize.width
+ padSize.width
/2;
1450 r.
size.height
= textSize.height
+ padSize.height
/2;
1451 [textField setFrame
: r
];
1452 [textField setStringValue
: str
];
1455 - (void
) showAtX
: (int
)x Y
: (int
)y
for: (int
)seconds
1457 NSRect wr
= [win frame
];
1459 wr.origin
= NSMakePoint
(x
, y
);
1460 wr.
size = [textField frame
].
size;
1462 [win setFrame
: wr display
: YES
];
1463 [win orderFront
: self
];
1465 timer
= [NSTimer scheduledTimerWithTimeInterval
: (float
)seconds target
: self
1466 selector
: @selector
(hide
)
1467 userInfo
: nil repeats
: NO
];
1476 if ([timer isValid
])
1485 return timer
!= nil
;
1490 return [textField frame
];
1493 @
end /* EmacsTooltip */
1497 /* ==========================================================================
1499 Popup Dialog: implementing functions
1501 ========================================================================== */
1505 pop_down_menu
(Lisp_Object arg
)
1507 struct Lisp_Save_Value
*p
= XSAVE_VALUE
(arg
);
1508 if (popup_activated_flag
)
1510 popup_activated_flag
= 0;
1512 [NSApp endModalSession
: popupSession
];
1513 [((EmacsDialogPanel
*) (p
->pointer
)) close];
1514 [[FRAME_NS_VIEW
(SELECTED_FRAME
()) window
] makeKeyWindow
];
1522 ns_popup_dialog
(Lisp_Object position
, Lisp_Object contents
, Lisp_Object header
)
1525 Lisp_Object window
, tem
;
1530 NSTRACE
(x
-popup
-dialog);
1534 isQ
= NILP
(header
);
1536 if (EQ
(position
, Qt
)
1537 ||
(CONSP
(position
) && (EQ
(XCAR
(position
), Qmenu_bar
)
1538 || EQ
(XCAR
(position
), Qtool_bar
))))
1540 window
= selected_window
;
1542 else if (CONSP
(position
))
1545 tem
= Fcar
(position
);
1546 if (XTYPE
(tem
) == Lisp_Cons
)
1547 window
= Fcar
(Fcdr
(position
));
1550 tem
= Fcar
(Fcdr
(position
)); /* EVENT_START (position) */
1551 window
= Fcar
(tem
); /* POSN_WINDOW (tem) */
1554 else if (WINDOWP
(position
) || FRAMEP
(position
))
1561 if (FRAMEP
(window
))
1562 f
= XFRAME
(window
);
1563 else if (WINDOWP
(window
))
1565 CHECK_LIVE_WINDOW
(window
);
1566 f
= XFRAME
(WINDOW_FRAME
(XWINDOW
(window
)));
1569 CHECK_WINDOW
(window
);
1571 p.x
= (int
)f
->left_pos
+ ((int
)FRAME_COLUMN_WIDTH
(f
) * f
->text_cols
)/2;
1572 p.y
= (int
)f
->top_pos
+ (FRAME_LINE_HEIGHT
(f
) * f
->text_lines
)/2;
1575 dialog = [[EmacsDialogPanel alloc
] initFromContents
: contents
1578 int specpdl_count
= SPECPDL_INDEX
();
1579 record_unwind_protect
(pop_down_menu
, make_save_value
(dialog, 0));
1580 popup_activated_flag
= 1;
1581 tem
= [dialog runDialogAt
: p
];
1582 unbind_to
(specpdl_count
, Qnil
); /* calls pop_down_menu */
1590 /* ==========================================================================
1592 Popup Dialog: class implementation
1594 ========================================================================== */
1596 @interface FlippedView
: NSView
1601 @implementation FlippedView
1608 @implementation EmacsDialogPanel
1611 #define ICONSIZE
64.0
1612 #define TEXTHEIGHT
20.0
1613 #define MINCELLWIDTH
90.0
1615 - initWithContentRect
: (NSRect
)contentRect styleMask
: (unsigned int
)aStyle
1616 backing
: (NSBackingStoreType
)backingType defer
: (BOOL
)flag
1618 NSSize spacing
= {SPACER
, SPACER
};
1620 char this_cmd_name
[80];
1622 static NSImageView
*imgView
;
1623 static FlippedView
*contentView
;
1628 area.origin.x
= 3*SPACER
;
1629 area.origin.y
= 2*SPACER
;
1630 area.
size.width
= ICONSIZE
;
1631 area.
size.height
= ICONSIZE
;
1632 img
= [[NSImage imageNamed
: @"NSApplicationIcon"
] copy
];
1633 [img setScalesWhenResized
: YES
];
1634 [img setSize
: NSMakeSize
(ICONSIZE
, ICONSIZE
)];
1635 imgView
= [[NSImageView alloc
] initWithFrame
: area
];
1636 [imgView setImage
: img
];
1637 [imgView setEditable
: NO
];
1641 aStyle
= NSTitledWindowMask
;
1645 [super initWithContentRect
: contentRect styleMask
: aStyle
1646 backing
: backingType defer
: flag];
1647 contentView
= [[FlippedView alloc
] initWithFrame
: [[self contentView
] frame
]];
1648 [self setContentView
: contentView
];
1650 [[self contentView
] setAutoresizesSubviews
: YES
];
1652 [[self contentView
] addSubview
: imgView
];
1653 [self setTitle
: @""
];
1655 area.origin.x
+= ICONSIZE
+2*SPACER
;
1656 /* area.origin.y = TEXTHEIGHT; ICONSIZE/2-10+SPACER; */
1657 area.
size.width
= 400;
1658 area.
size.height
= TEXTHEIGHT
;
1659 command
= [[[NSTextField alloc
] initWithFrame
: area
] autorelease
];
1660 [[self contentView
] addSubview
: command
];
1661 [command setStringValue
: @"Emacs"
];
1662 [command setDrawsBackground
: NO
];
1663 [command setBezeled
: NO
];
1664 [command setSelectable
: NO
];
1665 [command setFont
: [NSFont boldSystemFontOfSize
: 13.0]];
1667 /* area.origin.x = ICONSIZE+2*SPACER;
1668 area.origin.y = TEXTHEIGHT + 2*SPACER;
1669 area.size.width = 400;
1670 area.size.height= 2;
1671 tem = [[[NSBox alloc] initWithFrame: area] autorelease];
1672 [[self contentView] addSubview: tem];
1673 [tem setTitlePosition: NSNoTitle];
1674 [tem setAutoresizingMask: NSViewWidthSizable];*/
1676 /* area.origin.x = ICONSIZE+2*SPACER; */
1677 area.origin.y
+= TEXTHEIGHT
+SPACER
;
1678 area.
size.width
= 400;
1679 area.
size.height
= TEXTHEIGHT
;
1680 title = [[[NSTextField alloc
] initWithFrame
: area
] autorelease
];
1681 [[self contentView
] addSubview
: title];
1682 [title setDrawsBackground
: NO
];
1683 [title setBezeled
: NO
];
1684 [title setSelectable
: NO
];
1685 [title setFont
: [NSFont systemFontOfSize
: 11.0]];
1687 cell = [[[NSButtonCell alloc
] initTextCell
: @""
] autorelease
];
1688 [cell setBordered
: NO
];
1689 [cell setEnabled
: NO
];
1690 [cell setCellAttribute
: NSCellIsInsetButton to
: 8];
1691 [cell setBezelStyle
: NSRoundedBezelStyle
];
1693 matrix
= [[NSMatrix alloc
] initWithFrame
: contentRect
1694 mode
: NSHighlightModeMatrix
1697 numberOfColumns
: 1];
1698 [[self contentView
] addSubview
: matrix
];
1700 [matrix setFrameOrigin
: NSMakePoint
(area.origin.x
,
1701 area.origin.y
+ (TEXTHEIGHT
+3*SPACER
))];
1702 [matrix setIntercellSpacing
: spacing
];
1704 [self setOneShot
: YES
];
1705 [self setReleasedWhenClosed
: YES
];
1706 [self setHidesOnDeactivate
: YES
];
1711 - (BOOL
)windowShouldClose
: (id
)sender
1713 [NSApp stopModalWithCode
: XHASH
(Qnil
)]; // FIXME
: BIG UGLY HACK
!!
1718 void process_dialog
(id window
, Lisp_Object list
)
1723 for (; XTYPE
(list
) == Lisp_Cons
; list
= XCDR
(list
))
1726 if (XTYPE
(item
) == Lisp_String
)
1728 [window addString
: SDATA
(item
) row
: row
++];
1730 else if (XTYPE
(item
) == Lisp_Cons
)
1732 [window addButton
: SDATA
(XCAR
(item
))
1733 value
: XCDR
(item
) row
: row
++];
1735 else if (NILP
(item
))
1744 - addButton
: (char *)str value
: (Lisp_Object
)val row
: (int
)row
1753 cell = [matrix cellAtRow
: row column
: cols
-1];
1754 [cell setTarget
: self
];
1755 [cell setAction
: @selector
(clicked
: )];
1756 [cell setTitle
: [NSString stringWithUTF8String
: str
]];
1757 [cell setTag
: XHASH
(val
)]; // FIXME
: BIG UGLY HACK
!!
1758 [cell setBordered
: YES
];
1759 [cell setEnabled
: YES
];
1765 - addString
: (char *)str row
: (int
)row
1774 cell = [matrix cellAtRow
: row column
: cols
-1];
1775 [cell setTitle
: [NSString stringWithUTF8String
: str
]];
1776 [cell setBordered
: YES
];
1777 [cell setEnabled
: NO
];
1793 NSArray
*sellist
= nil
;
1796 sellist
= [sender selectedCells
];
1797 if ([sellist count
]<1)
1800 seltag
= [[sellist objectAtIndex
: 0] tag
];
1801 if (seltag
!= XHASH
(Qundefined
)) // FIXME
: BIG UGLY HACK
!!
1802 [NSApp stopModalWithCode
: seltag
];
1807 - initFromContents
: (Lisp_Object
)contents isQuestion
: (BOOL
)isQ
1812 if (XTYPE
(contents
) == Lisp_Cons
)
1814 head
= Fcar
(contents
);
1815 process_dialog
(self
, Fcdr
(contents
));
1820 if (XTYPE
(head
) == Lisp_String
)
1821 [title setStringValue
:
1822 [NSString stringWithUTF8String
: SDATA
(head
)]];
1823 else if (isQ
== YES
)
1824 [title setStringValue
: @"Question"
];
1826 [title setStringValue
: @"Information"
];
1832 if (cols
== 1 && rows
> 1) /* Never told where to split */
1835 for (i
= 0; i
<rows
/2; i
++)
1837 [matrix putCell
: [matrix cellAtRow
: (rows
+1)/2 column
: 0]
1838 atRow
: i column
: 1];
1839 [matrix removeRow
: (rows
+1)/2];
1845 NSSize csize
= [matrix cellSize
];
1846 if (csize.width
< MINCELLWIDTH
)
1848 csize.width
= MINCELLWIDTH
;
1849 [matrix setCellSize
: csize
];
1850 [matrix sizeToCells
];
1855 [command sizeToFit
];
1859 if (r.
size.width
+r.origin.x
> t.
size.width
+t.origin.x
)
1861 t.origin.x
= r.origin.x
;
1862 t.
size.width
= r.
size.width
;
1864 r
= [command frame
];
1865 if (r.
size.width
+r.origin.x
> t.
size.width
+t.origin.x
)
1867 t.origin.x
= r.origin.x
;
1868 t.
size.width
= r.
size.width
;
1872 s
= [(NSView
*)[self contentView
] frame
];
1873 r.
size.width
+= t.origin.x
+t.
size.width
+2*SPACER
-s.
size.width
;
1874 r.
size.height
+= t.origin.y
+t.
size.height
+SPACER
-s.
size.height
;
1875 [self setFrame
: r display
: NO
];
1884 { [super dealloc
]; return; };
1888 - (Lisp_Object
)runDialogAt
: (NSPoint
)p
1891 extern EMACS_TIME timer_check
(int do_it_now
); /* TODO: add to a header */
1893 /* initiate a session that will be ended by pop_down_menu */
1894 popupSession
= [NSApp beginModalSessionForWindow
: self
];
1895 while (popup_activated_flag
1896 && (ret
= [NSApp runModalSession
: popupSession
])
1897 == NSRunContinuesResponse
)
1899 /* Run this for timers.el, indep of atimers; might not return.
1900 TODO: use return value to avoid calling every iteration. */
1902 [NSThread sleepUntilDate
: [NSDate dateWithTimeIntervalSinceNow
: 0.1]];
1905 { /* FIXME: BIG UGLY HACK!!! */
1907 *(EMACS_INT
*)(&tmp
) = ret
;
1915 /* ==========================================================================
1919 ========================================================================== */
1921 DEFUN
("ns
-reset-menu"
, Fns_reset_menu
, Sns_reset_menu
, 0, 0, 0,
1922 doc: /* Cause the NS menu to be re-calculated. */)
1925 set_frame_menubar
(SELECTED_FRAME
(), 1, 0);
1930 DEFUN
("x
-popup
-menu"
, Fx_popup_menu
, Sx_popup_menu
, 2, 2, 0,
1931 doc: /* Pop up a deck-of-cards menu and return user's selection.
1932 POSITION is a position specification. This is either a mouse button event
1933 or a list ((XOFFSET YOFFSET) WINDOW)
1934 where XOFFSET and YOFFSET are positions in pixels from the top left
1935 corner of WINDOW. (WINDOW may be a window or a frame object.)
1936 This controls the position of the top left of the menu as a whole.
1937 If POSITION is t, it means to use the current mouse position.
1939 MENU is a specifier for a menu. For the simplest case, MENU is a keymap.
1940 The menu items come from key bindings that have a menu string as well as
1941 a definition; actually, the \"definition\" in such a key binding looks like
1942 \(STRING . REAL-DEFINITION). To give the menu a title, put a string into
1943 the keymap as a top-level element.
1945 If REAL-DEFINITION is nil, that puts a nonselectable string in the menu.
1946 Otherwise, REAL-DEFINITION should be a valid key binding definition.
1948 You can also use a list of keymaps as MENU.
1949 Then each keymap makes a separate pane.
1951 When MENU is a keymap or a list of keymaps, the return value is the
1952 list of events corresponding to the user's choice. Note that
1953 `x-popup-menu' does not actually execute the command bound to that
1956 Alternatively, you can specify a menu of multiple panes
1957 with a list of the form (TITLE PANE1 PANE2...),
1958 where each pane is a list of form (TITLE ITEM1 ITEM2...).
1959 Each ITEM is normally a cons cell (STRING . VALUE);
1960 but a string can appear as an item--that makes a nonselectable line
1962 With this form of menu, the return value is VALUE from the chosen item. */)
1964 Lisp_Object position
, menu;
1966 return ns_popup_menu
(position
, menu);
1970 DEFUN
("x
-popup
-dialog"
, Fx_popup_dialog
, Sx_popup_dialog
, 2, 3, 0,
1971 doc: /* Pop up a dialog box and return user's selection.
1972 POSITION specifies which frame to use.
1973 This is normally a mouse button event or a window or frame.
1974 If POSITION is t, it means to use the frame the mouse is on.
1975 The dialog box appears in the middle of the specified frame.
1977 CONTENTS specifies the alternatives to display in the dialog box.
1978 It is a list of the form (DIALOG ITEM1 ITEM2...).
1979 Each ITEM is a cons cell (STRING . VALUE).
1980 The return value is VALUE from the chosen item.
1982 An ITEM may also be just a string--that makes a nonselectable item.
1983 An ITEM may also be nil--that means to put all preceding items
1984 on the left of the dialog box and all following items on the right.
1985 \(By default, approximately half appear on each side.)
1987 If HEADER is non-nil, the frame title for the box is "Information",
1988 otherwise it is "Question".
1990 If the user gets rid of the dialog box without making a valid choice,
1991 for instance using the window manager, then this produces a quit and
1992 `x-popup-dialog' does not return. */)
1993 (position
, contents
, header
)
1994 Lisp_Object position
, contents
, header
;
1996 return ns_popup_dialog
(position
, contents
, header
);
1999 DEFUN
("
menu-or
-popup
-active
-p"
, Fmenu_or_popup_active_p
, Smenu_or_popup_active_p
, 0, 0, 0,
2000 doc: /* Return t if a menu or popup dialog is active. */)
2003 return popup_activated
() ? Qt
: Qnil
;
2006 /* ==========================================================================
2008 Lisp interface declaration
2010 ========================================================================== */
2015 defsubr
(&Sx_popup_menu
);
2016 defsubr
(&Sx_popup_dialog
);
2017 defsubr
(&Sns_reset_menu
);
2018 defsubr
(&Smenu_or_popup_active_p
);
2019 staticpro
(&menu_items
);
2022 Qdebug_on_next_call
= intern
("
debug-on
-next
-call"
);
2023 staticpro
(&Qdebug_on_next_call
);
2026 // arch
-tag
: 75773656-52e5
-4c44
-a398
-47bd87b32619