1/* 2 * tkMacOSXMenus.c -- 3 * 4 * These calls set up and manage the menubar for the 5 * Macintosh version of Tk. 6 * 7 * Copyright (c) 1995-1996 Sun Microsystems, Inc. 8 * Copyright 2001, Apple Computer, Inc. 9 * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net> 10 * 11 * See the file "license.terms" for information on usage and redistribution 12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 * 14 * RCS: @(#) $Id: tkMacOSXMenus.c,v 1.2.2.16 2007/11/09 06:26:56 das Exp $ 15 */ 16 17#include "tkMacOSXPrivate.h" 18 19#define kAppleMenu 256 20#define kAppleAboutItem 1 21#define kFileMenu 2 22#define kEditMenu 3 23 24#define kSourceItem 1 25#define kDemoItem 2 26#define kCloseItem 3 27 28#define EDIT_CUT 1 29#define EDIT_COPY 2 30#define EDIT_PASTE 3 31#define EDIT_CLEAR 4 32 33MenuRef tkAppleMenu; 34MenuRef tkFileMenu; 35MenuRef tkEditMenu; 36 37static Tcl_Interp * gInterp = NULL; /* Standard menu interpreter. */ 38static EventHandlerRef menuEventHandlerRef = NULL; 39 40static void GenerateEditEvent(int flag); 41static Tcl_Obj* GetWidgetDemoPath(Tcl_Interp *interp); 42static OSStatus MenuEventHandlerProc(EventHandlerCallRef callRef, 43 EventRef event, void *userData); 44 45 46/* 47 *---------------------------------------------------------------------- 48 * 49 * GetWidgetDemoPath -- 50 * 51 * Get path to the widget demo. 52 * 53 * Results: 54 * pathObj with ref count 0. 55 * 56 * Side effects: 57 * None. 58 * 59 *---------------------------------------------------------------------- 60 */ 61 62Tcl_Obj* 63GetWidgetDemoPath( 64 Tcl_Interp *interp) 65{ 66 Tcl_Obj *libpath , *result = NULL; 67 68 libpath = Tcl_GetVar2Ex(gInterp, "tk_library", NULL, TCL_GLOBAL_ONLY); 69 if (libpath) { 70 Tcl_Obj *demo[2] = { Tcl_NewStringObj("demos", 5), 71 Tcl_NewStringObj("widget", 6) }; 72 73 Tcl_IncrRefCount(libpath); 74 result = Tcl_FSJoinToPath(libpath, 2, demo); 75 Tcl_DecrRefCount(libpath); 76 } 77 return result; 78} 79 80/* 81 *---------------------------------------------------------------------- 82 * 83 * TkMacOSXHandleMenuSelect -- 84 * 85 * Handles events that occur in the Menu bar. 86 * 87 * Results: 88 * None. 89 * 90 * Side effects: 91 * None. 92 * 93 *---------------------------------------------------------------------- 94 */ 95 96void 97TkMacOSXHandleMenuSelect( 98 MenuID theMenu, 99 MenuItemIndex theItem, 100 int optionKeyPressed) 101{ 102 Tk_Window tkwin; 103 Window window; 104 TkDisplay *dispPtr; 105 106 if (theItem == 0) { 107 TkMacOSXClearMenubarActive(); 108 return; 109 } 110 111 switch (theMenu) { 112 case kAppleMenu: 113 switch (theItem) { 114 case kAppleAboutItem: 115 { 116 Tcl_CmdInfo dummy; 117 if (optionKeyPressed || gInterp == NULL || 118 Tcl_GetCommandInfo(gInterp, 119 "tkAboutDialog", &dummy) == 0) { 120 TkAboutDlg(); 121 } else { 122 if (Tcl_EvalEx(gInterp, "tkAboutDialog", -1, 123 TCL_EVAL_GLOBAL) != TCL_OK) { 124 Tcl_BackgroundError(gInterp); 125 } 126 Tcl_ResetResult(gInterp); 127 } 128 break; 129 } 130 } 131 break; 132 case kFileMenu: 133 switch (theItem) { 134 case kSourceItem: 135 if (gInterp) { 136 if(Tcl_EvalEx(gInterp, "tk_getOpenFile -filetypes {" 137 "{{TCL Scripts} {.tcl} TEXT} " 138 "{{Text Files} {} TEXT}}", -1, TCL_EVAL_GLOBAL) 139 == TCL_OK) { 140 Tcl_Obj *path = Tcl_GetObjResult(gInterp); 141 int len; 142 143 Tcl_GetStringFromObj(path, &len); 144 if (len) { 145 Tcl_IncrRefCount(path); 146 if (Tcl_FSEvalFile(gInterp, path) 147 == TCL_ERROR) { 148 Tcl_BackgroundError(gInterp); 149 } 150 Tcl_DecrRefCount(path); 151 } 152 } 153 Tcl_ResetResult(gInterp); 154 } 155 break; 156 case kDemoItem: 157 if (gInterp) { 158 Tcl_Obj *path = GetWidgetDemoPath(gInterp); 159 160 if (path) { 161 Tcl_IncrRefCount(path); 162 if (Tcl_FSEvalFile(gInterp, path) 163 == TCL_ERROR) { 164 Tcl_BackgroundError(gInterp); 165 } 166 Tcl_DecrRefCount(path); 167 Tcl_ResetResult(gInterp); 168 } 169 } 170 break; 171 case kCloseItem: 172 /* Send close event */ 173 window = TkMacOSXGetXWindow(ActiveNonFloatingWindow()); 174 dispPtr = TkGetDisplayList(); 175 tkwin = Tk_IdToWindow(dispPtr->display, window); 176 TkGenWMDestroyEvent(tkwin); 177 break; 178 } 179 break; 180 case kEditMenu: 181 /* 182 * This implementation just send the keysyms Tk thinks are 183 * associated with function keys that do Cut, Copy & Paste on 184 * a Sun keyboard. 185 */ 186 GenerateEditEvent(theItem); 187 break; 188 default: 189 TkMacOSXDispatchMenuEvent(theMenu, theItem); 190 break; 191 } 192 /* 193 * Finally we unhighlight the menu. 194 */ 195 HiliteMenu(0); 196} 197 198/* 199 *---------------------------------------------------------------------- 200 * 201 * MenuEventHandlerProc -- 202 * 203 * One-time handler of kEventMenuEnableItems for the edit menu. 204 * 205 * Results: 206 * OS status code. 207 * 208 * Side effects: 209 * None. 210 * 211 *---------------------------------------------------------------------- 212 */ 213 214static OSStatus 215MenuEventHandlerProc( 216 EventHandlerCallRef callRef, 217 EventRef event, 218 void *userData) 219{ 220 OSStatus result = eventNotHandledErr, err; 221 int menuContext; 222 223 err = ChkErr(GetEventParameter, event, kEventParamMenuContext, typeUInt32, 224 NULL, sizeof(menuContext), NULL, &menuContext); 225 if (err == noErr && (menuContext & kMenuContextMenuBarTracking)) { 226 if (gInterp) { 227 Tcl_Obj *path = GetWidgetDemoPath(gInterp); 228 229 if (path) { 230 Tcl_IncrRefCount(path); 231 if (Tcl_FSAccess(path, R_OK) == 0) { 232 EnableMenuItem(tkFileMenu, kDemoItem); 233 } 234 Tcl_DecrRefCount(path); 235 } 236 } 237 ChkErr(RemoveEventHandler, menuEventHandlerRef); 238 menuEventHandlerRef = NULL; 239 result = noErr; 240 } 241 242 return result; 243} 244 245/* 246 *---------------------------------------------------------------------- 247 * 248 * TkMacOSXInitMenus -- 249 * 250 * This procedure initializes the Macintosh menu bar. 251 * 252 * Results: 253 * None. 254 * 255 * Side effects: 256 * None. 257 * 258 *---------------------------------------------------------------------- 259 */ 260 261void 262TkMacOSXInitMenus( 263 Tcl_Interp *interp) 264{ 265 OSStatus err; 266 EventHandlerUPP menuEventHandlerUPP; 267 const EventTypeSpec menuEventTypes[] = { 268 {kEventClassMenu, kEventMenuEnableItems}, 269 }; 270 271 gInterp = interp; 272 if (TkMacOSXUseMenuID(kAppleMenu) != TCL_OK) { 273 Tcl_Panic("Menu ID %d is already in use!", kAppleMenu); 274 } 275 err = ChkErr(CreateNewMenu, kAppleMenu, kMenuAttrDoNotUseUserCommandKeys, 276 &tkAppleMenu); 277 if (err != noErr) { 278 Tcl_Panic("CreateNewMenu failed !"); 279 } 280 SetMenuTitle(tkAppleMenu, "\p\024"); 281 InsertMenu(tkAppleMenu, 0); 282 AppendMenu(tkAppleMenu, "\pAbout Tcl & Tk\xc9"); 283 AppendMenu(tkAppleMenu, "\p(-"); 284 285 if (TkMacOSXUseMenuID(kFileMenu) != TCL_OK) { 286 Tcl_Panic("Menu ID %d is already in use!", kFileMenu); 287 } 288 err = ChkErr(CreateNewMenu, kFileMenu, kMenuAttrDoNotUseUserCommandKeys, 289 &tkFileMenu); 290 if (err != noErr) { 291 Tcl_Panic("CreateNewMenu failed !"); 292 } 293 SetMenuTitle(tkFileMenu, "\pFile"); 294 InsertMenu(tkFileMenu, 0); 295 InsertMenuItem(tkFileMenu, "\pSource\xc9", kSourceItem - 1); 296 InsertMenuItem(tkFileMenu, "\pRun Widget Demo", kDemoItem - 1); 297 InsertMenuItem(tkFileMenu, "\pClose/W", kCloseItem - 1); 298 DisableMenuItem(tkFileMenu, kDemoItem); 299 menuEventHandlerUPP = NewEventHandlerUPP(MenuEventHandlerProc); 300 ChkErr(InstallEventHandler, GetMenuEventTarget(tkFileMenu), 301 menuEventHandlerUPP, GetEventTypeCount(menuEventTypes), 302 menuEventTypes, NULL, &menuEventHandlerRef); 303 DisposeEventHandlerUPP(menuEventHandlerUPP); 304 305 if (TkMacOSXUseMenuID(kEditMenu) != TCL_OK) { 306 Tcl_Panic("Menu ID %d is already in use!", kEditMenu); 307 } 308 err = ChkErr(CreateNewMenu, kEditMenu, kMenuAttrDoNotUseUserCommandKeys, 309 &tkEditMenu); 310 if (err != noErr) { 311 Tcl_Panic("CreateNewMenu failed !"); 312 } 313 SetMenuTitle(tkEditMenu, "\pEdit"); 314 InsertMenu(tkEditMenu, 0); 315 AppendMenu(tkEditMenu, "\pCut/X"); 316 AppendMenu(tkEditMenu, "\pCopy/C"); 317 AppendMenu(tkEditMenu, "\pPaste/V"); 318 AppendMenu(tkEditMenu, "\pClear"); 319 if (TkMacOSXUseMenuID(kHMHelpMenuID) != TCL_OK) { 320 Tcl_Panic("Help menu ID %s is already in use!", kHMHelpMenuID); 321 } 322 323 /* 324 * Workaround a Carbon bug with kHICommandPreferences: the first call to 325 * IsMenuKeyEvent returns false for the preferences menu item key shorcut 326 * event (even if the corresponding menu item is dynamically enabled by a 327 * kEventCommandUpdateStatus handler), unless the kHICommandPreferences 328 * menu item has previously been enabled manually. [Bug 1481503] 329 */ 330 EnableMenuCommand(NULL, kHICommandPreferences); 331 332 DrawMenuBar(); 333 return; 334} 335 336/* 337 *---------------------------------------------------------------------- 338 * 339 * GenerateEditEvent -- 340 * 341 * Takes an edit menu item and posts the corasponding a virtual 342 * event to Tk's event queue. 343 * 344 * Results: 345 * None. 346 * 347 * Side effects: 348 * May place events of queue. 349 * 350 *---------------------------------------------------------------------- 351 */ 352 353static void 354GenerateEditEvent( 355 int flag) 356{ 357 XVirtualEvent event; 358 int x, y; 359 Tk_Window tkwin; 360 Window window; 361 TkDisplay *dispPtr; 362 363 window = TkMacOSXGetXWindow(ActiveNonFloatingWindow()); 364 dispPtr = TkGetDisplayList(); 365 tkwin = Tk_IdToWindow(dispPtr->display, window); 366 tkwin = (Tk_Window) ((TkWindow *) tkwin)->dispPtr->focusPtr; 367 if (tkwin == NULL) { 368 return; 369 } 370 371 bzero(&event, sizeof(XVirtualEvent)); 372 event.type = VirtualEvent; 373 event.serial = Tk_Display(tkwin)->request; 374 event.send_event = false; 375 event.display = Tk_Display(tkwin); 376 event.event = Tk_WindowId(tkwin); 377 event.root = XRootWindow(Tk_Display(tkwin), 0); 378 event.subwindow = None; 379 event.time = TkpGetMS(); 380 381 XQueryPointer(NULL, None, NULL, NULL, 382 &event.x_root, &event.y_root, &x, &y, &event.state); 383 tkwin = Tk_TopCoordsToWindow(tkwin, x, y, &event.x, &event.y); 384 event.same_screen = true; 385 386 switch (flag) { 387 case EDIT_CUT: 388 event.name = Tk_GetUid("Cut"); 389 break; 390 case EDIT_COPY: 391 event.name = Tk_GetUid("Copy"); 392 break; 393 case EDIT_PASTE: 394 event.name = Tk_GetUid("Paste"); 395 break; 396 case EDIT_CLEAR: 397 event.name = Tk_GetUid("Clear"); 398 break; 399 } 400 Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); 401} 402