1/*
2 * tkMacOSXMenus.c --
3 *
4 *	These calls set up the default menus for Tk.
5 *
6 * Copyright (c) 1995-1996 Sun Microsystems, Inc.
7 * Copyright 2001-2009, Apple Inc.
8 * Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id$
14 */
15
16#include "tkMacOSXPrivate.h"
17#include "tkMenu.h"
18
19static void		GenerateEditEvent(const char *name);
20static Tcl_Obj *	GetWidgetDemoPath(Tcl_Interp *interp);
21
22#pragma mark TKApplication(TKMenus)
23
24@implementation TKApplication(TKMenus)
25- (void)_setupMenus {
26    if (_defaultMainMenu) {
27	return;
28    }
29    TkMenuInit();
30    NSString *applicationName = [[NSBundle mainBundle]
31	    objectForInfoDictionaryKey:@"CFBundleName"];
32    if (!applicationName) {
33	applicationName = [[NSProcessInfo processInfo] processName];
34    }
35    NSString *aboutName = (applicationName &&
36	    ![applicationName isEqualToString:@"Wish"] &&
37	    ![applicationName hasPrefix:@"tclsh"]) ?
38	    applicationName : @"Tcl & Tk";
39    _servicesMenu = [NSMenu menuWithTitle:@"Services"];
40    _defaultApplicationMenuItems = [[NSArray arrayWithObjects:
41	    [NSMenuItem separatorItem],
42	    [NSMenuItem itemWithTitle:
43		   [NSString stringWithFormat:@"Preferences%C", 0x2026]
44		   action:@selector(preferences:) keyEquivalent:@","],
45	    [NSMenuItem separatorItem],
46	    [NSMenuItem itemWithTitle:@"Services" submenu:_servicesMenu],
47	    [NSMenuItem separatorItem],
48	    [NSMenuItem itemWithTitle:
49		   [NSString stringWithFormat:@"Hide %@", applicationName]
50		   action:@selector(hide:) keyEquivalent:@"h"],
51	    [NSMenuItem itemWithTitle:@"Hide Others"
52		   action:@selector(hideOtherApplications:) keyEquivalent:@"h"
53		   keyEquivalentModifierMask:
54		   NSCommandKeyMask|NSAlternateKeyMask],
55	    [NSMenuItem itemWithTitle:@"Show All"
56		   action:@selector(unhideAllApplications:)],
57	    [NSMenuItem separatorItem],
58	    [NSMenuItem itemWithTitle:
59		   [NSString stringWithFormat:@"Quit %@", applicationName]
60		   action: @selector(terminate:) keyEquivalent:@"q"],
61	    nil] retain];
62    _defaultApplicationMenu = [TKMenu menuWithTitle:applicationName
63	    menuItems:_defaultApplicationMenuItems];
64    [_defaultApplicationMenu insertItem:
65	    [NSMenuItem itemWithTitle:
66		    [NSString stringWithFormat:@"About %@", aboutName]
67		    action:@selector(orderFrontStandardAboutPanel:)] atIndex:0];
68    TKMenu *fileMenu = [TKMenu menuWithTitle:@"File" menuItems:
69	    [NSArray arrayWithObjects:
70	    [NSMenuItem itemWithTitle:
71		   [NSString stringWithFormat:@"Source%C", 0x2026]
72		   action:@selector(tkSource:)],
73	    [NSMenuItem itemWithTitle:@"Run Widget Demo"
74		   action:@selector(tkDemo:)],
75	    [NSMenuItem itemWithTitle:@"Close" action:@selector(performClose:)
76		   target:nil keyEquivalent:@"w"],
77	    nil]];
78    TKMenu *editMenu = [TKMenu menuWithTitle:@"Edit" menuItems:
79	    [NSArray arrayWithObjects:
80	    [NSMenuItem itemWithTitle:@"Undo" action:@selector(undo:)
81		   target:nil keyEquivalent:@"z"],
82	    [NSMenuItem itemWithTitle:@"Redo" action:@selector(redo:)
83		   target:nil keyEquivalent:@"y"],
84	    [NSMenuItem separatorItem],
85	    [NSMenuItem itemWithTitle:@"Cut" action:@selector(cut:)
86		   target:nil keyEquivalent:@"x"],
87	    [NSMenuItem itemWithTitle:@"Copy" action:@selector(copy:)
88		   target:nil keyEquivalent:@"c"],
89	    [NSMenuItem itemWithTitle:@"Paste" action:@selector(paste:)
90		   target:nil keyEquivalent:@"v"],
91	    [NSMenuItem itemWithTitle:@"Delete" action:@selector(delete:)
92		   target:nil],
93	    nil]];
94    _defaultWindowsMenuItems = [[NSArray arrayWithObjects:
95	    [NSMenuItem itemWithTitle:@"Minimize"
96		   action:@selector(performMiniaturize:) target:nil
97		   keyEquivalent:@"m"],
98	    [NSMenuItem itemWithTitle:@"Zoom" action:@selector(performZoom:)
99		   target:nil],
100	    [NSMenuItem separatorItem],
101	    [NSMenuItem itemWithTitle:@"Bring All to Front"
102		   action:@selector(arrangeInFront:)],
103	    nil] retain];
104    TKMenu *windowsMenu = [TKMenu menuWithTitle:@"Window" menuItems:
105	    _defaultWindowsMenuItems];
106    _defaultHelpMenuItems = [[NSArray arrayWithObjects:
107	    [NSMenuItem itemWithTitle:
108		   [NSString stringWithFormat:@"%@ Help", applicationName]
109		   action:@selector(showHelp:) keyEquivalent:@"?"],
110	    nil] retain];
111    TKMenu *helpMenu = [TKMenu menuWithTitle:@"Help" menuItems:
112	    _defaultHelpMenuItems];
113    [self setServicesMenu:_servicesMenu];
114    [self setWindowsMenu:windowsMenu];
115    _defaultMainMenu = [[TKMenu menuWithTitle:@"" submenus:[NSArray
116	    arrayWithObjects:_defaultApplicationMenu, fileMenu, editMenu,
117	    windowsMenu, helpMenu, nil]] retain];
118    [_defaultMainMenu setSpecial:tkMainMenu];
119    [_defaultApplicationMenu setSpecial:tkApplicationMenu];
120    [windowsMenu setSpecial:tkWindowsMenu];
121    [helpMenu setSpecial:tkHelpMenu];
122    [self tkSetMainMenu:nil];
123}
124- (void)dealloc {
125    [_defaultMainMenu release];
126    [_defaultHelpMenuItems release];
127    [_defaultWindowsMenuItems release];
128    [_defaultApplicationMenuItems release];
129    [super dealloc];
130}
131- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem {
132    SEL action = [anItem action];
133
134    if (sel_isEqual(action, @selector(preferences:))) {
135	Tcl_CmdInfo dummy;
136	return (_eventInterp && Tcl_GetCommandInfo(_eventInterp,
137		"::tk::mac::ShowPreferences", &dummy));
138    } else if (sel_isEqual(action, @selector(tkDemo:))) {
139	BOOL haveDemo = NO;
140	if (_eventInterp) {
141	    Tcl_Obj *path = GetWidgetDemoPath(_eventInterp);
142
143	    if (path) {
144		Tcl_IncrRefCount(path);
145		haveDemo = (Tcl_FSAccess(path, R_OK) == 0);
146		Tcl_DecrRefCount(path);
147	    }
148	}
149	return haveDemo;
150    } else {
151        return [super validateUserInterfaceItem:anItem];
152    }
153}
154- (void)orderFrontStandardAboutPanel:(id)sender {
155    Tcl_CmdInfo dummy;
156    if (!_eventInterp || !Tcl_GetCommandInfo(_eventInterp, "tkAboutDialog",
157	    &dummy) || (GetCurrentEventKeyModifiers() & optionKey)) {
158	TkAboutDlg();
159    } else {
160	int code = Tcl_EvalEx(_eventInterp, "tkAboutDialog", -1,
161		TCL_EVAL_GLOBAL);
162	if (code != TCL_OK) {
163	    Tcl_BackgroundError(_eventInterp);
164	}
165	Tcl_ResetResult(_eventInterp);
166    }
167}
168- (void)showHelp:(id)sender {
169    Tcl_CmdInfo dummy;
170    if (!_eventInterp || !Tcl_GetCommandInfo(_eventInterp,
171	    "::tk::mac::ShowHelp", &dummy)) {
172	[super showHelp:sender];
173    } else {
174	int code = Tcl_EvalEx(_eventInterp, "::tk::mac::ShowHelp", -1,
175		TCL_EVAL_GLOBAL);
176	if (code != TCL_OK) {
177	    Tcl_BackgroundError(_eventInterp);
178	}
179	Tcl_ResetResult(_eventInterp);
180    }
181}
182- (void)tkSource:(id)sender {
183    if (_eventInterp) {
184	if (Tcl_EvalEx(_eventInterp, "tk_getOpenFile -filetypes {"
185		"{{TCL Scripts} {.tcl} TEXT} {{Text Files} {} TEXT}}",
186		-1, TCL_EVAL_GLOBAL) == TCL_OK) {
187	    Tcl_Obj *path = Tcl_GetObjResult(_eventInterp);
188	    int len;
189	    Tcl_GetStringFromObj(path, &len);
190	    if (len) {
191		Tcl_IncrRefCount(path);
192		int code = Tcl_FSEvalFile(_eventInterp, path);
193		if (code != TCL_OK) {
194		    Tcl_BackgroundError(_eventInterp);
195		}
196		Tcl_DecrRefCount(path);
197	    }
198	}
199	Tcl_ResetResult(_eventInterp);
200    }
201}
202- (void)tkDemo:(id)sender {
203    if (_eventInterp) {
204	Tcl_Obj *path = GetWidgetDemoPath(_eventInterp);
205	if (path) {
206	    Tcl_IncrRefCount(path);
207	    int code = Tcl_FSEvalFile(_eventInterp, path);
208	    if (code != TCL_OK) {
209		Tcl_BackgroundError(_eventInterp);
210	    }
211	    Tcl_DecrRefCount(path);
212	    Tcl_ResetResult(_eventInterp);
213	}
214    }
215}
216@end
217
218#pragma mark TKContentView(TKMenus)
219
220@implementation TKContentView(TKMenus)
221- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem {
222    return YES;
223}
224#define EDIT_ACTION(a, e) \
225    - (void) a:(id)sender { \
226	if ([sender isKindOfClass:[NSMenuItem class]]) { \
227	    GenerateEditEvent(#e); \
228	} \
229    }
230EDIT_ACTION(cut, Cut)
231EDIT_ACTION(copy, Copy)
232EDIT_ACTION(paste, Paste)
233EDIT_ACTION(delete, Clear)
234EDIT_ACTION(undo, Undo)
235EDIT_ACTION(redo, Redo)
236#undef EDIT_ACTION
237@end
238
239#pragma mark -
240
241/*
242 *----------------------------------------------------------------------
243 *
244 * GetWidgetDemoPath --
245 *
246 *	Get path to the widget demo.
247 *
248 * Results:
249 *	pathObj with ref count 0.
250 *
251 * Side effects:
252 *	None.
253 *
254 *----------------------------------------------------------------------
255 */
256
257static Tcl_Obj *
258GetWidgetDemoPath(
259    Tcl_Interp *interp)
260{
261    Tcl_Obj *libpath, *result = NULL;
262
263    libpath = Tcl_GetVar2Ex(interp, "tk_library", NULL, TCL_GLOBAL_ONLY);
264    if (libpath) {
265	Tcl_Obj *demo[2] = {	Tcl_NewStringObj("demos", 5),
266				Tcl_NewStringObj("widget", 6) };
267
268	Tcl_IncrRefCount(libpath);
269	result = Tcl_FSJoinToPath(libpath, 2, demo);
270	Tcl_DecrRefCount(libpath);
271    } else {
272	Tcl_ResetResult(interp);
273    }
274    return result;
275}
276
277/*
278 *----------------------------------------------------------------------
279 *
280 * TkMacOSXHandleMenuSelect --
281 *
282 *	Handles events that occur in the Menu bar.
283 *
284 * Results:
285 *	None.
286 *
287 * Side effects:
288 *	None.
289 *
290 *----------------------------------------------------------------------
291 */
292
293void
294TkMacOSXHandleMenuSelect(
295    short theMenu,
296    unsigned short theItem,
297    int optionKeyPressed)
298{
299    Tcl_Panic("TkMacOSXHandleMenuSelect: Obsolete, no more Carbon!");
300}
301
302/*
303 *----------------------------------------------------------------------
304 *
305 * TkMacOSXInitMenus --
306 *
307 *	This procedure initializes the Macintosh menu bar.
308 *
309 * Results:
310 *	None.
311 *
312 * Side effects:
313 *	None.
314 *
315 *----------------------------------------------------------------------
316 */
317
318void
319TkMacOSXInitMenus(
320    Tcl_Interp *interp)
321{
322    [NSApp _setupMenus];
323}
324
325/*
326 *----------------------------------------------------------------------
327 *
328 * GenerateEditEvent --
329 *
330 *	Takes an edit menu item and posts the corasponding a virtual event to
331 *	Tk's event queue.
332 *
333 * Results:
334 *	None.
335 *
336 * Side effects:
337 *	May place events of queue.
338 *
339 *----------------------------------------------------------------------
340 */
341
342static void
343GenerateEditEvent(
344    const char *name)
345{
346    XVirtualEvent event;
347    int x, y;
348    TkWindow *winPtr = TkMacOSXGetTkWindow([NSApp keyWindow]);
349    Tk_Window tkwin = (Tk_Window) winPtr;
350
351    if (tkwin == NULL) {
352	return;
353    }
354    tkwin = (Tk_Window) winPtr->dispPtr->focusPtr;
355    if (tkwin == NULL) {
356	return;
357    }
358    bzero(&event, sizeof(XVirtualEvent));
359    event.type = VirtualEvent;
360    event.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
361    event.send_event = false;
362    event.display = Tk_Display(tkwin);
363    event.event = Tk_WindowId(tkwin);
364    event.root = XRootWindow(Tk_Display(tkwin), 0);
365    event.subwindow = None;
366    event.time = TkpGetMS();
367    XQueryPointer(NULL, winPtr->window, NULL, NULL,
368	    &event.x_root, &event.y_root, &x, &y, &event.state);
369    Tk_TopCoordsToWindow(tkwin, x, y, &event.x, &event.y);
370    event.same_screen = true;
371    event.name = Tk_GetUid(name);
372    Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL);
373}
374
375#pragma mark -
376#pragma mark NSMenu & NSMenuItem Utilities
377
378@implementation NSMenu(TKUtils)
379+ (id)menuWithTitle:(NSString *)title {
380    NSMenu *m = [[self alloc] initWithTitle:title];
381    return [m autorelease];
382}
383+ (id)menuWithTitle:(NSString *)title menuItems:(NSArray *)items {
384    NSMenu *m = [[self alloc] initWithTitle:title];
385    for (NSMenuItem *i in items) {
386	[m addItem:i];
387    }
388    return [m autorelease];
389}
390+ (id)menuWithTitle:(NSString *)title submenus:(NSArray *)submenus {
391    NSMenu *m = [[self alloc] initWithTitle:title];
392    for (NSMenu *i in submenus) {
393	[m addItem:[NSMenuItem itemWithSubmenu:i]];
394    }
395    return [m autorelease];
396}
397- (NSMenuItem *)itemWithSubmenu:(NSMenu *)submenu {
398    return [self itemAtIndex:[self indexOfItemWithSubmenu:submenu]];
399}
400- (NSMenuItem *)itemInSupermenu {
401    NSMenu *supermenu = [self supermenu];
402    return (supermenu ? [supermenu itemWithSubmenu:self] : nil);
403}
404@end
405
406@implementation NSMenuItem(TKUtils)
407+ (id)itemWithSubmenu:(NSMenu *)submenu {
408    NSMenuItem *i = [[self alloc] initWithTitle:[submenu title] action:NULL
409	    keyEquivalent:@""];
410    [i setSubmenu:submenu];
411    return [i autorelease];
412}
413+ (id)itemWithTitle:(NSString *)title submenu:(NSMenu *)submenu {
414    NSMenuItem *i = [[self alloc] initWithTitle:title action:NULL
415	    keyEquivalent:@""];
416    [i setSubmenu:submenu];
417    return [i autorelease];
418}
419+ (id)itemWithTitle:(NSString *)title action:(SEL)action {
420    NSMenuItem *i = [[self alloc] initWithTitle:title action:action
421	    keyEquivalent:@""];
422    [i setTarget:NSApp];
423    return [i autorelease];
424}
425+ (id)itemWithTitle:(NSString *)title action:(SEL)action
426	target:(id)target {
427    NSMenuItem *i = [[self alloc] initWithTitle:title action:action
428	    keyEquivalent:@""];
429    [i setTarget:target];
430    return [i autorelease];
431}
432+ (id)itemWithTitle:(NSString *)title action:(SEL)action
433	keyEquivalent:(NSString *)keyEquivalent {
434    NSMenuItem *i = [[self alloc] initWithTitle:title action:action
435	    keyEquivalent:keyEquivalent];
436    [i setTarget:NSApp];
437    return [i autorelease];
438}
439+ (id)itemWithTitle:(NSString *)title action:(SEL)action
440	target:(id)target keyEquivalent:(NSString *)keyEquivalent {
441    NSMenuItem *i = [[self alloc] initWithTitle:title action:action
442	    keyEquivalent:keyEquivalent];
443    [i setTarget:target];
444    return [i autorelease];
445}
446+ (id)itemWithTitle:(NSString *)title action:(SEL)action
447	keyEquivalent:(NSString *)keyEquivalent
448	keyEquivalentModifierMask:(NSUInteger)keyEquivalentModifierMask {
449    NSMenuItem *i = [[self alloc] initWithTitle:title action:action
450	    keyEquivalent:keyEquivalent];
451    [i setTarget:NSApp];
452    [i setKeyEquivalentModifierMask:keyEquivalentModifierMask];
453    return [i autorelease];
454}
455+ (id)itemWithTitle:(NSString *)title action:(SEL)action
456	target:(id)target keyEquivalent:(NSString *)keyEquivalent
457	keyEquivalentModifierMask:(NSUInteger)keyEquivalentModifierMask {
458    NSMenuItem *i = [[self alloc] initWithTitle:title action:action
459	    keyEquivalent:keyEquivalent];
460    [i setTarget:target];
461    [i setKeyEquivalentModifierMask:keyEquivalentModifierMask];
462    return [i autorelease];
463}
464@end
465
466/*
467 * Local Variables:
468 * mode: c
469 * c-basic-offset: 4
470 * fill-column: 79
471 * coding: utf-8
472 * End:
473 */
474