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