1/** <title>NSApplication</title> 2 3 <abstract>The one and only application class.</abstract> 4 5 Copyright (C) 1996-2015 Free Software Foundation, Inc. 6 7 Author: Scott Christley <scottc@net-community.com> 8 Date: 1996 9 Author: Felipe A. Rodriguez <far@ix.netcom.com> 10 Date: August 1998 11 Author: Richard Frith-Macdonald <richard@brainstorm.co.uk> 12 Date: December 1998 13 14 This file is part of the GNUstep GUI Library. 15 16 This library is free software; you can redistribute it and/or 17 modify it under the terms of the GNU Lesser General Public 18 License as published by the Free Software Foundation; either 19 version 2 of the License, or (at your option) any later version. 20 21 This library is distributed in the hope that it will be useful, 22 but WITHOUT ANY WARRANTY; without even the implied warranty of 23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 24 Lesser General Public License for more details. 25 26 You should have received a copy of the GNU Lesser General Public 27 License along with this library; see the file COPYING.LIB. 28 If not, see <http://www.gnu.org/licenses/> or write to the 29 Free Software Foundation, 51 Franklin Street, Fifth Floor, 30 Boston, MA 02110-1301, USA. 31*/ 32 33#import "config.h" 34#include <stdio.h> 35#include <stdlib.h> 36 37#import <Foundation/NSArray.h> 38#import <Foundation/NSAutoreleasePool.h> 39#import <Foundation/NSBundle.h> 40#import <Foundation/NSDebug.h> 41#import <Foundation/NSDictionary.h> 42#import <Foundation/NSError.h> 43#import <Foundation/NSErrorRecoveryAttempting.h> 44#import <Foundation/NSException.h> 45#import <Foundation/NSFileManager.h> 46#import <Foundation/NSInvocation.h> 47#import <Foundation/NSNotification.h> 48#import <Foundation/NSObject.h> 49#import <Foundation/NSPathUtilities.h> 50#import <Foundation/NSProcessInfo.h> 51#import <Foundation/NSRunLoop.h> 52#import <Foundation/NSSet.h> 53#import <Foundation/NSString.h> 54#import <Foundation/NSTimer.h> 55#import <Foundation/NSThread.h> 56#import <Foundation/NSUserDefaults.h> 57#import <Foundation/NSValue.h> 58 59#ifndef LIB_FOUNDATION_LIBRARY 60#import <Foundation/NSConnection.h> 61#endif 62 63#import "AppKit/AppKitExceptions.h" 64#import "AppKit/NSAlert.h" 65#import "AppKit/NSApplication.h" 66#import "AppKit/NSCell.h" 67#import "AppKit/NSCursor.h" 68#import "AppKit/NSDocumentController.h" 69#import "AppKit/NSEvent.h" 70#import "AppKit/NSFontManager.h" 71#import "AppKit/NSImage.h" 72#import "AppKit/NSMenu.h" 73#import "AppKit/NSMenuItem.h" 74#import "AppKit/NSNibLoading.h" 75#import "AppKit/NSPageLayout.h" 76#import "AppKit/NSPanel.h" 77#import "AppKit/NSPasteboard.h" 78#import "AppKit/NSToolbarItem.h" 79#import "AppKit/NSWorkspace.h" 80#import "AppKit/NSScreen.h" 81#import "AppKit/PSOperators.h" 82 83#import "GSIconManager.h" 84#import "GNUstepGUI/GSDisplayServer.h" 85#import "GNUstepGUI/GSServicesManager.h" 86#import "GSGuiPrivate.h" 87#import "GNUstepGUI/GSInfoPanel.h" 88#import "GNUstepGUI/GSVersion.h" 89#import "NSDocumentFrameworkPrivate.h" 90#import "NSToolbarFrameworkPrivate.h" 91 92// minimize icon when suppressed? 93#define MINI_ICON 0 94 95/* The -gui thread. See the comment in initialize_gnustep_backend. */ 96NSThread *GSAppKitThread; 97 98/* Notifications used to implement hide and unhide functionality. */ 99static NSString *GSHideOtherApplicationsNotification 100 = @"GSHideOtherApplicationsNotification"; 101static NSString *GSUnhideAllApplicationsNotification 102 = @"GSUnhideAllApplicationsNotification"; 103 104/* 105 * Base library exception handler 106 */ 107static NSUncaughtExceptionHandler *defaultUncaughtExceptionHandler; 108 109/* 110 * Gui library user friendly exception handler 111 */ 112static void 113_NSAppKitUncaughtExceptionHandler (NSException *exception) 114{ 115 NSInteger retVal; 116 117 /* Reset the exception handler to the Base library's one, to prevent 118 recursive calls to the gui one. */ 119 NSSetUncaughtExceptionHandler(defaultUncaughtExceptionHandler); 120 121 /* 122 * If there is no graphics context to run the alert panel in or 123 * its a severe error, use a non-graphical exception handler 124 */ 125 if (GSCurrentContext() == nil 126 || [[exception name] isEqual: NSWindowServerCommunicationException] 127 || [[exception name] isEqual: GSWindowServerInternalException]) 128 { 129 /* The following will raise again the exception using the base 130 library exception handler */ 131 [exception raise]; 132 } 133 134 retVal = GSRunExceptionPanel 135 ([NSString stringWithFormat: _(@"Critical Error in %@"), 136 [[NSProcessInfo processInfo] processName]], 137 exception, 138 _(@"Abort"), 139 nil, 140#ifdef DEBUG 141 _(@"Debug")); 142#else 143 nil); 144#endif 145 146 /* The user wants to abort */ 147 if (retVal == NSAlertDefault) 148 { 149 /* The following will raise again the exception using the base 150 library exception handler */ 151 [exception raise]; 152 } 153 else 154 { 155 /* Debug button: abort so we can trace the error in gdb */ 156 abort(); 157 } 158} 159 160/* Get the bundle. */ 161NSBundle * 162GSGuiBundle(void) 163{ 164 /* This is the bundle from where we load localization of messages. */ 165 static NSBundle *guiBundle = nil; 166 167 if (!guiBundle) 168 { 169 /* Create the gui bundle we use to localize messages. */ 170 guiBundle = [NSBundle bundleForLibrary: @"gnustep-gui" 171 version: OBJC_STRINGIFY(GNUSTEP_GUI_MAJOR_VERSION.GNUSTEP_GUI_MINOR_VERSION)]; 172 RETAIN(guiBundle); 173 } 174 return guiBundle; 175} 176 177@interface GSBackend : NSObject 178{} 179+ (void) initializeBackend; 180@end 181 182static NSString * 183gnustep_backend_path(NSString *dir, NSString *name) 184{ 185 NSString *path; 186 NSEnumerator *benum; 187 188 NSDebugFLLog(@"BackendBundle", @"Looking for %@", name); 189 190 /* Find the backend framework */ 191 benum = [NSStandardLibraryPaths() objectEnumerator]; 192 while ((path = [benum nextObject])) 193 { 194 path = [path stringByAppendingPathComponent: dir]; 195 path = [path stringByAppendingPathComponent: name]; 196 if ([[NSFileManager defaultManager] fileExistsAtPath: path]) 197 { 198 break; 199 } 200 } 201 return path; 202} 203 204/* Find and load the backend framework, if there is one. The name is 205 taken from a user default containing the name of the backend framework, 206 such as 'GNUstep-back', or simply 'back', or for historical reasons, 207 'libgnustep-back'. */ 208static NSString * 209gnustep_backend_framework(NSString *bundleName) 210{ 211 if (bundleName == nil) 212 bundleName = @"GNUstep_back.framework"; 213 else 214 { 215 if ([bundleName hasPrefix: @"GNUstep-"]) 216 bundleName = [bundleName stringByAppendingString: @".framework"]; 217 else 218 { 219 if ([bundleName hasPrefix: @"libgnustep-"]) 220 { 221 bundleName = [bundleName substringFromIndex: [@"libgnustep-" length]]; 222 } 223 bundleName = [NSString stringWithFormat: @"GNUstep-%@.framework", 224 bundleName]; 225 } 226 } 227 228 return gnustep_backend_path(@"Frameworks", bundleName); 229} 230 231/* Find and load the backend bundle, if there is one. The name is 232 taken from a user default containing the name of the backend bundle, 233 such as 'back', or for historical reasons, 'libgnustep-back'. New 234 versions may also have a version number associated with it. */ 235static NSString * 236gnustep_backend_bundle(NSString *bundleName) 237{ 238 NSString *path, *bundleWithVersion; 239 int version = GNUSTEP_GUI_MAJOR_VERSION * 100 + GNUSTEP_GUI_MINOR_VERSION; 240 241 if (bundleName == nil) 242 { 243 bundleName = @"libgnustep-back"; 244 } 245 else 246 { 247 if ([bundleName hasPrefix: @"libgnustep-"] == NO) 248 { 249 bundleName = [NSString stringWithFormat: @"libgnustep-%@", 250 bundleName]; 251 } 252 } 253 bundleWithVersion = [NSString stringWithFormat: @"%@-%03d.bundle", 254 bundleName, version]; 255 bundleName = [bundleName stringByAppendingString: @".bundle"]; 256 path = gnustep_backend_path(@"Bundles", bundleWithVersion); 257 if (path == nil) 258 { 259 NSLog(@"Did not find correct version of backend (%@), " 260 @"falling back to std (%@).", bundleWithVersion, bundleName); 261 path = gnustep_backend_path(@"Bundles", bundleName); 262 } 263 return path; 264} 265 266BOOL 267initialize_gnustep_backend(void) 268{ 269 static int first = 1; 270 271 if (first) 272 { 273 Class backend; 274 275 /* 276 Remember which thread we are running in. This thread will be the 277 -gui thread, ie. the only thread that may do any rendering. With 278 the exception of a few methods explicitly marked as thread-safe, 279 other threads should not call any methods in -gui. 280 */ 281 GSAppKitThread = [NSThread currentThread]; 282 283 first = 0; 284#ifdef BACKEND_BUNDLE 285 { 286 NSBundle *theBundle; 287 NSString *path, *bundleName; 288 NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; 289 290 /* What backend ? */ 291 bundleName = [defs stringForKey: @"GSBackend"]; 292 path = gnustep_backend_framework (bundleName); 293 if (path == nil) 294 { 295 NSDebugLLog(@"BackendBundle", @"Did not find backend framework."); 296 path = gnustep_backend_bundle (bundleName); 297 } 298 299 /* FIXME/TODO - update localized error messages. */ 300 301 /* Backend found ? */ 302 if (bundleName == nil) 303 bundleName = @"back"; 304 NSCAssert1(path != nil, _(@"Unable to find backend %@"), bundleName); 305 NSDebugLog(@"Loading Backend from %@", path); 306 NSDebugFLLog(@"BackendBundle", @"Loading Backend from %@", path); 307 308 /* Create a bundle object. (Should normally succeed). */ 309 theBundle = [NSBundle bundleWithPath: path]; 310 NSCAssert1(theBundle != nil, 311 _(@"Can't create NSBundle object for backend at path %@"), 312 path); 313 314 /* Now load the object file from the bundle. */ 315 NSCAssert1 ([theBundle load], 316 _(@"Can't load object file from backend at path %@"), 317 path); 318 319 /* Now get the GSBackend class, which should have just been loaded 320 * from the bundle. */ 321 backend = NSClassFromString (@"GSBackend"); 322 NSCAssert1 (backend != Nil, 323 _(@"Backend at path %@ doesn't contain the GSBackend class"), path); 324 [backend initializeBackend]; 325 } 326 327#else 328 /* GSBackend will be in a separate library linked in with the app. 329 This would be cleaner with ...classNamed: @"GSBackend", but that 330 doesn't work in some cases (Mac OS X for instance). */ 331 [GSBackend initializeBackend]; 332#endif 333 } 334 return YES; 335} 336 337void 338gsapp_user_bundles(void) 339{ 340 NSUserDefaults *defs=[NSUserDefaults standardUserDefaults]; 341 NSArray *a=[defs arrayForKey: @"GSAppKitUserBundles"]; 342 NSUInteger i, c; 343 c = [a count]; 344 if (a == nil || c == 0) 345 return; 346 NSLog(@"Loading %d user defined AppKit bundles", (int)c); 347 for (i = 0; i < c; i++) 348 { 349 NSBundle *b = [NSBundle bundleWithPath: [a objectAtIndex: i]]; 350 if (!b) 351 { 352 NSLog(@"* Unable to load '%@'", [a objectAtIndex: i]); 353 continue; 354 } 355 NSLog(@"Loaded '%@'\n", [a objectAtIndex: i]); 356 [[[b principalClass] alloc] init]; 357 } 358} 359 360/* 361 * Types 362 */ 363struct _NSModalSession { 364 NSInteger runState; 365 NSInteger entryLevel; 366 NSWindow *window; 367 NSModalSession previous; 368}; 369 370@interface NSApplication (Private) 371- (void) _appIconInit; 372- (void) _loadAppIconImage; 373- (NSDictionary*) _notificationUserInfo; 374- (void) _openDocument: (NSString*)name; 375- (id) _targetForAction: (SEL)aSelector 376 keyWindow: (NSWindow *)keyWindow 377 mainWindow: (NSWindow *)mainWindow; 378- (void) _windowDidBecomeKey: (NSNotification*) notification; 379- (void) _windowDidBecomeMain: (NSNotification*) notification; 380- (void) _windowDidResignKey: (NSNotification*) notification; 381- (void) _windowWillClose: (NSNotification*) notification; 382- (void) _workspaceNotification: (NSNotification*) notification; 383- (NSArray *) _openFiles; 384- (NSMenu *) _dockMenu; 385@end 386 387@interface NSWindow (TitleWithRepresentedFilename) 388- (BOOL) _hasTitleWithRepresentedFilename; 389@end 390 391@interface NSWindow (ApplicationPrivate) 392- (void) setAttachedSheet: (id) sheet; 393@end 394 395@implementation NSWindow (ApplicationPrivate) 396/** 397 * Associate sheet with the window it's attached to. The window is not retained. 398 */ 399- (void) setAttachedSheet: (id) sheet 400{ 401 _attachedSheet = sheet; 402} 403@end 404 405@interface NSIconWindow : NSWindow 406@end 407 408@interface NSAppIconView : NSView 409- (void) setImage: (NSImage *)anImage; 410@end 411 412@interface NSMenu (HorizontalPrivate) 413- (void) _organizeMenu; 414@end 415 416/* 417 * Class variables 418 */ 419static NSEvent *null_event; 420static Class arpClass; 421static NSNotificationCenter *nc; 422 423NSApplication *NSApp = nil; 424 425@implementation NSIconWindow 426 427- (BOOL) canBecomeMainWindow 428{ 429 return NO; 430} 431 432- (BOOL) canBecomeKeyWindow 433{ 434 return NO; 435} 436 437- (BOOL) becomesKeyOnlyIfNeeded 438{ 439 return YES; 440} 441 442- (BOOL) worksWhenModal 443{ 444 return YES; 445} 446 447- (void) orderWindow: (NSWindowOrderingMode)place relativeTo: (NSInteger)otherWin 448{ 449 if ([[NSUserDefaults standardUserDefaults] 450 boolForKey: @"GSSuppressAppIcon"] == NO) 451 { 452 [super orderWindow: place relativeTo: otherWin]; 453 } 454} 455 456- (void) _initDefaults 457{ 458 [super _initDefaults]; 459 /* Set the title of the window to the process name. Even as the 460 window shows no title bar, the window manager may show it. */ 461 [self setTitle: [[NSProcessInfo processInfo] processName]]; 462 [self setExcludedFromWindowsMenu: YES]; 463 [self setReleasedWhenClosed: NO]; 464 465#if MINI_ICON 466 /* Hack ... 467 * At least one window manager won't miniaturize a window unless 468 * it's at the standard level. If the app icon is suppressed, we 469 * may still want a miniaturised version while the app is hidden. 470 */ 471 if (YES == [[NSUserDefaults standardUserDefaults] 472 boolForKey: @"GSSuppressAppIcon"]) 473 { 474 return; 475 } 476#endif 477 /* App icons and mini windows are displayed at dock level by default. Yet, 478 with the current window level mapping in -back, some window managers 479 will order pop up and context menus behind app icons and mini windows. 480 Therefore, it is possible to have app icons and mini windows displayed 481 at normal window level under control of a user preference. */ 482 // See also NSMiniWindow -_initDefaults in NSWindow.m 483 if ([[NSUserDefaults standardUserDefaults] 484 boolForKey: @"GSAllowWindowsOverIcons"] == YES) 485 _windowLevel = NSDockWindowLevel; 486} 487 488- (void) rightMouseDown: (NSEvent *)theEvent 489{ 490 NSMenu *menu = nil; 491 NSInterfaceStyle style = NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil); 492 493 if (style == NSMacintoshInterfaceStyle || style == NSWindows95InterfaceStyle) 494 { 495 menu = [NSApp _dockMenu]; 496 } 497 if (menu) 498 { 499 [NSMenu popUpContextMenu: menu 500 withEvent: theEvent 501 forView: [self contentView]]; 502 } 503 else 504 { 505 [super rightMouseDown: theEvent]; 506 } 507} 508 509@end 510 511@implementation NSAppIconView 512 513// Class variables 514static NSCell* dragCell = nil; 515static NSCell* tileCell = nil; 516 517static NSSize scaledIconSizeForSize(NSSize imageSize) 518{ 519 NSSize iconSize, retSize; 520 521 iconSize = GSGetIconSize(); 522 retSize.width = imageSize.width * iconSize.width / 64; 523 retSize.height = imageSize.height * iconSize.height / 64; 524 return retSize; 525} 526 527+ (void) initialize 528{ 529 NSImage *tileImage; 530 NSSize iconSize; 531 532 iconSize = GSGetIconSize(); 533 /* _appIconInit will set our image */ 534 dragCell = [[NSCell alloc] initImageCell: nil]; 535 [dragCell setBordered: NO]; 536 537 tileImage = [[GSCurrentServer() iconTileImage] copy]; 538 [tileImage setScalesWhenResized: YES]; 539 [tileImage setSize: iconSize]; 540 tileCell = [[NSCell alloc] initImageCell: tileImage]; 541 RELEASE(tileImage); 542 [tileCell setBordered: NO]; 543} 544 545- (BOOL) acceptsFirstMouse: (NSEvent*)theEvent 546{ 547 return YES; 548} 549 550- (void) concludeDragOperation: (id<NSDraggingInfo>)sender 551{ 552} 553 554- (NSDragOperation) draggingEntered: (id<NSDraggingInfo>)sender 555{ 556 return NSDragOperationGeneric; 557} 558 559- (void) draggingExited: (id<NSDraggingInfo>)sender 560{ 561} 562 563- (NSDragOperation) draggingUpdated: (id<NSDraggingInfo>)sender 564{ 565 return NSDragOperationGeneric; 566} 567 568- (void) drawRect: (NSRect)rect 569{ 570 NSSize iconSize = GSGetIconSize(); 571 572 [tileCell drawWithFrame: NSMakeRect(0, 0, iconSize.width, iconSize.height) 573 inView: self]; 574 [dragCell drawWithFrame: NSMakeRect(0, 0, iconSize.width, iconSize.height) 575 inView: self]; 576 577 if ([NSApp isHidden]) 578 { 579 NSRectEdge mySides[] = {NSMinXEdge, NSMinYEdge, NSMaxXEdge, NSMaxYEdge}; 580 CGFloat myGrays[] = {NSBlack, NSWhite, NSWhite, NSBlack}; 581 NSDrawTiledRects(NSMakeRect(4, 4, 3, 2), rect, mySides, myGrays, 4); 582 } 583} 584 585- (id) initWithFrame: (NSRect)frame 586{ 587 self = [super initWithFrame: frame]; 588 [self registerForDraggedTypes: [NSArray arrayWithObjects: 589 NSFilenamesPboardType, nil]]; 590 return self; 591} 592 593- (void) mouseDown: (NSEvent*)theEvent 594{ 595 if ([theEvent clickCount] >= 2) 596 { 597 /* if not hidden raise windows which are possibly obscured. */ 598 if ([NSApp isHidden] == NO) 599 { 600 NSArray *windows = RETAIN(GSOrderedWindows()); 601 NSWindow *aWin; 602 NSEnumerator *iter = [windows reverseObjectEnumerator]; 603 604 while ((aWin = [iter nextObject])) 605 { 606 if ([aWin isVisible] == YES && [aWin isMiniaturized] == NO 607 && aWin != [NSApp keyWindow] && aWin != [NSApp mainWindow] 608 && aWin != [self window] 609 && ([aWin styleMask] & NSMiniWindowMask) == 0) 610 { 611 [aWin orderFrontRegardless]; 612 } 613 } 614 615 if ([NSApp isActive] == YES) 616 { 617 if ([NSApp keyWindow] != nil) 618 { 619 [[NSApp keyWindow] orderFront: self]; 620 } 621 else if ([NSApp mainWindow] != nil) 622 { 623 [[NSApp mainWindow] makeKeyAndOrderFront: self]; 624 } 625 else 626 { 627 /* We need give input focus to some window otherwise we'll 628 never get keyboard events. FIXME: doesn't work. */ 629 NSWindow *menu_window= [[NSApp mainMenu] window]; 630 NSDebugLLog(@"Focus", 631 @"No key on activation - make menu key"); 632 [GSServerForWindow(menu_window) setinputfocus: 633 [menu_window windowNumber]]; 634 } 635 } 636 637 RELEASE(windows); 638 } 639 [NSApp unhide: self]; // or activate or do nothing. 640 } 641 else 642 { 643 NSPoint lastLocation; 644 NSPoint location; 645 NSUInteger eventMask = NSLeftMouseDownMask | NSLeftMouseUpMask 646 | NSPeriodicMask | NSOtherMouseUpMask | NSRightMouseUpMask; 647 NSDate *theDistantFuture = [NSDate distantFuture]; 648 BOOL done = NO; 649 650 lastLocation = [theEvent locationInWindow]; 651 [NSEvent startPeriodicEventsAfterDelay: 0.02 withPeriod: 0.02]; 652 653 while (!done) 654 { 655 theEvent = [NSApp nextEventMatchingMask: eventMask 656 untilDate: theDistantFuture 657 inMode: NSEventTrackingRunLoopMode 658 dequeue: YES]; 659 660 switch ([theEvent type]) 661 { 662 case NSRightMouseUp: 663 case NSOtherMouseUp: 664 case NSLeftMouseUp: 665 /* any mouse up means we're done */ 666 done = YES; 667 break; 668 case NSPeriodic: 669 location = [_window mouseLocationOutsideOfEventStream]; 670 if (NSEqualPoints(location, lastLocation) == NO) 671 { 672 NSPoint origin = [_window frame].origin; 673 674 origin.x += (location.x - lastLocation.x); 675 origin.y += (location.y - lastLocation.y); 676 [_window setFrameOrigin: origin]; 677 } 678 break; 679 680 default: 681 break; 682 } 683 } 684 [NSEvent stopPeriodicEvents]; 685 } 686} 687 688- (BOOL) prepareForDragOperation: (id<NSDraggingInfo>)sender 689{ 690 return YES; 691} 692 693- (BOOL) performDragOperation: (id<NSDraggingInfo>)sender 694{ 695 NSArray *types; 696 NSPasteboard *dragPb; 697 698 dragPb = [sender draggingPasteboard]; 699 types = [dragPb types]; 700 if ([types containsObject: NSFilenamesPboardType] == YES) 701 { 702 NSArray *names = [dragPb propertyListForType: NSFilenamesPboardType]; 703 NSUInteger index; 704 705 [NSApp activateIgnoringOtherApps: YES]; 706 for (index = 0; index < [names count]; index++) 707 { 708 [NSApp _openDocument: [names objectAtIndex: index]]; 709 } 710 return YES; 711 } 712 return NO; 713} 714 715- (void) setImage: (NSImage *)anImage 716{ 717 NSImage *imgCopy = [anImage copy]; 718 719 if (imgCopy) 720 { 721 NSSize imageSize = [imgCopy size]; 722 723 [imgCopy setScalesWhenResized: YES]; 724 [imgCopy setSize: scaledIconSizeForSize(imageSize)]; 725 } 726 [dragCell setImage: imgCopy]; 727 RELEASE(imgCopy); 728 [self setNeedsDisplay: YES]; 729} 730 731@end 732 733/** 734 * <p>Every graphical GNUstep application has exactly one instance of 735 * <code>NSApplication</code> (or a subclass) instantiated. Usually this is 736 * created through the +sharedApplication method. Once created, this instance 737 * is always accessible through the global variable '<code>NSApp</code>'.</p> 738 * 739 * <p>The NSApplication instance manages the main run loop, dispatches 740 * events, and manages resources. It sets up the connection to the window 741 * server and provides special methods for putting up "modal" (always on top) 742 * windows.</p> 743 * 744 * <p>Typically, -run is called by an application's <code>main</code> method 745 * after the NSApplication instance is created, which never returns. However, 746 * applications needing to integrate other event loops may strategically call 747 * the -stop: method, followed by -run later on.</p> 748 * 749 * <p>To avoid most common needs for subclassing, NSApplication allows you to 750 * specify a <em>delegate</em> that is messaged in particular situations. 751 * See -delegate , -setDelegate: , and [(NSApplicationDelegate)].</p> 752 * 753 * <p><strong>Subclassing</strong> should be a last resort, and delegate 754 * methods should be used in most cases. However, subclassing is most 755 * frequently done to implement custom event loop management by overriding 756 * -run when the method described above is not sufficient, or to intercept 757 * events by overriding -sendEvent: .</p> 758 */ 759@implementation NSApplication 760 761static BOOL _isAutolaunchChecked = NO; 762 763/* 764 * Class methods 765 */ 766+ (void) initialize 767{ 768 if (self == [NSApplication class]) 769 { 770 CREATE_AUTORELEASE_POOL(pool); 771 /* 772 * Dummy functions to fool linker into linking files that contain 773 * only catagories - static libraries seem to have problems here. 774 */ 775 extern void GSStringDrawingDummyFunction(void); 776 777 GSStringDrawingDummyFunction(); 778 779 [self setVersion: 1]; 780 781 /* Cache the NSAutoreleasePool class */ 782 arpClass = [NSAutoreleasePool class]; 783 nc = [NSNotificationCenter defaultCenter]; 784 [pool drain]; 785 } 786} 787 788// Helper method 789+ (void) _invokeWithAutoreleasePool: (NSInvocation*) inv 790{ 791 CREATE_AUTORELEASE_POOL(pool); 792 793 [inv invoke]; 794 [pool drain]; 795} 796 797/** 798 * Calls [NSThread+detachNewThreadSelector:toTarget:withObject:] with the 799 * invocation wrapped by an autorelease pool. 800 */ 801+ (void) detachDrawingThread: (SEL)selector 802 toTarget: (id)target 803 withObject: (id)argument 804{ 805 NSInvocation *inv; 806 807 inv = [NSInvocation 808 invocationWithMethodSignature: 809 [target methodSignatureForSelector: selector]]; 810 [inv setTarget: target]; 811 [inv setSelector: selector]; 812 [inv setArgument: argument atIndex: 2]; 813 [NSThread detachNewThreadSelector: @selector(_invokeWithAutoreleasePool:) 814 toTarget: self 815 withObject: inv]; 816} 817 818/** 819 * <p>Return the shared application instance, creating one (of the 820 * receiver class) if needed. There is (and must always be) only a 821 * single shared application instance for each application. After the 822 * shared application instance has been created, you can access it 823 * directly via the global variable <code>NSApp</code> (but not before!). When 824 * the shared application instance is created, it is also automatically 825 * initialized (that is, its <code>init</code> method is called), which 826 * connects to the window server and prepares the gui library for actual 827 * operation. For this reason, you must always call <code>[NSApplication 828 * sharedApplication]</code> before using any functionality of the gui 829 * library - so, normally, this should be one of the first commands in 830 * your program (if you use <code>NSApplicationMain()</code>, this is 831 * automatically done).</p> 832 * 833 * <p>The shared application instance is normally an instance of 834 * NSApplication; but you can subclass NSApplication, and have an 835 * instance of your own subclass be created and used as the shared 836 * application instance. If you want to get this result, you need to 837 * make sure the first time you call +sharedApplication is on your 838 * custom NSApplication subclass (rather than on NSApplication). 839 * Putting <code>[MyApplicationClass sharedApplication]</code>; as the first 840 * command in your program is the recommended way. :-) If you use 841 * <code>NSApplicationMain()</code>, it automatically creates the appropriate 842 * instance (which you can control by editing the info dictionary of 843 * the application).</p> 844 * 845 * <p>It is not safe to call this method from multiple threads - it would 846 * be useless anyway since the whole library is not thread safe: there 847 * must always be at most one thread using the gui library at a time. 848 * (If you absolutely need to have multiple threads in your 849 * application, make sure only one of them uses the gui [the 'drawing' 850 * thread], and the other ones do not).</p> 851 */ 852+ (NSApplication *) sharedApplication 853{ 854 /* If the global application does not yet exist then create it. */ 855 if (NSApp == nil) 856 { 857 /* -init sets NSApp. */ 858 [[self alloc] init]; 859 } 860 return NSApp; 861} 862 863/* 864 * Instance methods 865 */ 866 867/** 868 * The real gui initialisation ... called from -init 869 */ 870- (void) _init 871{ 872 GSDisplayServer *srv; 873 NSDictionary *attributes; 874 /* Initialization must be enclosed in an autorelease pool. */ 875 CREATE_AUTORELEASE_POOL(_app_init_pool); 876 877 /* 878 * Set NSApp as soon as possible, since other gui classes (which 879 * we refer or use in this method) might be calling [NSApplication 880 * sharedApplication] during their initialization, and we want 881 * those calls to succeed. 882 */ 883 NSApp = self; 884 885 /* Initialize the backend here. */ 886 initialize_gnustep_backend(); 887 888 /* Load user-defined bundles */ 889 gsapp_user_bundles(); 890 891 /* Connect to our window server. */ 892 srv = [GSDisplayServer serverWithAttributes: nil]; 893 RETAIN(srv); 894 [GSDisplayServer setCurrentServer: srv]; 895 896 /* Create a default context with the attributes of the main screen. */ 897 attributes = [[NSScreen mainScreen] deviceDescription]; 898 _default_context = [NSGraphicsContext graphicsContextWithAttributes: attributes]; 899 RETAIN(_default_context); 900 [NSGraphicsContext setCurrentContext: _default_context]; 901 902 /* Initialize font manager. */ 903 [NSFontManager sharedFontManager]; 904 905 _hidden = [[NSMutableArray alloc] init]; 906 _inactive = [[NSMutableArray alloc] init]; 907 _unhide_on_activation = YES; 908 _app_is_hidden = NO; 909 /* Ivar already automatically initialized to NO when the app is 910 created. */ 911 //_app_is_active = NO; 912 //_main_menu = nil; 913 _windows_need_update = YES; 914 915 /* Save the base library exception handler */ 916 defaultUncaughtExceptionHandler = NSGetUncaughtExceptionHandler(); 917 /* Set a new exception handler for the gui library. */ 918 NSSetUncaughtExceptionHandler(_NSAppKitUncaughtExceptionHandler); 919 920 _listener = [GSServicesManager newWithApplication: self]; 921 922 /* NSEvent doesn't use -init so we use +alloc instead of +new. */ 923 _current_event = [NSEvent alloc]; // no current event 924 null_event = [NSEvent alloc]; // create dummy event 925 926 /* We are the end of responder chain. */ 927 [self setNextResponder: nil]; 928 929 /* Create our app icon. 930 NB We are doing this here because WindowMaker will not map the app icon 931 window unless it is the very first window being mapped. */ 932 [self _appIconInit]; 933 934 [_app_init_pool drain]; 935} 936 937 938/** 939 * This method initializes an NSApplication instance. It sets the 940 * shared application instance to be the receiver, and then connects 941 * to the window server and performs the actual gui library 942 * initialization. 943 * 944 * If there is a already a shared application instance, calling this 945 * method results in an assertion (and normally program abortion/crash). 946 * 947 * It is recommended that you /never/ call this method directly from 948 * your code! It's called automatically (and only once) by 949 * [NSApplication sharedApplication]. You might override this method 950 * in subclasses (make sure to call super's :-), then your overridden 951 * method will automatically be called (guaranteed once in the 952 * lifetime of the application) when you call [MyApplicationClass 953 * sharedApplication]. 954 * 955 * If you call this method from your code (which we discourage you 956 * from doing), it is <em>your</em> responsibility to make sure it is called 957 * only once (this is according to the openstep specification). Since 958 * +sharedApplication automatically calls this method, making also 959 * sure it calls it only once, you definitely want to use 960 * +sharedApplication instead of calling -init directly. 961 */ 962- (id) init 963{ 964 /* 965 * As per openstep specification, calling -init twice is a bug in 966 * the program. +sharedApplication automatically makes sure it 967 * never calls -init more than once, and programmers should normally 968 * use +sharedApplication in programs. 969 * 970 * Please refrain from trying to have this method work with multiple 971 * calls (such as returning NSApp instead of raising an assertion). 972 * No matter what you do, you can't protect subclass -init custom 973 * code from multiple executions by changing the implementation here 974 * - so it's just simpler and cleaner that multiple -init executions 975 * are always forbidden, and subclasses inherit exactly the same 976 * kind of multiple execution protection as the superclass has, and 977 * initialization code behaves always in the same way for this class 978 * and for subclasses. 979 */ 980 NSAssert (NSApp == nil, _(@"[NSApplication -init] called more than once")); 981 982 /* 983 * The appkit should run in the main thread ... so to be sure we perform 984 * all the initialisation there. 985 */ 986 [self performSelectorOnMainThread: @selector(_init) 987 withObject: self 988 waitUntilDone: YES]; 989 return NSApp; 990} 991 992/** 993 * <p>Activates the application, sets the application icon, loads the main 994 * Nib file if <code>NSMainNibFile</code> is set in the application 995 * property list, posts an 996 * <code>NSApplicationWillFinishLaunchingNotification</code>, and takes care 997 * of a few other startup tasks. 998 * If you override this method, be sure to call <em>super</em>.</p> 999 * 1000 * <p>The -run method calls this the first time it is called, before starting 1001 * the event loop for the first time.</p> 1002 */ 1003- (void) finishLaunching 1004{ 1005 NSDocumentController *sdc; 1006 NSUserDefaults *defs = [NSUserDefaults standardUserDefaults]; 1007 NSString *filePath; 1008 NSArray *windows_list; 1009 NSUInteger count; 1010 NSUInteger i; 1011 BOOL hadDuplicates = NO; 1012 BOOL didAutoreopen = NO; 1013 NSArray *files = nil; 1014 1015 /* post notification that launch will finish */ 1016 [nc postNotificationName: NSApplicationWillFinishLaunchingNotification 1017 object: self]; 1018 1019 /* Register our listener to incoming services requests etc. */ 1020 [_listener registerAsServiceProvider]; 1021 1022 /* 1023 * Establish the current key and main windows. We need to do this in case 1024 * the windows were created and set to be key/main earlier - before the 1025 * app was active. 1026 */ 1027 windows_list = [self windows]; 1028 count = [windows_list count]; 1029 for (i = 0; i < count; i++) 1030 { 1031 NSWindow *win = [windows_list objectAtIndex: i]; 1032 1033 if ([win isKeyWindow] == YES) 1034 { 1035 if (_key_window == nil) 1036 { 1037 _key_window = win; 1038 } 1039 else 1040 { 1041 hadDuplicates = YES; 1042 NSDebugLog(@"Duplicate keyWindow ignored"); 1043 [win resignKeyWindow]; 1044 } 1045 } 1046 if ([win isMainWindow] == YES) 1047 { 1048 if (_main_window == nil) 1049 { 1050 _main_window = win; 1051 } 1052 else 1053 { 1054 hadDuplicates = YES; 1055 NSDebugLog(@"Duplicate mainWindow ignored"); 1056 [win resignMainWindow]; 1057 } 1058 } 1059 } 1060 1061 /* 1062 * If there was more than one window set as key or main, we must make sure 1063 * that the one we have recorded is the real one by making it become key/main 1064 * again. 1065 */ 1066 if (hadDuplicates) 1067 { 1068 [_main_window resignMainWindow]; 1069 [_main_window becomeMainWindow]; 1070 [_main_window orderFrontRegardless]; 1071 [_key_window resignKeyWindow]; 1072 [_key_window becomeKeyWindow]; 1073 [_key_window orderFrontRegardless]; 1074 } 1075 1076 // Make sure there is one designated main window 1077 if (_main_window == nil) 1078 { 1079 for (i = 0; i < count; i++) 1080 { 1081 NSWindow *win = [windows_list objectAtIndex: i]; 1082 1083 if ([win isVisible] && [win canBecomeMainWindow]) 1084 { 1085 _main_window = win; 1086 break; 1087 } 1088 } 1089 } 1090 1091 /* Register self as observer to window events. */ 1092 [nc addObserver: self selector: @selector(_windowWillClose:) 1093 name: NSWindowWillCloseNotification object: nil]; 1094 [nc addObserver: self selector: @selector(_windowDidBecomeKey:) 1095 name: NSWindowDidBecomeKeyNotification object: nil]; 1096 [nc addObserver: self selector: @selector(_windowDidBecomeMain:) 1097 name: NSWindowDidBecomeMainNotification object: nil]; 1098 [nc addObserver: self selector: @selector(_windowDidResignKey:) 1099 name: NSWindowDidResignKeyNotification object: nil]; 1100 [nc addObserver: self selector: @selector(_windowDidResignMain:) 1101 name: NSWindowDidResignMainNotification object: nil]; 1102 1103 /* register as observer for hide/unhide notifications */ 1104 [[[NSWorkspace sharedWorkspace] notificationCenter] 1105 addObserver: self selector: @selector(_workspaceNotification:) 1106 name: GSHideOtherApplicationsNotification object: nil]; 1107 [[[NSWorkspace sharedWorkspace] notificationCenter] 1108 addObserver: self selector: @selector(_workspaceNotification:) 1109 name: GSUnhideAllApplicationsNotification object: nil]; 1110 1111 // Don't activate the application, when the delegate hid it 1112 if (![self isHidden]) 1113 { 1114 [self activateIgnoringOtherApps: YES]; 1115 } 1116 1117 /* 1118 * Instantiate the NSDocumentController if we are a doc-based app 1119 * and eventually reopen all autosaved documents 1120 */ 1121 sdc = [NSDocumentController sharedDocumentController]; 1122 if ([[sdc documentClassNames] count] > 0) 1123 { 1124 didAutoreopen = [sdc _reopenAutosavedDocuments]; 1125 } 1126 1127 /* 1128 * Now check to see if we were launched with arguments asking to 1129 * open a file. We permit some variations on the default name. 1130 */ 1131 1132 if ((files = [self _openFiles]) != nil) 1133 { 1134 [_listener application: self openFiles: files]; 1135 } 1136 else if ((filePath = [defs stringForKey: @"GSFilePath"]) != nil 1137 || (filePath = [defs stringForKey: @"NSOpen"]) != nil) 1138 { 1139 [_listener application: self openFile: filePath]; 1140 } 1141 else if ((filePath = [defs stringForKey: @"GSTempPath"]) != nil) 1142 { 1143 [_listener application: self openTempFile: filePath]; 1144 } 1145 else if ((filePath = [defs stringForKey: @"NSPrint"]) != nil) 1146 { 1147 [_listener application: self printFile: filePath]; 1148 [self terminate: self]; 1149 } 1150 else if (!didAutoreopen && ![defs boolForKey: @"autolaunch"]) 1151 { 1152 // For document based applications we automatically open a fresh document 1153 // unless denied by the delegate. For non-document based applications we 1154 // open a fresh document only when requested by the delegate. 1155 // Note: We consider an application document based if the shared document 1156 // controller reports at least one editable type. 1157 BOOL docBased = 1158 [[sdc documentClassNames] count] > 0 && [sdc defaultType] != nil; 1159 BOOL shouldOpen = docBased ? YES : NO; 1160 1161 if ([_delegate respondsToSelector: 1162 @selector(applicationShouldOpenUntitledFile:)]) 1163 { 1164 shouldOpen = [_delegate applicationShouldOpenUntitledFile: self]; 1165 } 1166 if (shouldOpen) 1167 { 1168 if (docBased) 1169 { 1170 NSError *err = nil; 1171 if ([sdc openUntitledDocumentAndDisplay: YES error: &err] == nil) 1172 { 1173 [sdc presentError: err]; 1174 } 1175 } 1176 else if ([_delegate respondsToSelector: 1177 @selector(applicationOpenUntitledFile:)]) 1178 { 1179 [_delegate applicationOpenUntitledFile: self]; 1180 } 1181 } 1182 } 1183} 1184 1185/* 1186 * Posts <code>NSApplicationDidFinishLaunchingNotification</code>. 1187 * 1188 * <p>The -run method calls this the first time it is called, before starting 1189 * the event loop for the first time and after calling finishLaunching.</p> 1190 */ 1191- (void) _didFinishLaunching 1192{ 1193 /* finish the launching post notification that launching has finished */ 1194 [nc postNotificationName: NSApplicationDidFinishLaunchingNotification 1195 object: self]; 1196 1197 NS_DURING 1198 { 1199 [[[NSWorkspace sharedWorkspace] notificationCenter] 1200 postNotificationName: NSWorkspaceDidLaunchApplicationNotification 1201 object: [NSWorkspace sharedWorkspace] 1202 userInfo: [self _notificationUserInfo]]; 1203 } 1204 NS_HANDLER 1205 { 1206 NSLog (_(@"Problem during launch app notification: %@"), 1207 [localException reason]); 1208 [localException raise]; 1209 } 1210 NS_ENDHANDLER 1211} 1212 1213- (void) dealloc 1214{ 1215 GSDisplayServer *srv = GSServerForWindow(_app_icon_window); 1216 1217 if (srv == nil) 1218 { 1219 srv = GSCurrentServer(); 1220 } 1221 [[[NSWorkspace sharedWorkspace] notificationCenter] 1222 removeObserver: self]; 1223 [nc removeObserver: self]; 1224 1225 RELEASE(_hidden); 1226 RELEASE(_inactive); 1227 RELEASE(_listener); 1228 RELEASE(null_event); 1229 RELEASE(_current_event); 1230 1231 /* We may need to tidy up nested modal session structures. */ 1232 while (_session != 0) 1233 { 1234 NSModalSession tmp = _session; 1235 1236 _session = tmp->previous; 1237 NSZoneFree(NSDefaultMallocZone(), tmp); 1238 } 1239 1240 /* Release the menus, then set them to nil so we don't try updating 1241 them after they have been deallocated. */ 1242 DESTROY(_main_menu); 1243 DESTROY(_windows_menu); 1244 1245 TEST_RELEASE(_app_icon); 1246 TEST_RELEASE(_app_icon_window); 1247 TEST_RELEASE(_infoPanel); 1248 1249 /* Destroy the default context */ 1250 [NSGraphicsContext setCurrentContext: nil]; 1251 DESTROY(_default_context); 1252 1253 /* Close the server */ 1254 [srv closeServer]; 1255 DESTROY(srv); 1256 1257 [super dealloc]; 1258} 1259 1260/** 1261 * Activate app unconditionally if flag is YES, otherwise only if no other app 1262 * is active. (<em><strong>Note:</strong> this is currently not implemented 1263 * under GNUstep. The app is always activated unconditionally.</em>) Usually 1264 * it is not necessary to manually call this method, except in some 1265 * circumstances of interapplication communication. 1266 */ 1267- (void) activateIgnoringOtherApps: (BOOL)flag 1268{ 1269 if (_isAutolaunchChecked == NO) 1270 { 1271 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 1272 NSString *autolaunch = [defaults objectForKey: @"autolaunch"]; 1273 1274 _isAutolaunchChecked = YES; 1275 1276 /* Application was executed with an argument '-autolaunch YES'. 1277 Do not activate application on first call. */ 1278 if (autolaunch && [autolaunch isEqualToString: @"YES"]) 1279 { 1280 return; 1281 } 1282 } 1283 1284 // TODO: Currently the flag is ignored 1285 if (_app_is_active == NO) 1286 { 1287 NSUInteger count; 1288 NSUInteger i; 1289 NSDictionary *info; 1290 1291 /* 1292 * Menus should observe this notification in order to make themselves 1293 * visible when the application is active. 1294 */ 1295 [nc postNotificationName: NSApplicationWillBecomeActiveNotification 1296 object: self]; 1297 1298 _app_is_active = YES; 1299 1300 if ([[NSUserDefaults standardUserDefaults] 1301 boolForKey: @"GSSuppressAppIcon"]) 1302 { 1303 [_app_icon_window orderOut: self]; 1304 } 1305 1306 /* Make sure to calculate count after the notification, since 1307 inactive status might be changed by a notifiee. */ 1308 count = [_inactive count]; 1309 for (i = 0; i < count; i++) 1310 { 1311 [[_inactive objectAtIndex: i] orderFrontRegardless]; 1312 } 1313 [_inactive removeAllObjects]; 1314 1315 if (_unhide_on_activation) 1316 { 1317 [self unhide: nil]; 1318 } 1319 1320 if ([self keyWindow] == nil && _hidden_key != nil 1321 && [[self windows] indexOfObjectIdenticalTo: _hidden_key] != NSNotFound) 1322 { 1323 [_hidden_key makeKeyWindow]; 1324 _hidden_key = nil; 1325 } 1326 1327 if ([self mainWindow] == nil && _hidden_main != nil 1328 && [[self windows] indexOfObjectIdenticalTo: _hidden_main] != NSNotFound) 1329 { 1330 [_hidden_main makeMainWindow]; 1331 _hidden_main = nil; 1332 } 1333 1334 if ([self keyWindow] != nil) 1335 { 1336 [[self keyWindow] orderFront: self]; 1337 } 1338 else if ([self mainWindow] != nil) 1339 { 1340 [[self mainWindow] makeKeyAndOrderFront: self]; 1341 } 1342 else 1343 { 1344 /* We need give input focus to some window otherwise we'll never get 1345 keyboard events. FIXME: doesn't work. */ 1346 NSWindow *menu_window= [[self mainMenu] window]; 1347 NSDebugLLog(@"Focus", @"No key on activation - make menu key"); 1348 [GSServerForWindow(menu_window) setinputfocus: 1349 [menu_window windowNumber]]; 1350 } 1351 1352 info = [self _notificationUserInfo]; 1353 [nc postNotificationName: NSApplicationDidBecomeActiveNotification 1354 object: self 1355 userInfo: info]; 1356 [[[NSWorkspace sharedWorkspace] notificationCenter] 1357 postNotificationName: NSApplicationDidBecomeActiveNotification 1358 object: [NSWorkspace sharedWorkspace] 1359 userInfo: info]; 1360 } 1361} 1362 1363/** 1364 * Forcefully deactivate the app, without activating another. It is rarely 1365 * necessary to use this method. 1366 */ 1367- (void) deactivate 1368{ 1369 if (_app_is_active == YES) 1370 { 1371 NSArray *windows_list; 1372 NSDictionary *info; 1373 NSWindow *win; 1374 NSEnumerator *iter; 1375 1376 [nc postNotificationName: NSApplicationWillResignActiveNotification 1377 object: self]; 1378 1379 _app_is_active = NO; 1380 1381 if ([self keyWindow] != nil) 1382 { 1383 _hidden_key = [self keyWindow]; 1384 [_hidden_key resignKeyWindow]; 1385 } 1386 // The main window is saved for when the app is activated again. 1387 // This is necessary for menu in window. 1388 if ([self mainWindow] != nil) 1389 { 1390 _hidden_main = [self mainWindow]; 1391 [_hidden_main resignMainWindow]; 1392 } 1393 1394 windows_list = GSOrderedWindows(); 1395 iter = [windows_list reverseObjectEnumerator]; 1396 1397 while ((win = [iter nextObject])) 1398 { 1399 NSModalSession theSession; 1400 1401 if ([win isVisible] == NO) 1402 { 1403 continue; /* Already invisible */ 1404 } 1405 if ([win canHide] == NO) 1406 { 1407 continue; /* Can't be hidden */ 1408 } 1409 if (win == _app_icon_window) 1410 { 1411 continue; /* can't hide the app icon. */ 1412 } 1413 /* Don't order out modal windows */ 1414 theSession = _session; 1415 while (theSession != 0) 1416 { 1417 if (win == theSession->window) 1418 break; 1419 theSession = theSession->previous; 1420 } 1421 if (theSession) 1422 continue; 1423 1424 if ([win hidesOnDeactivate] == YES) 1425 { 1426 /* NB Order is important here. When a hide on deactivate window 1427 is ordered out while the application is inactive, it gets 1428 removed from the _inactive list. Therefore, we must first 1429 order out the window and then add it to the _inactive list. */ 1430 [win orderOut: self]; 1431 [_inactive addObject: win]; 1432 } 1433 } 1434 1435 if (YES == [[NSUserDefaults standardUserDefaults] 1436 boolForKey: @"GSSuppressAppIcon"]) 1437 { 1438#if MINI_ICON 1439 NSRect f = [[[self mainMenu] window] frame]; 1440 NSPoint p = f.origin; 1441 1442 p.y += f.size.height; 1443 [_app_icon_window setFrameTopLeftPoint: p]; 1444 [_app_icon_window orderFrontRegardless]; 1445 [_app_icon_window miniaturize: self]; 1446#else 1447 [_app_icon_window orderFrontRegardless]; 1448#endif 1449 } 1450 1451 info = [self _notificationUserInfo]; 1452 [nc postNotificationName: NSApplicationDidResignActiveNotification 1453 object: self 1454 userInfo: info]; 1455 [[[NSWorkspace sharedWorkspace] notificationCenter] 1456 postNotificationName: NSApplicationDidResignActiveNotification 1457 object: [NSWorkspace sharedWorkspace] 1458 userInfo: info]; 1459 } 1460} 1461 1462/** 1463 * Returns whether this app is the currently active GNUstep application. 1464 * Note that on a GNUstep system, unlike OS X, it is possible for NO GNUstep 1465 * app to be active. 1466 */ 1467- (BOOL) isActive 1468{ 1469 return _app_is_active; 1470} 1471 1472/** 1473 * Cause all other apps to hide themselves. 1474 */ 1475- (void) hideOtherApplications: (id)sender 1476{ 1477 [[[NSWorkspace sharedWorkspace] notificationCenter] 1478 postNotificationName: GSHideOtherApplicationsNotification 1479 object: [NSWorkspace sharedWorkspace] 1480 userInfo: [self _notificationUserInfo]]; 1481} 1482 1483/** 1484 * Cause all apps including this one to unhide themselves. 1485 */ 1486- (void) unhideAllApplications: (id)sender 1487{ 1488 [[[NSWorkspace sharedWorkspace] notificationCenter] 1489 postNotificationName: GSUnhideAllApplicationsNotification 1490 object: [NSWorkspace sharedWorkspace] 1491 userInfo: [self _notificationUserInfo]]; 1492} 1493 1494#define NSLogUncaughtExceptionMask 1 1495#define NSHandleUncaughtExceptionMask 2 1496#define NSLogUncaughtSystemExceptionMask 4 1497#define NSHandleUncaughtSystemExceptionMask 8 1498#define NSLogRuntimeErrorMask 16 1499#define NSLogUncaughtRuntimeErrorMask 32 1500 1501/** 1502 * Private method to handle an exception which occurs in the application runloop. 1503 */ 1504- (void) _handleException: (NSException *)exception 1505{ 1506 NSInteger mask = [[NSUserDefaults standardUserDefaults] integerForKey: @"NSExceptionHandlerMask"]; 1507 1508 /** 1509 * If we are in debug mode, then rethrow the exception so that 1510 * the application can be stopped. 1511 **/ 1512 1513 // log the exception. 1514 if (mask & NSLogUncaughtExceptionMask) 1515 { 1516 [self reportException: exception]; 1517 } 1518 1519 // allow the default handler to handle the exception. 1520 if (mask & NSHandleUncaughtExceptionMask) 1521 { 1522 [exception raise]; 1523 } 1524} 1525 1526/* 1527 * Running the main event loop 1528 */ 1529 1530/** 1531 * <p>This method first calls -finishLaunching (if this is the first time -run) 1532 * has been called, then starts the main event loop of the application which 1533 * continues until -terminate: or -stop: is called.</p> 1534 * 1535 * <p>At each iteration, at most one event is dispatched, the main and services 1536 * menus are sent [NSMenu-update] messages, and -updateWindows is possibly 1537 * called if this has been flagged as necessary.</p> 1538 */ 1539- (void) run 1540{ 1541 NSEvent *e; 1542 id distantFuture = [NSDate distantFuture]; /* Cache this, safe */ 1543 1544 if (_runLoopPool != nil) 1545 { 1546 [NSException raise: NSInternalInconsistencyException 1547 format: @"NSApp's run called recursively"]; 1548 } 1549 1550 /* 1551 * Set this flag here in case the application is actually terminated 1552 * inside -finishLaunching. 1553 */ 1554 _app_is_running = YES; 1555 1556 if (_app_is_launched == NO) 1557 { 1558 _app_is_launched = YES; 1559 IF_NO_GC(_runLoopPool = [arpClass new]); 1560 1561 [self finishLaunching]; 1562 [self _didFinishLaunching]; 1563 1564 [_listener updateServicesMenu]; 1565 [_main_menu update]; 1566 DESTROY(_runLoopPool); 1567 } 1568 1569 while (_app_is_running) 1570 { 1571 IF_NO_GC(_runLoopPool = [arpClass new]); 1572 1573 // Catch and report any uncaught exceptions. 1574 NS_DURING 1575 { 1576 e = [self nextEventMatchingMask: NSAnyEventMask 1577 untilDate: distantFuture 1578 inMode: NSDefaultRunLoopMode 1579 dequeue: YES]; 1580 1581 if (e != nil) 1582 { 1583 NSEventType type = [e type]; 1584 1585 [self sendEvent: e]; 1586 1587 // update (en/disable) the services menu's items 1588 if (type != NSPeriodic && type != NSMouseMoved) 1589 { 1590 [_listener updateServicesMenu]; 1591 [_main_menu update]; 1592 } 1593 } 1594 } 1595 NS_HANDLER 1596 { 1597 [self _handleException: localException]; 1598 } 1599 NS_ENDHANDLER; 1600 1601 DESTROY (_runLoopPool); 1602 } 1603 1604 /* Every single non trivial line of code must be enclosed into an 1605 autorelease pool. Create an autorelease pool here to wrap 1606 synchronize and the NSDebugLog. */ 1607 IF_NO_GC(_runLoopPool = [arpClass new]); 1608 1609 [[NSUserDefaults standardUserDefaults] synchronize]; 1610 DESTROY (_runLoopPool); 1611} 1612 1613/** 1614 * Returns whether the event loop managed by -run is currently active. 1615 */ 1616- (BOOL) isRunning 1617{ 1618 return _app_is_running; 1619} 1620 1621/* 1622 * Running modal event loops 1623 */ 1624 1625/** 1626 * Halts a currently running modal event loop started by -runModalForWindow: 1627 * or -runModalSession: . If you wish to halt the session in response to 1628 * user interaction with the modal window, use -stopModalWithCode: or 1629 * -stopModal instead; only use this to halt the loop from elsewhere, such as 1630 * another thread. 1631 */ 1632- (void) abortModal 1633{ 1634/* FIXME: The previous, now commented out, code here did only work from within the modal loop, 1635 which is contrary to the purpose of this method. Calling stopModalWithCode: works a bit better, 1636 but still relies on the modal loop to cooperate. Calling that method via performSelectorOnMainThread:... 1637 and moving the exception into stopModalWithCode:, looks like another option. Still this would 1638 rely on the loop getting executed. 1639 1640 if (_session == 0) 1641 { 1642 [NSException raise: NSAbortModalException 1643 format: @"abortModal called while not in a modal session"]; 1644 } 1645 [NSException raise: NSAbortModalException format: @"abortModal"]; 1646*/ 1647 [self stopModalWithCode: NSRunAbortedResponse]; 1648} 1649 1650/** 1651 * Set up modal session for theWindow, and, if it is not visible already, 1652 * puts it up on screen, centering it if it is an NSPanel. It is then 1653 * ordered front and made key or main window. 1654 */ 1655- (NSModalSession) beginModalSessionForWindow: (NSWindow*)theWindow 1656{ 1657 NSModalSession theSession; 1658 1659 theSession = (NSModalSession)NSZoneMalloc(NSDefaultMallocZone(), 1660 sizeof(struct _NSModalSession)); 1661 theSession->runState = NSRunContinuesResponse; 1662 theSession->entryLevel = [theWindow level]; 1663 theSession->window = theWindow; 1664 theSession->previous = _session; 1665 _session = theSession; 1666 1667 /* 1668 * Displaying / raising window but centering panel only if not up 1669 * seems to match the behavior on OS X (Panther). 1670 */ 1671 if ([theWindow isKindOfClass: [NSPanel class]]) 1672 { 1673 if ([theWindow isVisible] == NO) 1674 [theWindow center]; 1675 [theWindow setLevel: NSModalPanelWindowLevel]; 1676 } 1677 [theWindow orderFrontRegardless]; 1678 if ([self isActive] == YES) 1679 { 1680 if ([theWindow canBecomeKeyWindow] == YES) 1681 { 1682 [theWindow makeKeyWindow]; 1683 } 1684 else if ([theWindow canBecomeMainWindow] == YES) 1685 { 1686 [theWindow makeMainWindow]; 1687 } 1688 } 1689 1690 return theSession; 1691} 1692 1693/** 1694 * Clean up after a modal session has been run. Called with theSession 1695 * returned from a previous call to beginModalSessionForWindow: . 1696 */ 1697- (void) endModalSession: (NSModalSession)theSession 1698{ 1699 NSModalSession tmp = _session; 1700 NSArray *windows = [self windows]; 1701 1702 if (theSession == 0) 1703 { 1704 [NSException raise: NSInvalidArgumentException 1705 format: @"null pointer passed to endModalSession:"]; 1706 } 1707 /* Remove this session from linked list of sessions. */ 1708 while (tmp != 0 && tmp != theSession) 1709 { 1710 tmp = tmp->previous; 1711 } 1712 if (tmp == 0) 1713 { 1714 [NSException raise: NSInvalidArgumentException 1715 format: @"unknown session passed to endModalSession:"]; 1716 } 1717 while (_session != theSession) 1718 { 1719 tmp = _session; 1720 _session = tmp->previous; 1721 if ([windows indexOfObjectIdenticalTo: tmp->window] != NSNotFound) 1722 { 1723 [tmp->window setLevel: tmp->entryLevel]; 1724 } 1725 NSZoneFree(NSDefaultMallocZone(), tmp); 1726 } 1727 _session = _session->previous; 1728 if ([windows indexOfObjectIdenticalTo: theSession->window] != NSNotFound) 1729 { 1730 [theSession->window setLevel: theSession->entryLevel]; 1731 } 1732 NSZoneFree(NSDefaultMallocZone(), theSession); 1733 1734 // Bring the next modal window to front 1735 if ((_session != 0) && 1736 ([windows indexOfObjectIdenticalTo: _session->window] != NSNotFound)) 1737 { 1738 NSWindow *theWindow = _session->window; 1739 1740 // Same code as in beginModalSessionForWindow: 1741 [theWindow orderFrontRegardless]; 1742 if ([self isActive] == YES) 1743 { 1744 if ([theWindow canBecomeKeyWindow] == YES) 1745 { 1746 [theWindow makeKeyWindow]; 1747 } 1748 else if ([theWindow canBecomeMainWindow] == YES) 1749 { 1750 [theWindow makeMainWindow]; 1751 } 1752 } 1753 } 1754 else 1755 { 1756 [_main_menu update]; 1757 } 1758} 1759 1760/** 1761 * Starts modal event loop for given window, after calling 1762 * -beginModalSessionForWindow:. Loop is broken only by -stopModal , 1763 * -stopModalWithCode: , or -abortModal , at which time -endModalSession: 1764 * is called automatically. 1765 */ 1766- (NSInteger) runModalForWindow: (NSWindow*)theWindow 1767{ 1768 NSModalSession theSession = 0; 1769 NSInteger code = NSRunContinuesResponse; 1770 1771 NS_DURING 1772 { 1773 NSDate *limit; 1774 GSDisplayServer *srv; 1775 1776 theSession = [self beginModalSessionForWindow: theWindow]; 1777 limit = [NSDate distantFuture]; 1778 srv = GSCurrentServer(); 1779 1780 while (code == NSRunContinuesResponse) 1781 { 1782 /* 1783 * Try to handle events for this session, discarding others. 1784 */ 1785 code = [self runModalSession: theSession]; 1786 if (code == NSRunContinuesResponse) 1787 { 1788 /* 1789 * Wait until there are more events to handle. 1790 */ 1791 DPSPeekEvent(srv, NSAnyEventMask, limit, NSModalPanelRunLoopMode); 1792 } 1793 } 1794 1795 [self endModalSession: theSession]; 1796 } 1797 NS_HANDLER 1798 { 1799 if (theSession != 0) 1800 { 1801 NSWindow *win_to_close = theSession->window; 1802 1803 [self endModalSession: theSession]; 1804 [win_to_close close]; 1805 } 1806 if ([[localException name] isEqual: NSAbortModalException] == NO) 1807 { 1808 [localException raise]; 1809 } 1810 code = NSRunAbortedResponse; 1811 } 1812 NS_ENDHANDLER 1813 1814 return code; 1815} 1816 1817/** 1818<p> 1819Processes any events for a modal session described by the theSession 1820variable. When finished, it returns the state of the session (i.e. 1821whether it is still running or has been stopped, etc) 1822</p> 1823<p>If there are no pending events for the session, this method returns 1824immediately. 1825</p> 1826<p> 1827 Although Apple's docs state that, before processing the events, it makes the 1828 session window key and orders the window front, this method does not attempt 1829 to do this, because: 1) we don't want to interfere with use of other apps 1830 during modal session for this app; 2) occasionally other windows are active 1831 and should be usable during modal sessions (e.g., a popup dialog from a modal 1832 window); 3) most of the time -beginModalSessionForWindow: will have been 1833 called in advance. If the latter is not the case, you may need to order the 1834 window front yourself in advance. 1835</p> 1836<p> 1837See Also: -runModalForWindow: 1838</p> 1839*/ 1840- (NSInteger) runModalSession: (NSModalSession)theSession 1841{ 1842 NSAutoreleasePool *pool; 1843 GSDisplayServer *srv; 1844 BOOL done = NO; 1845 NSEvent *event; 1846 NSDate *limit; 1847 1848 if (theSession != _session) 1849 { 1850 [NSException raise: NSInvalidArgumentException 1851 format: @"runModalSession: with wrong session"]; 1852 } 1853 1854 // Use the default context for all events. 1855 srv = GSCurrentServer(); 1856 1857 // Only handle input which is already available. 1858 limit = [NSDate distantPast]; 1859 1860 /* 1861 * Deal with the events in the queue. 1862 */ 1863 while (done == NO && theSession->runState == NSRunContinuesResponse) 1864 { 1865 IF_NO_GC(pool = [arpClass new]); 1866 1867 event = DPSGetEvent(srv, NSAnyEventMask, limit, NSModalPanelRunLoopMode); 1868 if (event != nil) 1869 { 1870 NSWindow *eventWindow = [event window]; 1871 1872 /* 1873 * We handle events for the session window, events for any 1874 * window which works when modal, and any window management 1875 * events. All others are ignored/discarded. 1876 */ 1877 if (eventWindow == theSession->window 1878 || [eventWindow worksWhenModal] == YES 1879 || [event type] == NSAppKitDefined) 1880 { 1881 ASSIGN(_current_event, event); 1882 } 1883 else 1884 { 1885 event = nil; // Ignore/discard this event. 1886 } 1887 } 1888 else 1889 { 1890 done = YES; // No more events pending. 1891 } 1892 1893 if (event != nil) 1894 { 1895 NSEventType type = [_current_event type]; 1896 1897 [self sendEvent: _current_event]; 1898 1899 // update (en/disable) the services menu's items 1900 if (type != NSPeriodic && type != NSMouseMoved) 1901 { 1902 [_listener updateServicesMenu]; 1903 [_main_menu update]; 1904 } 1905 1906 /* 1907 * Check to see if the window has gone away - if so, end session. 1908 */ 1909 if ([[self windows] indexOfObjectIdenticalTo: _session->window] 1910 == NSNotFound 1911 || ![_session->window isVisible]) 1912 { 1913 [self stopModal]; 1914 } 1915 } 1916 [pool drain]; 1917 } 1918 1919 NSAssert(_session == theSession, @"Session was changed while running"); 1920 1921 return theSession->runState; 1922} 1923 1924/** 1925<p> 1926 Returns the window that is part of the current modal session, if any. 1927</p> 1928<p> 1929See -runModalForWindow: 1930</p> 1931*/ 1932- (NSWindow *) modalWindow 1933{ 1934 if (_session != 0) 1935 return (_session->window); 1936 else 1937 return nil; 1938} 1939 1940/** 1941 * Stops the main run loop, as well as a modal session if it is running. 1942 */ 1943- (void) stop: (id)sender 1944{ 1945 if (_session != 0) 1946 [self stopModal]; 1947 else 1948 { 1949 _app_is_running = NO; 1950 /* 1951 * add dummy event to queue to assure loop cycles 1952 * at least one more time 1953 */ 1954 DPSPostEvent(GSCurrentServer(), null_event, NO); 1955 } 1956} 1957 1958/**<p> Stops a running modal session causing -runModalForWindow: or 1959 * -runModalSession: to return <code>NSRunStoppedResponse</code>. Use this 1960 * or -stopModalWithCode: to end a modal session in response to user input.</p> 1961 * <p>See Also: -stopModalWithCode:</p> 1962 */ 1963- (void) stopModal 1964{ 1965 [self stopModalWithCode: NSRunStoppedResponse]; 1966} 1967 1968/** 1969 * Stops a running modal session causing -runModalForWindow: or 1970 * -runModalSession: to return the specified integer code. Use this 1971 * or -stopModal to end a modal session in response to user input. 1972 */ 1973- (void) stopModalWithCode: (NSInteger)returnCode 1974{ 1975 /* According to the spec, there is no exception which is thrown 1976 * if we are not currently in a modal session. While it is not 1977 * good practice to call this when we're not, we shouldn't throw 1978 * an exception. 1979 */ 1980 if (_session != 0 && _session->runState == NSRunContinuesResponse) 1981 { 1982 if (returnCode == NSRunContinuesResponse) 1983 { 1984 [NSException raise: NSInvalidArgumentException 1985 format: @"stopModalWithCode: with NSRunContinuesResponse"]; 1986 } 1987 _session->runState = returnCode; 1988 } 1989} 1990 1991/** 1992 * Put up a modal window centered relative to docWindow. On OS X this is 1993 * deprecated in favor of 1994 * -beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo: . 1995 */ 1996- (NSInteger) runModalForWindow: (NSWindow *)theWindow 1997 relativeToWindow: (NSWindow *)docWindow 1998{ 1999 if ((docWindow != nil) && (theWindow != nil)) 2000 { 2001 NSRect docFrame = [docWindow frame]; 2002 NSPoint point = docFrame.origin; 2003 NSRect theFrame = [theWindow frame]; 2004 NSSize size = theFrame.size; 2005 2006 // Calculate window position... 2007 point.x += (docFrame.size.width - size.width) / 2; 2008 point.y += (docFrame.size.height - size.height) / 2; 2009 2010 NSDebugLLog(@"NSWindow", @"Positioning window %@ relative to %@ at %@", 2011 NSStringFromRect(theFrame), NSStringFromRect(docFrame), NSStringFromPoint(point)); 2012 // Position window... 2013 [theWindow setFrameOrigin: point]; 2014 } 2015 [theWindow orderWindow: NSWindowAbove 2016 relativeTo: [docWindow windowNumber]]; 2017 return [self runModalForWindow: theWindow]; 2018} 2019 2020/** 2021 * Put up a modal sheet sliding down from top of docWindow. If modalDelegate 2022 * is non-nil and responds to didEndSelector (this is optional), it is invoked 2023 * after the session ends. The selector should take three arguments: 2024 * NSWindow *, int, void *. It is passed the sheet window, the return code, 2025 * and the contextInfo passed in here. 2026 2027 * <em>Under GNUstep, the sheet aspect is not implemented (just centers 2028 * window on the screen), but modalDelegate didEndSelector is called if 2029 * both non-nil.</em> 2030 */ 2031- (void) beginSheet: (NSWindow *)sheet 2032 modalForWindow: (NSWindow *)docWindow 2033 modalDelegate: (id)modalDelegate 2034 didEndSelector: (SEL)didEndSelector 2035 contextInfo: (void *)contextInfo 2036{ 2037 // FIXME 2038 NSInteger ret; 2039 2040 [sheet setParentWindow: docWindow]; 2041 [docWindow setAttachedSheet: sheet]; 2042 2043 [[NSNotificationCenter defaultCenter] 2044 postNotificationName: NSWindowWillBeginSheetNotification 2045 object: docWindow]; 2046 ret = [self runModalForWindow: sheet 2047 relativeToWindow: docWindow]; 2048 2049 if (modalDelegate && [modalDelegate respondsToSelector: didEndSelector]) 2050 { 2051 void (*didEnd)(id, SEL, id, NSInteger, void*); 2052 2053 didEnd = (void (*)(id, SEL, id, NSInteger, void*))[modalDelegate methodForSelector: 2054 didEndSelector]; 2055 didEnd(modalDelegate, didEndSelector, sheet, ret, contextInfo); 2056 } 2057 2058 [sheet close]; 2059 [docWindow setAttachedSheet: nil]; 2060 [sheet setParentWindow: nil]; 2061 [[NSNotificationCenter defaultCenter] 2062 postNotificationName: NSWindowDidEndSheetNotification 2063 object: docWindow]; 2064} 2065 2066/** 2067 * Analogous to -stopModal for sheets. 2068 */ 2069- (void) endSheet: (NSWindow *)sheet 2070{ 2071 // FIXME 2072 [self stopModal]; 2073} 2074 2075/** 2076 * Analogous to -stopModalWithCode: for sheets. 2077 */ 2078- (void) endSheet: (NSWindow *)sheet 2079 returnCode: (NSInteger)returnCode 2080{ 2081 // FIXME 2082 [self stopModalWithCode: returnCode]; 2083} 2084 2085 2086/* 2087 * Getting, removing, and posting events 2088 */ 2089 2090/* Private method used by GSDragView to dispatch drag events as Cocoa does. */ 2091- (void) _postAndSendEvent: (NSEvent *)anEvent 2092{ 2093 ASSIGN(_current_event, anEvent); 2094 [self sendEvent: anEvent]; 2095} 2096 2097/** 2098 * Called by -run to dispatch events that are received according to AppKit's 2099 * forwarding conventions. You rarely need to invoke this directly. If you 2100 * want to synthesize an event for processing, call -postEvent:atStart: . 2101 */ 2102- (void) sendEvent: (NSEvent *)theEvent 2103{ 2104 NSEventType type; 2105 2106 type = [theEvent type]; 2107 switch (type) 2108 { 2109 case NSPeriodic: /* NSApplication traps the periodic events */ 2110 break; 2111 2112 case NSKeyDown: 2113 { 2114 NSDebugLLog(@"NSEvent", @"send key down event\n"); 2115 /* Key equivalents must be looked up explicitly in the Services menu 2116 after checking the main menu, as NSMenu's -performKeyEquivalent: 2117 ignores the Services menu. See the comment in that method for a 2118 rationale. */ 2119 if ([[self keyWindow] performKeyEquivalent: theEvent] == NO 2120 && [[self mainMenu] performKeyEquivalent: theEvent] == NO 2121 && [[self servicesMenu] performKeyEquivalent: theEvent] == NO) 2122 { 2123 [[theEvent window] sendEvent: theEvent]; 2124 } 2125 break; 2126 } 2127 2128 case NSKeyUp: 2129 { 2130 NSDebugLLog(@"NSEvent", @"send key up event\n"); 2131 [[theEvent window] sendEvent: theEvent]; 2132 break; 2133 } 2134 2135 default: /* pass all other events to the event's window */ 2136 { 2137 NSWindow *window = [theEvent window]; 2138 2139 if (!theEvent) 2140 NSDebugLLog(@"NSEvent", @"NSEvent is nil!\n"); 2141 if (type == NSMouseMoved) 2142 NSDebugLLog(@"NSMotionEvent", @"Send move (%d) to %@", 2143 (int)type, window); 2144 else 2145 NSDebugLLog(@"NSEvent", @"Send NSEvent type: %@ to %@", 2146 theEvent, window); 2147 if (window) 2148 [window sendEvent: theEvent]; 2149 else if (type == NSRightMouseDown) 2150 [self rightMouseDown: theEvent]; 2151 } 2152 } 2153} 2154 2155/** 2156 * Returns the most recent event -run pulled off the event queue. 2157 */ 2158- (NSEvent*) currentEvent 2159{ 2160 return _current_event; 2161} 2162 2163/* Utility for pen-device input. See NSResponder. */ 2164- (BOOL) shouldBeTreatedAsInkEvent: (NSEvent *)theEvent 2165{ 2166 return [[theEvent window] shouldBeTreatedAsInkEvent: theEvent]; 2167} 2168 2169/** 2170 * Drop events matching mask from the queue, before but not including 2171 * lastEvent. The mask is a bitwise AND of event mask constants. 2172 * See (EventType) . Use <code>NSAnyEventMask</code> to discard everything 2173 * up to lastEvent. 2174 */ 2175- (void) discardEventsMatchingMask: (NSUInteger)mask 2176 beforeEvent: (NSEvent *)lastEvent 2177{ 2178 DPSDiscardEvents(GSCurrentServer(), mask, lastEvent); 2179} 2180 2181/** 2182 * Return the next event matching mask from the queue, dequeuing if flag is 2183 * YES. Intervening events are NOT dequeued. If no matching event is on the 2184 * queue, will wait for one until expiration, returning nil if none found. 2185 * See (EventType) for the list of masks. 2186 */ 2187- (NSEvent*) nextEventMatchingMask: (NSUInteger)mask 2188 untilDate: (NSDate*)expiration 2189 inMode: (NSString*)mode 2190 dequeue: (BOOL)flag 2191{ 2192 NSEvent *event; 2193 2194 if (_windows_need_update) 2195 { 2196 [self updateWindows]; 2197 } 2198 if (!expiration) 2199 expiration = [NSDate distantPast]; 2200 2201 if (flag) 2202 event = DPSGetEvent(GSCurrentServer(), mask, expiration, mode); 2203 else 2204 event = DPSPeekEvent(GSCurrentServer(), mask, expiration, mode); 2205 2206 if (event == null_event) 2207 { 2208 // Never return the null_event 2209 event = nil; 2210 } 2211 2212 if (event) 2213 { 2214IF_NO_GC(NSAssert([event retainCount] > 0, NSInternalInconsistencyException)); 2215 /* 2216 * If we are not in a tracking loop, we may want to unhide a hidden 2217 * because the mouse has been moved. 2218 */ 2219 if (mode != NSEventTrackingRunLoopMode) 2220 { 2221 _windows_need_update = YES; 2222 if ([NSCursor isHiddenUntilMouseMoves]) 2223 { 2224 NSEventType type = [event type]; 2225 2226 if ((type == NSLeftMouseDown) || (type == NSLeftMouseUp) 2227 || (type == NSOtherMouseDown) || (type == NSOtherMouseUp) 2228 || (type == NSRightMouseDown) || (type == NSRightMouseUp) 2229 || (type == NSMouseMoved)) 2230 { 2231 [NSCursor setHiddenUntilMouseMoves: NO]; 2232 } 2233 } 2234 } 2235 2236 if (flag) 2237 ASSIGN(_current_event, event); 2238 } 2239 return event; 2240} 2241 2242/** 2243 * Add an event to be processed by the app, either at end or at beginning 2244 * (next up) if flag is YES. This provides a way to provide synthetic input 2245 * to an application, or, for whatever reason, to allow a real event to be 2246 * re-dispatched. 2247 */ 2248- (void) postEvent: (NSEvent *)event atStart: (BOOL)flag 2249{ 2250 DPSPostEvent(GSCurrentServer(), event, flag); 2251} 2252 2253/** 2254 * Sends the aSelector message to the receiver returned by the 2255 * -targetForAction:to:from: method (to which the aTarget and sender 2256 * arguments are passed).<br /> 2257 * The method in the receiver must expect a single argument ... 2258 * the sender.<br /> 2259 * Any value returned by the method in the receiver is ignored.<br /> 2260 * This method returns YES on success, NO on failure (when no receiver 2261 * can be found for aSelector). 2262 */ 2263- (BOOL) sendAction: (SEL)aSelector to: (id)aTarget from: (id)sender 2264{ 2265 id resp = [self targetForAction: aSelector to: aTarget from: sender]; 2266 2267 if (resp != nil) 2268 { 2269 IMP actionIMP = [resp methodForSelector: aSelector]; 2270 2271 if (0 != actionIMP) 2272 { 2273 actionIMP(resp, aSelector, sender); 2274 return YES; 2275 } 2276 } 2277 2278 return NO; 2279} 2280 2281/** 2282 * If theTarget responds to theAction it is returned, otherwise 2283 * the application searches for an object which will handle 2284 * theAction and returns the first object found.<br /> 2285 * Returns nil on failure. 2286 */ 2287- (id) targetForAction: (SEL)theAction to: (id)theTarget from: (id)sender 2288{ 2289 /* 2290 * If target responds to the selector then have it perform it. 2291 */ 2292 if (theTarget) 2293 { 2294 if ([theTarget respondsToSelector: theAction]) 2295 { 2296 return theTarget; 2297 } 2298 else 2299 { 2300 NSDebugLog(@"Target %@ does not respont to action %@", theTarget, NSStringFromSelector(theAction)); 2301 return nil; 2302 } 2303 } 2304 else if ([sender isKindOfClass: [NSToolbarItem class]]) 2305 { 2306 /* Special case for toolbar items which must look up the target in the 2307 responder chain of the window containing their toolbar not in the key 2308 or main window. 2309 Note: If (and only if) the toolbar's window is key window we must 2310 pass it as such to _targetForAction:... so that toolbar items in a 2311 modal dialog panel work. 2312 */ 2313 NSWindow *toolbarWindow = 2314 [[[(NSToolbarItem *)sender toolbar] _toolbarView] window]; 2315 NSWindow *keyWindow = [self keyWindow]; 2316 if (keyWindow != toolbarWindow) 2317 keyWindow = nil; 2318 return [self _targetForAction: theAction 2319 keyWindow: keyWindow 2320 mainWindow: toolbarWindow]; 2321 } 2322 else 2323 { 2324 return [self targetForAction: theAction]; 2325 } 2326} 2327 2328/** 2329 * <p> 2330 * Returns the target object that will respond to aSelector, if any. The 2331 * method first checks if any of the key window's first responders, the 2332 * key window or its delegate responds. Next it checks the main window in 2333 * the same way. Finally it checks the receiver (NSApplication) and its 2334 * delegate. 2335 * </p> 2336 */ 2337- (id) targetForAction: (SEL)aSelector 2338{ 2339 if (!aSelector) 2340 { 2341 return nil; 2342 } 2343 2344 /* During a modal session actions must not be sent to the main window of 2345 * the application, but rather to the dialog window of the modal session. 2346 * Note that the modal session window is not necessarily the key window, 2347 * as a panel with worksWhenModal = YES, e.g., the font panel, can still 2348 * become key window during a modal session. 2349 */ 2350 NSWindow *mainWindow = [self mainWindow]; 2351 if (_session != 0) 2352 mainWindow = _session->window; 2353 return [self _targetForAction: aSelector 2354 keyWindow: [self keyWindow] 2355 mainWindow: mainWindow]; 2356} 2357 2358 2359/** 2360 * Attempts to perform aSelector using [NSResponder-tryToPerform:with:] 2361 * and if that is not possible, attempts to get the application 2362 * delegate to perform the aSelector.<br /> 2363 * Returns YES if an object was found to perform aSelector, NO otherwise. 2364 */ 2365- (BOOL) tryToPerform: (SEL)aSelector with: (id)anObject 2366{ 2367 if ([super tryToPerform: aSelector with: anObject] == YES) 2368 { 2369 return YES; 2370 } 2371 if (_delegate != nil && [_delegate respondsToSelector: aSelector]) 2372 { 2373 IMP actionIMP = [_delegate methodForSelector: aSelector]; 2374 if (0 != actionIMP) 2375 { 2376 actionIMP(_delegate, aSelector, anObject); 2377 return YES; 2378 } 2379 } 2380 return NO; 2381} 2382 2383/**<p>Sets the application's icon. Any windows that use the old application 2384icon image as their mini window image will be updated to use the new 2385image.</p><p>See Also: -applicationIconImage</p> 2386*/ 2387- (void) setApplicationIconImage: (NSImage*)anImage 2388{ 2389 NSEnumerator *iterator; 2390 NSWindow *current; 2391 NSImage *old_app_icon = _app_icon; 2392 NSSize miniWindowSize; 2393 NSSize imageSize; 2394 GSDisplayServer *server; 2395 2396 // Ignore attempts to set nil as the icon image. 2397 if (nil == anImage) 2398 return; 2399 2400 RETAIN(old_app_icon); 2401 2402 // Use a copy as we change the name and size 2403 ASSIGNCOPY(_app_icon, anImage); 2404 2405 server = GSCurrentServer(); 2406 miniWindowSize = server != 0 ? [server iconSize] : NSZeroSize; 2407 if (miniWindowSize.width <= 0 || miniWindowSize.height <= 0) 2408 { 2409 miniWindowSize = NSMakeSize(48, 48); 2410 } 2411 2412 // restrict size when the icon is larger than the mini window. 2413 imageSize = [_app_icon size]; 2414 if (imageSize.width > miniWindowSize.width 2415 || imageSize.height > miniWindowSize.height) 2416 { 2417 [_app_icon setSize: miniWindowSize]; 2418 } 2419 2420 // Let horizontal menu change icon 2421 [_main_menu _organizeMenu]; 2422 2423 if (_app_icon_window != nil) 2424 { 2425 [(NSAppIconView *)[_app_icon_window contentView] setImage: _app_icon]; 2426 } 2427 2428 // Swap the old image for the new one wherever it's used 2429 iterator = [[self windows] objectEnumerator]; 2430 while ((current = [iterator nextObject]) != nil) 2431 { 2432 if ([current miniwindowImage] == old_app_icon) 2433 [current setMiniwindowImage: _app_icon]; 2434 } 2435 2436 DESTROY(old_app_icon); 2437} 2438 2439/**<p>Returns the current icon be used for the application.</p> 2440 <p>See Also: -setApplicationIconImage:</p> 2441 */ 2442- (NSImage*) applicationIconImage 2443{ 2444 if (!_app_icon) 2445 { 2446 /* load the application icon */ 2447 [self _loadAppIconImage]; 2448 } 2449 return _app_icon; 2450} 2451 2452/** 2453 * Returns the actual window object being used to display the application 2454 * icon (usually in the dock). 2455 */ 2456- (NSWindow*) iconWindow 2457{ 2458 return _app_icon_window; 2459} 2460 2461/* 2462 * Hiding and arranging windows 2463 */ 2464 2465/**<p> Request this application to "hide" (unmap all windows from the screen 2466 * except the icon window). Posts 2467 * <code>NSApplicationWillHideNotification</code> and 2468 * <code>NSApplicationDidHideNotification</code>. On OS X this activates 2469 * the next app that is running, however on GNUstep this is up to the window 2470 * manager.</p><p>See Also: -unhide: -isHidden</p> 2471 */ 2472- (void) hide: (id)sender 2473{ 2474#ifdef __MINGW32__ 2475 [self miniaturizeAll: sender]; 2476#else 2477 if (_app_is_hidden == NO) 2478 { 2479 if (![[NSUserDefaults standardUserDefaults] 2480 boolForKey: @"GSSuppressAppIcon"]) 2481 { 2482 NSArray *windows_list; 2483 NSDictionary *info; 2484 NSWindow *win; 2485 NSEnumerator *iter; 2486 2487 [nc postNotificationName: NSApplicationWillHideNotification 2488 object: self]; 2489 2490 if ([self keyWindow] != nil) 2491 { 2492 _hidden_key = [self keyWindow]; 2493 [_hidden_key resignKeyWindow]; 2494 } 2495 2496 // The main window is saved for when the app is activated again. 2497 // This is necessary for menu in window. 2498 if ([self mainWindow] != nil) 2499 { 2500 _hidden_main = [self mainWindow]; 2501 [_hidden_main resignMainWindow]; 2502 } 2503 2504 /** Ask the window manager to hide all the application windows for us. 2505 Return whether they have been hidden. */ 2506 win = [[self mainMenu] window]; 2507 if ([GSServerForWindow(win) hideApplication: [win windowNumber]] == NO) 2508 { 2509 windows_list = GSOrderedWindows(); 2510 iter = [windows_list reverseObjectEnumerator]; 2511 2512 while ((win = [iter nextObject])) 2513 { 2514 if ([win isVisible] == NO && ![win isMiniaturized]) 2515 { 2516 continue; /* Already invisible */ 2517 } 2518 if ([win canHide] == NO) 2519 { 2520 continue; /* Not hideable */ 2521 } 2522 if (win == _app_icon_window) 2523 { 2524 continue; /* can't hide the app icon. */ 2525 } 2526 if (_app_is_active == YES && [win hidesOnDeactivate] == YES) 2527 { 2528 continue; /* Will be hidden by deactivation */ 2529 } 2530 [_hidden addObject: win]; 2531 [win orderOut: self]; 2532 } 2533 } 2534 _app_is_hidden = YES; 2535 2536 if (YES == [[NSUserDefaults standardUserDefaults] 2537 boolForKey: @"GSSuppressAppIcon"]) 2538 { 2539#if MINI_ICON 2540 NSRect f = [[[self mainMenu] window] frame]; 2541 NSPoint p = f.origin; 2542 2543 p.y += f.size.height; 2544 [_app_icon_window setFrameTopLeftPoint: p]; 2545 [_app_icon_window orderFrontRegardless]; 2546 [_app_icon_window miniaturize: self]; 2547#else 2548 [_app_icon_window orderFrontRegardless]; 2549#endif 2550 } 2551 else 2552 { 2553 [[_app_icon_window contentView] setNeedsDisplay: YES]; 2554 } 2555 2556 /* 2557 * On hiding we also deactivate the application which will make the menus 2558 * go away too. 2559 */ 2560 [self deactivate]; 2561 _unhide_on_activation = YES; 2562 2563 info = [self _notificationUserInfo]; 2564 [nc postNotificationName: NSApplicationDidHideNotification 2565 object: self 2566 userInfo: info]; 2567 [[[NSWorkspace sharedWorkspace] notificationCenter] 2568 postNotificationName: NSApplicationDidHideNotification 2569 object: [NSWorkspace sharedWorkspace] 2570 userInfo: info]; 2571 } 2572 else 2573 { 2574 /*Minimize all windows if there isn't an AppIcon. This isn't the 2575 most elegant solution, but avoids to loss the app if the user 2576 hide it. */ 2577 [self miniaturizeAll: sender]; 2578 } 2579 } 2580#endif 2581} 2582 2583/**<p>Returns whether app is currently in hidden state.</p> 2584 <p>See Also: -hide: -unhide:</p> 2585 */ 2586- (BOOL) isHidden 2587{ 2588 return _app_is_hidden; 2589} 2590 2591/**<p>Unhides and activates this application.</p> 2592 <p>See Also: -unhideWithoutActivation -hide: -isHidden</p> 2593 */ 2594- (void) unhide: (id)sender 2595{ 2596 if (_app_is_hidden) 2597 { 2598 [self unhideWithoutActivation]; 2599 _unhide_on_activation = NO; 2600 } 2601 if (_app_is_active == NO) 2602 { 2603 /* 2604 * Activation should make the applications menus visible. 2605 */ 2606 [self activateIgnoringOtherApps: YES]; 2607 } 2608} 2609 2610/** 2611 * Unhides this app (displays its windows) but does not activate it. 2612 */ 2613- (void) unhideWithoutActivation 2614{ 2615 if (_app_is_hidden == YES) 2616 { 2617 NSDictionary *info; 2618 NSUInteger count; 2619 NSUInteger i; 2620 2621 [nc postNotificationName: NSApplicationWillUnhideNotification 2622 object: self]; 2623 2624 /* Make sure we set this before ordering windows to avoid possible 2625 recursive loops (some methods window/backend methods check if 2626 the app is hidden before ordering a window). */ 2627 _app_is_hidden = NO; 2628 2629 count = [_hidden count]; 2630 for (i = 0; i < count; i++) 2631 { 2632 NSWindow *win = [_hidden objectAtIndex: i]; 2633 [win orderFrontRegardless]; 2634 if ([win isMiniaturized]) 2635 { 2636 [GSServerForWindow(win) miniwindow: [win windowNumber]]; 2637 } 2638 } 2639 [_hidden removeAllObjects]; 2640 [[_app_icon_window contentView] setNeedsDisplay: YES]; 2641 2642 info = [self _notificationUserInfo]; 2643 [nc postNotificationName: NSApplicationDidUnhideNotification 2644 object: self 2645 userInfo: info]; 2646 [[[NSWorkspace sharedWorkspace] notificationCenter] 2647 postNotificationName: NSApplicationDidUnhideNotification 2648 object: [NSWorkspace sharedWorkspace] 2649 userInfo: info]; 2650 } 2651} 2652 2653/** 2654 * Arranges all non-miniaturized app's windows in front by successively calling 2655 * [NSWindow-orderFront:] on each window in the app's Windows menu. 2656 */ 2657- (void) arrangeInFront: (id)sender 2658{ 2659 NSMenu *menu; 2660 2661 menu = [self windowsMenu]; 2662 if (menu) 2663 { 2664 NSArray *itemArray; 2665 NSUInteger count; 2666 NSUInteger i; 2667 2668 itemArray = [menu itemArray]; 2669 count = [itemArray count]; 2670 for (i = 0; i < count; i++) 2671 { 2672 id win = [(NSMenuItem*)[itemArray objectAtIndex: i] target]; 2673 2674 if ([win isKindOfClass: [NSWindow class]] && 2675 [win isVisible] && ![win isMiniaturized]) 2676 { 2677 [win orderFront: sender]; 2678 } 2679 } 2680 } 2681} 2682 2683/* 2684 * User interface validation 2685 */ 2686- (BOOL) validateUserInterfaceItem: (id <NSValidatedUserInterfaceItem>)anItem 2687{ 2688 // FIXME 2689 return YES; 2690} 2691 2692/* 2693 * Managing windows 2694 */ 2695 2696/** 2697 * Returns current key window. If this app is active, one window should be 2698 * key. If this app is not active, nil should be returned. The key window 2699 * is the one that will receive keyboard input. 2700 */ 2701- (NSWindow*) keyWindow 2702{ 2703 return _key_window; 2704} 2705 2706/** 2707 * Returns current main window of this application. There need not necessarily 2708 * be a main window, even if app is active, and this should return nil if the 2709 * app is inactive. 2710 */ 2711- (NSWindow*) mainWindow 2712{ 2713 return _main_window; 2714} 2715 2716/** 2717 * Sends aSelector to each window, either in the app's internal window list 2718 * (flag = NO) or their stacking order from front to back on the screen 2719 * (flag = YES, <em>currently not implemented under GNUstep</em>). 2720 */ 2721- (NSWindow*) makeWindowsPerform: (SEL)aSelector inOrder: (BOOL)flag 2722{ 2723 NSArray *window_list; 2724 NSUInteger i, c; 2725 2726 // so i suppose when flag is YES it only runs on visible windows 2727 if (flag) 2728 { 2729 window_list = GSOrderedWindows(); 2730 } 2731 else 2732 { 2733 window_list = [self windows]; 2734 } 2735 2736 for (i = 0, c = [window_list count]; i < c; i++) 2737 { 2738 NSWindow *window = [window_list objectAtIndex: i]; 2739 2740 if ([window performSelector: aSelector] != nil) 2741 { 2742 return window; 2743 } 2744 } 2745 return nil; 2746} 2747 2748/** 2749 * Iconify all windows in the app. 2750 */ 2751- (void) miniaturizeAll: sender 2752{ 2753 NSArray *window_list = [self windows]; 2754 NSUInteger i, count; 2755 2756 for (i = 0, count = [window_list count]; i < count; i++) 2757 [[window_list objectAtIndex: i] miniaturize: sender]; 2758} 2759 2760/** 2761 * Prevent window reordering in response to most recent mouse down (useful to 2762 * prevent raise-on-mouse-click). <em>Not implemented under GNUstep.</em> 2763 */ 2764- (void) preventWindowOrdering 2765{ 2766 //TODO 2767} 2768 2769/** 2770 * Set whether the main run loop will request all visible windows update 2771 * themselves after the current or next event is processed. (Update occurs 2772 * after event dispatch in the loop.) 2773 * This is needed when in NSEventTrackingRunLoopMode. When the application 2774 * is using NSDefaultRunLoopMode or NSModalPanelRunLoopMode windows are updated 2775 * after each loop iteration irrespective of this setting. 2776 */ 2777- (void) setWindowsNeedUpdate: (BOOL)flag 2778{ 2779 _windows_need_update = flag; 2780} 2781 2782/** 2783 * Sends each of the app's visible windows an [NSWindow-update] message. 2784 * This method is called automatically for every iteration of the run loop 2785 * in NSDefaultRunLoopMode or NSModalPanelRunLoopMode, but is only called during 2786 * NSEventTrackingRunLoopMode if -setWindowsNeedUpdate: is set to YES. 2787 */ 2788- (void) updateWindows 2789{ 2790 NSArray *window_list = [self windows]; 2791 NSUInteger count = [window_list count]; 2792 NSUInteger i; 2793 2794 _windows_need_update = NO; 2795 [nc postNotificationName: NSApplicationWillUpdateNotification object: self]; 2796 2797 for (i = 0; i < count; i++) 2798 { 2799 NSWindow *win = [window_list objectAtIndex: i]; 2800 if ([win isVisible]) 2801 [win update]; 2802 } 2803 [nc postNotificationName: NSApplicationDidUpdateNotification object: self]; 2804} 2805 2806/** 2807 * Returns array of app's visible and invisible windows. 2808 */ 2809- (NSArray*) windows 2810{ 2811 return GSAllWindows(); 2812} 2813 2814/** 2815 * Returns window for windowNum. Note the window number can be obtained for 2816 * a window from [NSWindow-windowNumber]. 2817 */ 2818- (NSWindow *) windowWithWindowNumber: (NSInteger)windowNum 2819{ 2820 return GSWindowWithNumber(windowNum); 2821} 2822 2823/* 2824 * Showing Standard Panels 2825 */ 2826 2827/** Calls -orderFrontStandardAboutPanelWithOptions: with nil passed as 2828 the options dictionary. 2829*/ 2830- (void) orderFrontStandardAboutPanel: sender 2831{ 2832 [self orderFrontStandardAboutPanelWithOptions: nil]; 2833} 2834 2835/** OS X compatibility: Calls -orderFrontStandardInfoPanelWithOptions: . 2836*/ 2837- (void) orderFrontStandardAboutPanelWithOptions: (NSDictionary *)dictionary 2838{ 2839 [self orderFrontStandardInfoPanelWithOptions: dictionary]; 2840} 2841 2842/* infoPanel, GNUstep API */ 2843/** Calls -orderFrontStandardInfoPanelWithOptions: with nil passed as 2844 the options dictionary. 2845*/ 2846- (void) orderFrontStandardInfoPanel: sender 2847{ 2848 [self orderFrontStandardInfoPanelWithOptions: nil]; 2849} 2850 2851/** 2852 <p> 2853 Orders front the standard info panel for the application, 2854 taking the needed information from the <code>dictionary</code> 2855 argument. There is a single standard info panel per application; 2856 it is created the first time that this method is invoked, and then 2857 reused in all subsequent calls. The application standard info 2858 panel is immutable and can not be changed after creation. Useful 2859 keys for the <code>dictionary</code> are: 2860 </p> 2861 2862 <deflist> 2863 <term>ApplicationName</term> 2864 <desc>A string with the name of the 2865 application (eg, <var>"Gorm"</var>). If not available, the 2866 <file>Info-gnustep.plist</file> file is searched for the value of 2867 <var>ApplicationName</var> followed by 2868 <var>NSHumanReadableShortName</var>. If this also fails, the 2869 string returned by [NSProcessInfo-processName] is used. 2870 </desc> 2871 2872 <term>ApplicationDescription</term> 2873 <desc> A string with a very short 2874 description of the application (eg, <var>"GNUstep Graphics 2875 Objects Relationship Modeller"</var>). If not available, 2876 <file>Info-gnustep.plist</file> is searched for that key; if this 2877 fails, no application description is shown. 2878 </desc> 2879 2880 <term>ApplicationIcon</term> 2881 <desc> An image to be shown near the title. 2882 If not available, <file>Info-gnustep.plist</file> is searched for 2883 <var>ApplicationIcon</var>; if this fails, NSApp's -applicationIconImage 2884 method is used instead. 2885 </desc> 2886 2887 <term>ApplicationRelease</term> 2888 <desc> A string with the name of the 2889 application, release included (eg, <var>"Gorm 0.1"</var>). If 2890 not available, the value for <var>ApplicationVersion</var> is 2891 used instead. If this fails, <file>Info-gnustep.plist</file> is 2892 searched for <var>ApplicationRelease</var> or 2893 <var>NSAppVersion</var>, otherwise, <var>"Unknown"</var> is 2894 used. 2895 </desc> 2896 2897 <term>FullVersionID</term> 2898 <desc> A string with the full version of the 2899 application (eg, <var>"0.1.2b"</var> or 2900 <var>"snap011100"</var>). If not available, 2901 <var>Version</var> is used instead. If this fails, 2902 <file>Info-gnustep.plist</file> is looked for 2903 <var>NSBuildVersion</var>. If all fails, no full version is 2904 shown. 2905 </desc> 2906 2907 <term>Authors</term> 2908 <desc> An array of strings, each one with the name 2909 of an author (eg, <var>[NSArray arrayWithObject: "Nicola Pero 2910 <n.pero\@mi.flashnet.it>"]</var>). If not found, 2911 <file>Info-gnustep.plist</file> is searched for <var>Authors</var>, 2912 if this fails, <var>"Unknown"</var> is displayed. 2913 </desc> 2914 2915 <term>URL</term> 2916 <desc> [This field is still under work, so it might be 2917 changed] A string with an URL (eg, <var>"See 2918 http://www.gnustep.org"</var>). 2919 </desc> 2920 2921 <term>Copyright</term> 2922 <desc> A string with copyright owners (eg, 2923 <var>"Copyright (C) 2000 The Free Software Foundation, 2924 Inc."</var>). Support for multiple line strings is planned but 2925 not yet available. If not found, <file>Info-gnustep.plist</file> 2926 is searched for <var>Copyright</var> and then (failing this) for 2927 <var>NSHumanReadableCopyright</var>. If all fails, 2928 <var>"Copyright Information Not Available"</var> is used. 2929 </desc> 2930 2931 <term>CopyrightDescription</term> 2932 <desc>A string describing the kind of 2933 copyright (eg, <var>"Released under the GNU General Public 2934 License 2.0"</var>). If not available, 2935 <file>Info-gnustep.plist</file> is searched for 2936 <var>CopyrightDescription</var>. If this fails, no copyright 2937 description is shown. 2938 </desc> 2939 </deflist> 2940 */ 2941- (void) orderFrontStandardInfoPanelWithOptions: (NSDictionary *)dictionary 2942{ 2943 if (_infoPanel == nil) 2944 _infoPanel = [[GSInfoPanel alloc] initWithDictionary: dictionary]; 2945 2946 [_infoPanel setTitle: NSLocalizedString (@"Info", 2947 @"Title of the Info Panel")]; 2948 [_infoPanel orderFront: self]; 2949} 2950 2951/* 2952 * Getting the main menu 2953 */ 2954 2955/** 2956 * Returns the main menu of the receiver. 2957 */ 2958- (NSMenu*) mainMenu 2959{ 2960 return _main_menu; 2961} 2962 2963/** 2964 * Sets the main menu of the receiver. This is sent update messages by the 2965 * main event loop. 2966 */ 2967- (void) setMainMenu: (NSMenu*)aMenu 2968{ 2969 if (_main_menu == aMenu) 2970 { 2971 return; 2972 } 2973 2974 if (_main_menu != nil) 2975 { 2976 [_main_menu setMain: NO]; 2977 } 2978 2979 ASSIGN(_main_menu, aMenu); 2980 2981 if (_main_menu != nil) 2982 { 2983 [_main_menu setMain: YES]; 2984 } 2985} 2986 2987/* 2988 Overrides to show transient version of main menu as in NeXTstep. 2989*/ 2990- (void) rightMouseDown: (NSEvent*)theEvent 2991{ 2992 // On right mouse down display the main menu transient 2993 if (_main_menu != nil) 2994 [NSMenu popUpContextMenu: _main_menu 2995 withEvent: theEvent 2996 forView: nil]; 2997 else 2998 [super rightMouseDown: theEvent]; 2999} 3000 3001/** 3002 * Here for compatibility with OS X, but currently a no-op. 3003 */ 3004- (void) setAppleMenu: (NSMenu*)aMenu 3005{ 3006 //TODO: Unclear, what this should do. 3007} 3008 3009/* 3010 * Managing the Windows menu 3011 */ 3012 3013/** 3014 * Adds an item to the app's Windows menu. This is usually done automatically 3015 * so you don't need to call this method. 3016 */ 3017- (void) addWindowsItem: (NSWindow*)aWindow 3018 title: (NSString*)aString 3019 filename: (BOOL)isFilename 3020{ 3021 [self changeWindowsItem: aWindow title: aString filename: isFilename]; 3022} 3023 3024/** 3025 * Removes an item from the app's Windows menu. This is usually done 3026 * automatically so you don't need to call this method. 3027 */ 3028- (void) removeWindowsItem: (NSWindow*)aWindow 3029{ 3030 if (_windows_menu) 3031 { 3032 NSArray *itemArray; 3033 NSUInteger count; 3034 3035 itemArray = [_windows_menu itemArray]; 3036 count = [itemArray count]; 3037 while (count-- > 0) 3038 { 3039 NSMenuItem *item = [itemArray objectAtIndex: count]; 3040 3041 if ([item target] == aWindow) 3042 { 3043 [_windows_menu removeItemAtIndex: count]; 3044 return; 3045 } 3046 } 3047 } 3048} 3049 3050// this is an internal helper method for changeWindowsItem:title:filename 3051// when window represents an editable document 3052- (void) setImageForWindowsItem: (NSMenuItem *)item 3053{ 3054 NSImage *oldImage = [item image]; 3055 NSImage *newImage; 3056 3057 if (!([[item target] styleMask] & NSClosableWindowMask)) 3058 return; 3059 3060 if ([[item target] isDocumentEdited]) 3061 { 3062 newImage = [NSImage imageNamed: @"common_CloseBroken"]; 3063 } 3064 else 3065 { 3066 newImage = [NSImage imageNamed: @"common_Close"]; 3067 } 3068 3069 if (newImage != oldImage) 3070 { 3071 [item setImage: newImage]; 3072 } 3073} 3074 3075/** Changes the Window menu item associated with aWindow to aString. If no 3076 associated window item exists, one is created. If isFilename is YES, then 3077 aString is assumed to be a filename representation the way 3078 [NSWindow-setTitleWithRepresentedFilename:] would format it, otherwise 3079 the string is displayed literally in the menu item. 3080 */ 3081- (void) changeWindowsItem: (NSWindow*)aWindow 3082 title: (NSString*)aString 3083 filename: (BOOL)isFilename 3084{ 3085 NSArray *itemArray; 3086 NSUInteger count; 3087 NSUInteger i; 3088 id item; 3089 3090 if (![aWindow isKindOfClass: [NSWindow class]]) 3091 [NSException raise: NSInvalidArgumentException 3092 format: @"Object of bad type passed as window"]; 3093 3094 if (isFilename) 3095 { 3096 NSRange r = [aString rangeOfString: @" -- "]; 3097 3098 if (r.length > 0) 3099 { 3100 aString = [aString substringToIndex: r.location]; 3101 } 3102 } 3103 3104 /* 3105 * If there is no menu and nowhere to put one, we can't do anything. 3106 */ 3107 if (_windows_menu == nil) 3108 return; 3109 3110 /* 3111 * Check if the window is already in the menu. 3112 */ 3113 itemArray = [_windows_menu itemArray]; 3114 count = [itemArray count]; 3115 for (i = 0; i < count; i++) 3116 { 3117 NSMenuItem *item = [itemArray objectAtIndex: i]; 3118 3119 if ([item target] == aWindow) 3120 { 3121 /* 3122 * If our menu item already exists and with the correct 3123 * title, we need not continue. 3124 */ 3125 if ([[item title] isEqualToString: aString]) 3126 { 3127 return; 3128 } 3129 else 3130 { 3131 /* 3132 * Else, we need to remove the old item and add it again 3133 * with the new title. Then new item might be located 3134 * somewhere else in the menu than the old one (because 3135 * items in the menu are sorted by title) ... this is 3136 * why we remove the old one and then insert it again. 3137 */ 3138 [_windows_menu removeItem: item]; 3139 break; 3140 } 3141 } 3142 } 3143 3144 /* 3145 * Can't permit an untitled window in the window menu ... so if the 3146 * window has not title, we don't add it to the menu. 3147 */ 3148 if (aString == nil || [aString isEqualToString: @""]) 3149 return; 3150 3151 /* 3152 * Now we insert a menu item for the window in the correct order. 3153 * Window menu items are inserted in alphabetic order at the bottom 3154 * of the windows menu except for two special items with actions 3155 * 'performMiniaturize: ' and 'performClose: '. If these exist the 3156 * they are kept at the below all window entries in the menu. 3157 */ 3158 itemArray = [_windows_menu itemArray]; 3159 count = [itemArray count]; 3160 3161 if (count > 0 && sel_isEqual([[itemArray objectAtIndex: count-1] action], 3162 @selector(performClose:))) 3163 count--; 3164 if (count > 0 && sel_isEqual([[itemArray objectAtIndex: count-1] action], 3165 @selector(performMiniaturize:))) 3166 count--; 3167 for (i = 0; i < count; i++) 3168 { 3169 NSMenuItem *item = [itemArray objectAtIndex: i]; 3170 if ([[item target] isKindOfClass: [NSWindow class]] && 3171 sel_isEqual([item action], @selector(makeKeyAndOrderFront:))) 3172 break; 3173 } 3174 3175 while (i < count) 3176 { 3177 item = [itemArray objectAtIndex: i]; 3178 3179 if ([[item title] compare: aString] == NSOrderedDescending) 3180 break; 3181 i++; 3182 } 3183 3184 item = [_windows_menu insertItemWithTitle: aString 3185 action: @selector(makeKeyAndOrderFront:) 3186 keyEquivalent: @"" 3187 atIndex: i]; 3188 [item setTarget: aWindow]; 3189 3190 // When changing for a window with a file, we should also set the image. 3191 [self setImageForWindowsItem: item]; 3192} 3193 3194/** 3195 * Update Windows menu item for aWindow, to reflect its edited status. This 3196 * is usually done automatically so you don't need to call this. 3197 */ 3198- (void) updateWindowsItem: (NSWindow*)aWindow 3199{ 3200 NSMenu *menu; 3201 3202 menu = [self windowsMenu]; 3203 if (menu != nil) 3204 { 3205 NSArray *itemArray; 3206 NSUInteger count; 3207 NSUInteger i; 3208 BOOL found = NO; 3209 3210 itemArray = [menu itemArray]; 3211 count = [itemArray count]; 3212 for (i = 0; i < count; i++) 3213 { 3214 NSMenuItem *item = [itemArray objectAtIndex: i]; 3215 3216 if ([item target] == aWindow) 3217 { 3218 [self setImageForWindowsItem: item]; 3219 break; 3220 } 3221 } 3222 3223 if (found == NO) 3224 { 3225 [self changeWindowsItem: aWindow 3226 title: [aWindow title] 3227 filename: [aWindow _hasTitleWithRepresentedFilename]]; 3228 } 3229 } 3230} 3231 3232/** 3233 * Sets the windows menu of the receiver. The windows menu keeps track of all 3234 * windows open in the application. 3235 */ 3236- (void) setWindowsMenu: (NSMenu*)aMenu 3237{ 3238 if (_windows_menu == aMenu) 3239 { 3240 return; 3241 } 3242 3243 /* 3244 * Remove all the windows from the old windows menu. 3245 */ 3246 if (_windows_menu != nil) 3247 { 3248 NSArray *itemArray = [_windows_menu itemArray]; 3249 NSUInteger i, count = [itemArray count]; 3250 3251 for (i = 0; i < count; i++) 3252 { 3253 NSMenuItem *anItem = [itemArray objectAtIndex: i]; 3254 id win = [anItem target]; 3255 3256 if ([win isKindOfClass: [NSWindow class]]) 3257 { 3258 [_windows_menu removeItem: anItem]; 3259 } 3260 } 3261 } 3262 3263 /* Set the new _windows_menu. */ 3264 ASSIGN (_windows_menu, aMenu); 3265 3266 { 3267 /* 3268 * Now use [-changeWindowsItem:title:filename:] to build the new menu. 3269 */ 3270 NSArray * windows = [self windows]; 3271 NSUInteger i, count = [windows count]; 3272 for (i = 0; i < count; i++) 3273 { 3274 NSWindow *win = [windows objectAtIndex: i]; 3275 3276 if (([win isExcludedFromWindowsMenu] == NO) 3277 && ([win isVisible] || [win isMiniaturized])) 3278 { 3279 [self changeWindowsItem: win 3280 title: [win title] 3281 filename: [win _hasTitleWithRepresentedFilename]]; 3282 } 3283 } 3284 } 3285} 3286 3287/** 3288 * Returns current Windows menu for the application (whose window contents are 3289 * managed automatically by this class and NSWindow). 3290 */ 3291- (NSMenu*) windowsMenu 3292{ 3293 return _windows_menu; 3294} 3295 3296/* 3297 * Managing the Service menu 3298 */ 3299 3300/** 3301 * Registers the types this application can send and receive over Services. 3302 * sendTypes represents the pasteboard types this app can receive in Service 3303 * invocation. This is used to set up the services menu of this app -- an 3304 * item is added for each registered service that can accept one of sendTypes 3305 * or return one of returnTypes 3306 */ 3307- (void) registerServicesMenuSendTypes: (NSArray *)sendTypes 3308 returnTypes: (NSArray *)returnTypes 3309{ 3310 [_listener registerSendTypes: sendTypes 3311 returnTypes: returnTypes]; 3312} 3313 3314/** <p>Returns the services menu of the receiver.</p> 3315 <p>See Also: -setServicesMenu:</p> 3316 */ 3317- (NSMenu *) servicesMenu 3318{ 3319 return [_listener servicesMenu]; 3320} 3321 3322/**<p> Returns the services provided previously registered using the 3323 * -setServicesProvider: method.</p><p>See Also: -setServicesProvider:</p> 3324 */ 3325- (id) servicesProvider 3326{ 3327 return [_listener servicesProvider]; 3328} 3329 3330/**<p>ets the services menu for the receiver. This should be called, otherwise 3331 * warnings will be generated by the main event loop, which is in charge of 3332 * updating all menus, including the Services menu.</p> 3333 * <p>See Also: -servicesMenu</p> 3334 */ 3335- (void) setServicesMenu: (NSMenu *)aMenu 3336{ 3337 [_listener setServicesMenu: aMenu]; 3338} 3339 3340/** 3341 * Sets the object which provides services to other applications.<br /> 3342 * Passing a nil value for anObject will result in the provision of 3343 * services to other applications by this application being disabled.<br /> 3344 * See [NSPasteboard] for information about providing services. 3345 */ 3346- (void) setServicesProvider: (id)anObject 3347{ 3348 [_listener setServicesProvider: anObject]; 3349} 3350 3351/** 3352 * Return an object capable of sending and receiving the specified sendType 3353 * and returnType in a services interaction. NSApp is generally the last 3354 * responder to get this request, and the implementation passes it on to the 3355 * delegate if it handles it and is not itself an [NSResponder], or returns 3356 * nil otherwise. 3357 */ 3358- (id) validRequestorForSendType: (NSString *)sendType 3359 returnType: (NSString *)returnType 3360{ 3361 if (_delegate != nil && ![_delegate isKindOfClass: [NSResponder class]] 3362 && [_delegate respondsToSelector: 3363 @selector(validRequestorForSendType:returnType:)]) 3364 return [_delegate validRequestorForSendType: sendType 3365 returnType: returnType]; 3366 3367 return nil; 3368} 3369 3370/** 3371 * Returns the default drawing context for the app. 3372 */ 3373- (NSGraphicsContext *) context 3374{ 3375 return _default_context; 3376} 3377 3378/** 3379 * NSLogs an exception without raising it. 3380 */ 3381- (void) reportException: (NSException *)anException 3382{ 3383 if (anException) 3384 NSLog (_(@"reported exception - %@"), anException); 3385} 3386 3387- (BOOL) presentError: (NSError *)error 3388{ 3389 NSAlert *alert; 3390 NSInteger result; 3391 3392 error = [self willPresentError: error]; 3393 alert = [NSAlert alertWithError: error]; 3394 result = [alert runModal]; 3395 3396 if (result != NSAlertErrorReturn) 3397 { 3398 // Convert result (1, 0, -1) into index (0, 1, 2) 3399 result = 1 - result; 3400 return [[error recoveryAttempter] attemptRecoveryFromError: error 3401 optionIndex: result]; 3402 } 3403 else 3404 { 3405 return NO; 3406 } 3407} 3408 3409struct _DelegateWrapper 3410{ 3411 id delegate; 3412 SEL selector; 3413 NSError *error; 3414 void *context; 3415}; 3416 3417- (void) presentError: (NSError*)error 3418 modalForWindow: (NSWindow*)window 3419 delegate: (id)delegate 3420 didPresentSelector: (SEL)sel 3421 contextInfo: (void*)context 3422{ 3423 NSAlert *alert; 3424 struct _DelegateWrapper *wrapper; 3425 3426 error = [self willPresentError: error]; 3427 alert = [NSAlert alertWithError: error]; 3428 // FIXME: Who is trying to recover the error? 3429 wrapper = malloc(sizeof(struct _DelegateWrapper)); 3430 wrapper->delegate = delegate; 3431 wrapper->selector = sel; 3432 wrapper->error = error; 3433 wrapper->context = context; 3434 3435 [alert beginSheetModalForWindow: window 3436 modalDelegate: self 3437 didEndSelector: @selector(_didPresentError:returnCode:contextInfo:) 3438 contextInfo: wrapper]; 3439} 3440 3441- (void) _didPresentError: (NSWindow*)sheet 3442 returnCode: (NSInteger)result 3443 contextInfo: (void*)context 3444{ 3445 struct _DelegateWrapper *wrapper; 3446 id delegate; 3447 SEL sel; 3448 NSError *error; 3449 void *orgContext; 3450 BOOL recover; 3451 3452 wrapper = (struct _DelegateWrapper*)context; 3453 delegate = wrapper->delegate; 3454 sel = wrapper->selector; 3455 error = wrapper->error; 3456 orgContext = wrapper->context; 3457 free(wrapper); 3458 3459 if (result != NSAlertErrorReturn) 3460 { 3461 // Convert result (1, 0, -1) into index (0, 1, 2) 3462 result = 1 - result; 3463 recover = [[error recoveryAttempter] attemptRecoveryFromError: error 3464 optionIndex: result]; 3465 } 3466 else 3467 { 3468 recover = NO; 3469 } 3470 3471 if ([delegate respondsToSelector: sel]) 3472 { 3473 void (*didEnd)(id, SEL, BOOL, void*); 3474 3475 didEnd = (void (*)(id, SEL, BOOL, void*))[delegate methodForSelector: sel]; 3476 didEnd(delegate, sel, recover, orgContext); 3477 } 3478} 3479 3480- (NSError*) willPresentError: (NSError*)error 3481{ 3482 if ([_delegate respondsToSelector: @selector(application:willPresentError:)]) 3483 { 3484 return [_delegate application: self willPresentError: error]; 3485 } 3486 else 3487 { 3488 return error; 3489 } 3490} 3491 3492/** 3493 * Requests the application terminates the application. First an 3494 * -applicationShouldTerminate: message is sent to the delegate, and only if 3495 * it returns <code>NSTerminateNow</code> will termination be 3496 * carried out.<br /> 3497 * The old version of -applicationShouldTerminate: returned a BOOL, and this 3498 * should still work as YES is 3499 * equivalent to <code>NSTerminateNow</code> and NO is 3500 * equivalent to <code>NSTerminateCancel</code>. 3501 */ 3502- (void) terminate: (id)sender 3503{ 3504 NSDocumentController *sdc; 3505 NSApplicationTerminateReply termination; 3506 3507 /* First ask the shared document controller to save any unsaved changes */ 3508 sdc = [NSDocumentController sharedDocumentController]; 3509 if ([[sdc documentClassNames] count] > 0) 3510 { 3511 if ([sdc reviewUnsavedDocumentsWithAlertTitle: _(@"Quit") 3512 cancellable: YES] == NO) 3513 { 3514 return; 3515 } 3516 } 3517 3518 /* Now ask the application delegate whether its okay to terminate */ 3519 termination = NSTerminateNow; 3520 if ([_delegate respondsToSelector: @selector(applicationShouldTerminate:)]) 3521 { 3522 /* The old API has applicationShouldTerminate: return a BOOL, 3523 * so if we are linked in to an application which used that 3524 * API, the delegate might return a BOOL rather than an 3525 * NSTerminateNow. That's fine as both NSTerminateNow 3526 * and BOOL are integral types (though potentially of different sizes), 3527 * and NSTerminateNow is defined as YES and NSTerminateCancel as NO. 3528 * So all we need to do is mask the low byte of the return value in 3529 * case there is uninitialised random data in the higher bytes. 3530 */ 3531 termination = ([_delegate applicationShouldTerminate: self] & 0xff); 3532 } 3533 3534 if (termination == NSTerminateNow) 3535 { 3536 [self replyToApplicationShouldTerminate: YES]; 3537 } 3538 /* 3539 Don't need to do anything on NSTerminateLater, as long as user code 3540 follows the contract (to call replyTo... later). 3541 */ 3542} 3543 3544/** 3545 * Terminates the app if shouldTerminate is YES, otherwise does nothing. 3546 * This should be called by user code if a delegate has returned 3547 * <code>NSTerminateLater</code> to an -applicationShouldTerminate: message. 3548 */ 3549- (void) replyToApplicationShouldTerminate: (BOOL)shouldTerminate 3550{ 3551 // Prevent cycles in terminate: call. 3552 if (shouldTerminate && _app_is_running) 3553 { 3554 [nc postNotificationName: NSApplicationWillTerminateNotification 3555 object: self]; 3556 3557 _app_is_running = NO; 3558 3559 GSRemoveIcon(_app_icon_window); 3560 [[self windows] makeObjectsPerformSelector: @selector(close)]; 3561 [NSCursor setHiddenUntilMouseMoves: NO]; 3562 3563 /* Store our user information. */ 3564 [[NSUserDefaults standardUserDefaults] synchronize]; 3565 3566 /* Tell the Workspace that we really did terminate. */ 3567 [[[NSWorkspace sharedWorkspace] notificationCenter] 3568 postNotificationName: NSWorkspaceDidTerminateApplicationNotification 3569 object: [NSWorkspace sharedWorkspace] 3570 userInfo: [self _notificationUserInfo]]; 3571 3572 /* Destroy the main run loop pool (this also destroys any nested 3573 pools which might have been created inside this one). */ 3574 DESTROY (_runLoopPool); 3575 3576 /* Now free the NSApplication object. Enclose the operation 3577 into an autorelease pool, in case some -dealloc method needs 3578 to use any temporary object. */ 3579 { 3580 NSAutoreleasePool *pool; 3581 3582 IF_NO_GC(pool = [arpClass new]); 3583 3584 DESTROY(NSApp); 3585 3586 DESTROY(pool); 3587 } 3588 3589 /* And finally, stop the program. */ 3590 exit(0); 3591 } 3592} 3593 3594- (void) replyToOpenOrPrint: (NSApplicationDelegateReply)reply 3595{ 3596 // FIXME 3597} 3598 3599/** 3600 * Returns the application's delegate, as set by the -setDelegate: method.<br/> 3601 * <p>The application delegate will automatically be sent various 3602 * notifications (as long as it implements the appropriate methods) 3603 * when application events occur. The method to handle each of these 3604 * notifications has name mirroring the notification name, so for instance 3605 * an <em>NSApplicationDidBecomeActiveNotification</em> is handled by an 3606 * <code>applicationDidBecomeActive:</code> method. 3607 * </p> 3608 * <list> 3609 * <item>NSApplicationDidBecomeActiveNotification</item> 3610 * <item>NSApplicationDidFinishLaunchingNotification</item> 3611 * <item>NSApplicationDidHideNotification</item> 3612 * <item>NSApplicationDidResignActiveNotification</item> 3613 * <item>NSApplicationDidUnhideNotification</item> 3614 * <item>NSApplicationDidUpdateNotification</item> 3615 * <item>NSApplicationWillBecomeActiveNotification</item> 3616 * <item>NSApplicationWillFinishLaunchingNotification</item> 3617 * <item>NSApplicationWillHideNotification</item> 3618 * <item>NSApplicationWillResignActiveNotification</item> 3619 * <item>NSApplicationWillTerminateNotification</item> 3620 * <item>NSApplicationWillUnhideNotification</item> 3621 * <item>NSApplicationWillUpdateNotification</item> 3622 * </list> 3623 * <p>The delegate is also sent various messages to ask for authorisation 3624 * to perform actions, or to ask it to perform actions (again, as long 3625 * as it implements the appropriate methods). 3626 * </p> 3627 * <list> 3628 * <item>application:shouldTerminateAfterLastWindowClosed:</item> 3629 * <item>application:shouldOpenUntitledFile:</item> 3630 * <item>application:openFile:</item> 3631 * <item>application:openFiles:</item> 3632 * <item>application:openFileWithoutUI:</item> 3633 * <item>application:openTempFile:</item> 3634 * <item>application:openUntitledFile:</item> 3635 * <item>application:shouldOpenUntitledFile:</item> 3636 * <item>application:printFile:</item> 3637 * <item>application:shouldTerminate:</item> 3638 * <item>application:shouldTerminateAfterLastWindowClosed:</item> 3639 * </list> 3640 * <p>The delegate is also called upon to respond to any actions which 3641 * are not handled by a window, a window delgate, or by the application 3642 * object itself. This is controlled by the -targetForAction: method. 3643 * </p> 3644 * <p>Finally, the application delegate is responsible for handling 3645 * messages sent to the application from remote processes (see the 3646 * section documenting distributed objects for [NSPasteboard]). 3647 * </p> 3648 * <p>See -setDelegate: and [(NSApplicationDelegate)] for more information.</p> 3649 */ 3650- (id) delegate 3651{ 3652 return _delegate; 3653} 3654 3655/** 3656 * Sets the delegate of the application to anObject.<br /> 3657 * <p><em>Beware</em>, this does not retain anObject, so you must be sure 3658 * that, in the event of anObject being deallocated, you 3659 * stop it being the application delagate by calling this 3660 * method again with another object (or nil) as the argument. 3661 * </p> 3662 * <p>See -delegate and [(NSApplicationDelegate)] for more information.</p> 3663 */ 3664- (void) setDelegate: (id)anObject 3665{ 3666 if (_delegate) 3667 [nc removeObserver: _delegate name: nil object: self]; 3668 _delegate = anObject; 3669 3670#define SET_DELEGATE_NOTIFICATION(notif_name) \ 3671 if ([_delegate respondsToSelector: @selector(application##notif_name:)]) \ 3672 [nc addObserver: _delegate \ 3673 selector: @selector(application##notif_name:) \ 3674 name: NSApplication##notif_name##Notification object: self] 3675 3676 SET_DELEGATE_NOTIFICATION(DidBecomeActive); 3677 SET_DELEGATE_NOTIFICATION(DidFinishLaunching); 3678 SET_DELEGATE_NOTIFICATION(DidHide); 3679 SET_DELEGATE_NOTIFICATION(DidResignActive); 3680 SET_DELEGATE_NOTIFICATION(DidUnhide); 3681 SET_DELEGATE_NOTIFICATION(DidUpdate); 3682 SET_DELEGATE_NOTIFICATION(WillBecomeActive); 3683 SET_DELEGATE_NOTIFICATION(WillFinishLaunching); 3684 SET_DELEGATE_NOTIFICATION(WillHide); 3685 SET_DELEGATE_NOTIFICATION(WillResignActive); 3686 SET_DELEGATE_NOTIFICATION(WillTerminate); 3687 SET_DELEGATE_NOTIFICATION(WillUnhide); 3688 SET_DELEGATE_NOTIFICATION(WillUpdate); 3689} 3690 3691/* 3692 * Methods for scripting 3693 */ 3694 3695/** 3696 * OS X scripting method to return document objects being handled by 3697 * application. <em>Not implemented yet under GNUstep.</em> 3698 */ 3699- (NSArray *) orderedDocuments 3700{ 3701 // FIXME 3702 return nil; 3703} 3704 3705/** 3706 * OS X scripting method to return windows in front-to-back on-screen order 3707 * for scriptable windows. 3708 * <em>The GNUstep implementation returns all the windows excluding NSPanels. 3709 * some backends may return an array in an unspecified order.</em> 3710 */ 3711- (NSArray *) orderedWindows 3712{ 3713 NSArray *arr = GSOrderedWindows(); 3714 NSMutableArray *ret = [[NSMutableArray alloc] initWithCapacity:[arr count]]; 3715 NSEnumerator *iter = [arr objectEnumerator]; 3716 id win; 3717 while ((win = [iter nextObject])) 3718 { 3719 if (![win isKindOfClass:[NSPanel class]]) 3720 [ret addObject:win]; 3721 } 3722 3723 return AUTORELEASE(ret); 3724} 3725 3726/* 3727 * Methods for user attention requests 3728 */ 3729 3730/** 3731 * Cancels a request previously made through calling -requestUserAttention: . 3732 * Note that request is cancelled automatically if user activates the app. 3733 */ 3734- (void) cancelUserAttentionRequest: (NSInteger)request 3735{ 3736 // FIXME 3737} 3738 3739/** 3740 * Method that on OS X makes the icon jump in the doc. Mercifully, this is 3741 * unimplemented under GNUstep. If it <em>were</em> implemented, requestType 3742 * could be either <code>NSCriticalRequest</code> (bounce endlessly) or 3743 * <code>NSInformationalRequest</code> (bounce for one second). 3744 */ 3745- (NSInteger) requestUserAttention: (NSRequestUserAttentionType)requestType 3746{ 3747 // FIXME 3748 return 0; 3749} 3750 3751- (NSApplicationPresentationOptions) currentPresentationOptions 3752{ 3753 return _presentationOptions; 3754} 3755 3756- (NSApplicationPresentationOptions) presentationOptions 3757{ 3758 return _presentationOptions; 3759} 3760 3761/** 3762 * Currently unimplemented and unused in GNUstep, it could be extended to handle 3763 * special GNUstep needs too 3764 */ 3765- (void)setPresentationOptions: (NSApplicationPresentationOptions)options 3766{ 3767 _presentationOptions = options; 3768} 3769 3770/* 3771 * NSCoding protocol 3772 */ 3773- (void) encodeWithCoder: (NSCoder*)aCoder 3774{ 3775 [super encodeWithCoder: aCoder]; 3776 if ([aCoder allowsKeyedCoding]) 3777 { 3778 /* 3779 if (_delegate != nil) 3780 { 3781 [aCoder encodeObject: _delegate forKey: @"NSDelegate"]; 3782 } 3783 [aCoder encodeObject: _main_menu forKey: @"NSMainMenu"]; // ??? 3784 [aCoder encodeObject: _windows_menu forKey: @"NSWindowsMenu"]; // ??? 3785 */ 3786 } 3787 else 3788 { 3789 [aCoder encodeConditionalObject: _delegate]; 3790 [aCoder encodeObject: _main_menu]; 3791 [aCoder encodeConditionalObject: _windows_menu]; 3792 } 3793} 3794 3795- (id) initWithCoder: (NSCoder*)aDecoder 3796{ 3797 id obj; 3798 3799 [super initWithCoder: aDecoder]; 3800 if ([aDecoder allowsKeyedCoding]) 3801 { 3802 /* 3803 if ([aDecoder containsValueForKey: @"NSDelegate"]) 3804 { 3805 obj = [aDecoder decodeObjectForKey: @"NSDelegate"]; 3806 [self setDelegate: obj]; 3807 } 3808 obj = [aDecoder decodeObjectForKey: @"NSMainMenu"]; 3809 [self setMainMenu: obj]; 3810 obj = [aDecoder decodeObjectForKey: @"NSWindowsMenu"]; 3811 [self setWindowsMenu: obj]; 3812 */ 3813 } 3814 else 3815 { 3816 obj = [aDecoder decodeObject]; 3817 [self setDelegate: obj]; 3818 obj = [aDecoder decodeObject]; 3819 [self setMainMenu: obj]; 3820 obj = [aDecoder decodeObject]; 3821 [self setWindowsMenu: obj]; 3822 } 3823 return self; 3824} 3825 3826@end /* NSApplication */ 3827 3828 3829@implementation NSApplication (Private) 3830 3831- (void) _loadAppIconImage 3832{ 3833 NSDictionary *infoDict; 3834 NSString *appIconFile; 3835 NSImage *image = nil; 3836 3837 infoDict = [[NSBundle mainBundle] infoDictionary]; 3838 appIconFile = [infoDict objectForKey: @"NSIcon"]; 3839 if (appIconFile && ![appIconFile isEqual: @""]) 3840 { 3841 image = [NSImage imageNamed: appIconFile]; 3842 } 3843 3844 // Try to look up the icon file. 3845 appIconFile = [infoDict objectForKey: @"CFBundleIconFile"]; 3846 if (appIconFile && ![appIconFile isEqual: @""]) 3847 { 3848 image = [NSImage imageNamed: appIconFile]; 3849 } 3850 3851 if (image == nil) 3852 { 3853 image = [NSImage imageNamed: @"GNUstep"]; 3854 } 3855 else 3856 { 3857 /* Set the new image to be named 'NSApplicationIcon' ... to do that we 3858 * must first check that any existing image of the same name has its 3859 * name removed. 3860 */ 3861 [(NSImage*)[NSImage imageNamed: @"NSApplicationIcon"] setName: nil]; 3862 // We need to copy the image as we may have a proxy here 3863 image = AUTORELEASE([image copy]); 3864 [image setName: @"NSApplicationIcon"]; 3865 } 3866 [self setApplicationIconImage: image]; 3867} 3868 3869- (void) _appIconInit 3870{ 3871 NSAppIconView *iv; 3872 NSUInteger mask = NSIconWindowMask; 3873 BOOL suppress; 3874 3875 suppress = [[NSUserDefaults standardUserDefaults] 3876 boolForKey: @"GSSuppressAppIcon"]; 3877#if MINI_ICON 3878 if (suppress) 3879 { 3880 mask = NSMiniaturizableWindowMask; 3881 } 3882#endif 3883 3884 _app_icon_window = [[NSIconWindow alloc] initWithContentRect: NSZeroRect 3885 styleMask: mask 3886 backing: NSBackingStoreRetained 3887 defer: NO 3888 screen: nil]; 3889 3890 3891 3892 { 3893 NSRect iconContentRect; 3894 NSRect iconFrame; 3895 NSRect iconViewFrame; 3896 3897 iconContentRect = GSGetIconFrame(_app_icon_window); 3898 iconFrame = [_app_icon_window frameRectForContentRect: iconContentRect]; 3899 iconFrame.origin = [[NSScreen mainScreen] frame].origin; 3900 iconViewFrame = NSMakeRect(0, 0, 3901 iconContentRect.size.width, iconContentRect.size.height); 3902 [_app_icon_window setFrame: iconFrame display: YES]; 3903 3904 iv = [[NSAppIconView alloc] initWithFrame: iconViewFrame]; 3905 [iv setImage: [self applicationIconImage]]; 3906 [_app_icon_window setContentView: iv]; 3907 RELEASE(iv); 3908 } 3909 3910 if (NO == suppress) 3911 { 3912 /* The icon window is not suppressed ... display it. 3913 */ 3914 [_app_icon_window orderFrontRegardless]; 3915 } 3916} 3917 3918- (NSDictionary*) _notificationUserInfo 3919{ 3920 NSString *path; 3921 NSString *port; 3922 NSNumber *processIdentifier; 3923 NSDictionary *userInfo; 3924 3925 processIdentifier = [NSNumber numberWithInt: 3926 [[NSProcessInfo processInfo] processIdentifier]]; 3927 port = [(GSServicesManager*)_listener port]; 3928 path = [[NSBundle mainBundle] bundlePath]; 3929 if (port == nil) 3930 { 3931 if (path == nil) 3932 { 3933 userInfo = [NSDictionary dictionaryWithObjectsAndKeys: 3934 processIdentifier, @"NSApplicationProcessIdentifier", 3935 nil]; 3936 } 3937 else 3938 { 3939 userInfo = [NSDictionary dictionaryWithObjectsAndKeys: 3940 path, @"NSApplicationPath", 3941 processIdentifier, @"NSApplicationProcessIdentifier", 3942 nil]; 3943 } 3944 } 3945 else if (path == nil) 3946 { 3947 userInfo = [NSDictionary dictionaryWithObjectsAndKeys: 3948 port, @"NSApplicationName", 3949 processIdentifier, @"NSApplicationProcessIdentifier", 3950 nil]; 3951 } 3952 else 3953 { 3954 userInfo = [NSDictionary dictionaryWithObjectsAndKeys: 3955 port, @"NSApplicationName", 3956 path, @"NSApplicationPath", 3957 processIdentifier, @"NSApplicationProcessIdentifier", 3958 nil]; 3959 } 3960 return userInfo; 3961} 3962 3963- (void) _openDocument: (NSString*)filePath 3964{ 3965 [_listener application: self openFile: filePath]; 3966} 3967 3968- (id) _targetForAction: (SEL)aSelector window: (NSWindow *)window 3969{ 3970 id resp, delegate; 3971 NSDocumentController *sdc; 3972 3973 if (window == nil) 3974 { 3975 return nil; 3976 } 3977 3978 /* traverse the responder chain including the window's delegate */ 3979 resp = [window firstResponder]; 3980 while (resp != nil && resp != self) 3981 { 3982 if ([resp respondsToSelector: aSelector]) 3983 { 3984 return resp; 3985 } 3986 if (resp == window) 3987 { 3988 delegate = [window delegate]; 3989 if ([delegate respondsToSelector: aSelector]) 3990 { 3991 return delegate; 3992 } 3993 } 3994 resp = [resp nextResponder]; 3995 } 3996 3997 /* in a document based app try the window's document */ 3998 sdc = [NSDocumentController sharedDocumentController]; 3999 if ([[sdc documentClassNames] count] > 0) 4000 { 4001 resp = [sdc documentForWindow: window]; 4002 4003 if (resp != nil && [resp respondsToSelector: aSelector]) 4004 { 4005 return resp; 4006 } 4007 } 4008 4009 /* nothing found */ 4010 return nil; 4011} 4012 4013- (id) _targetForAction: (SEL)aSelector 4014 keyWindow: (NSWindow *)keyWindow 4015 mainWindow: (NSWindow *)mainWindow 4016{ 4017 NSDocumentController *sdc; 4018 id resp; 4019 4020 if (aSelector == NULL) 4021 return nil; 4022 4023 /* start looking in the key window's responder chain */ 4024 resp = [self _targetForAction: aSelector window: keyWindow]; 4025 if (resp != nil) 4026 { 4027 return resp; 4028 } 4029 4030 /* next check the main window's responder chain (provided it is not 4031 * the key window) */ 4032 if (mainWindow != keyWindow) 4033 { 4034 resp = [self _targetForAction: aSelector window: mainWindow]; 4035 if (resp != nil) 4036 { 4037 return resp; 4038 } 4039 } 4040 4041 /* try the shared application imstance and its delegate */ 4042 if ([self respondsToSelector: aSelector]) 4043 { 4044 return self; 4045 } 4046 4047 if (_delegate != nil && [_delegate respondsToSelector: aSelector]) 4048 { 4049 return _delegate; 4050 } 4051 4052 /* Try the NSApplication's responder list to determine if any of them 4053 * respond to the selector. 4054 */ 4055 resp = [self nextResponder]; 4056 while (resp != nil) 4057 { 4058 if ([resp respondsToSelector: aSelector]) 4059 { 4060 return resp; 4061 } 4062 resp = [resp nextResponder]; 4063 } 4064 4065 /* as last resort in a document based app, try the document controller */ 4066 sdc = [NSDocumentController sharedDocumentController]; 4067 if ([[sdc documentClassNames] count] > 0 4068 && [sdc respondsToSelector: aSelector]) 4069 { 4070 return sdc; 4071 } 4072 4073 /* give up */ 4074 return nil; 4075} 4076 4077- (void) _windowDidBecomeKey: (NSNotification*) notification 4078{ 4079 id obj = [notification object]; 4080 4081 if (_key_window == nil && [obj isKindOfClass: [NSWindow class]]) 4082 { 4083 _key_window = obj; 4084 [_main_menu update]; 4085 } 4086 else if (_key_window != obj) 4087 { 4088 NSLog(@"Bogus attempt to set key window"); 4089 } 4090} 4091 4092- (void) _windowDidBecomeMain: (NSNotification*) notification 4093{ 4094 id obj = [notification object]; 4095 4096 if (_main_window == nil && [obj isKindOfClass: [NSWindow class]]) 4097 { 4098 _main_window = obj; 4099 [_main_menu update]; 4100 } 4101 else if (_main_window != obj) 4102 { 4103 NSLog(@"Bogus attempt to set main window"); 4104 } 4105} 4106 4107- (void) _windowDidResignKey: (NSNotification*) notification 4108{ 4109 id obj = [notification object]; 4110 4111 if (_key_window == obj) 4112 { 4113 _key_window = nil; 4114 [NSCursor setHiddenUntilMouseMoves: NO]; 4115 } 4116 else 4117 { 4118 NSLog(@"Bogus attempt to resign key window"); 4119 } 4120} 4121 4122- (void) _windowDidResignMain: (NSNotification*) notification 4123{ 4124 id obj = [notification object]; 4125 4126 if (_main_window == obj) 4127 { 4128 _main_window = nil; 4129 } 4130 else 4131 { 4132 NSLog(@"Bogus attempt to resign key window"); 4133 } 4134} 4135 4136- (void) _lastWindowClosed 4137{ 4138 if ([_delegate respondsToSelector: 4139 @selector(applicationShouldTerminateAfterLastWindowClosed:)]) 4140 { 4141 if ([_delegate 4142 applicationShouldTerminateAfterLastWindowClosed: self]) 4143 { 4144 [self terminate: self]; 4145 } 4146 } 4147 /* wlux 2009-10-17: If we use MS Windows style menus, terminate 4148 the application by default when the last window is closed. */ 4149 else if (NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil) == 4150 NSWindows95InterfaceStyle) 4151 { 4152 [self terminate: self]; 4153 } 4154} 4155 4156- (void) _windowWillClose: (NSNotification*) notification 4157{ 4158 NSWindow *win = [notification object]; 4159 4160 if (_app_is_running) 4161 { 4162 /* FIXME Don't use GSOrderedWindows(), since it may not return all 4163 windows when the application is hidden. E.g., given a hidden 4164 application with a single window with canHide == NO, if the user 4165 closes this window it would appear as if the last window was closed. */ 4166 NSArray *windows_list = GSOrderedWindows(); 4167 NSEnumerator *iter = [windows_list objectEnumerator]; 4168 BOOL wasLast = YES; 4169 NSWindow *tmp; 4170 4171 while ((tmp = [iter nextObject])) 4172 { 4173 if (tmp != win && [tmp canBecomeMainWindow] == YES) 4174 { 4175 wasLast = NO; 4176 break; 4177 } 4178 } 4179 4180 /* Perform _lastWindowDidClose at the end of the event loop cycle so 4181 that all interested objects are notified before we terminate the 4182 application. In particular, this means that a modified document 4183 associated with the closed window has been closed before -terminate: 4184 is called and therefore the user isn't asked twice whether she wants 4185 to save that document's unsaved changes. */ 4186 if (wasLast) 4187 { 4188 [self performSelector: @selector(_lastWindowClosed) 4189 withObject: nil 4190 afterDelay: 0.1]; 4191 } 4192 } 4193} 4194 4195- (void) _workspaceNotification: (NSNotification*) notification 4196{ 4197 NSString *name = [notification name]; 4198 NSDictionary *info = [notification userInfo]; 4199 4200 /* 4201 * Handle hiding and unhiding of this app if necessary. 4202 */ 4203 if ([name isEqualToString: GSUnhideAllApplicationsNotification] == YES) 4204 { 4205 [self unhideWithoutActivation]; 4206 } 4207 else if ([name isEqualToString: GSHideOtherApplicationsNotification] == YES) 4208 { 4209 NSString *port = [info objectForKey: @"NSApplicationName"]; 4210 4211 if ([port isEqual: [[GSServicesManager manager] port]] == NO) 4212 { 4213 [self hide: self]; 4214 } 4215 } 4216} 4217 4218- (NSArray *) _openFiles 4219{ 4220 NSMutableArray *files = nil; 4221 NSArray *args = [[NSProcessInfo processInfo] arguments]; 4222 NSEnumerator *en = [args objectEnumerator]; 4223 NSString *file = nil; 4224 4225 [en nextObject]; // skip the first element, which is always empty... 4226 while ((file = [en nextObject]) != nil) 4227 { 4228 if ([file length] == 0) 4229 { 4230 continue; 4231 } 4232 4233 if ([file characterAtIndex: 0] != '-') 4234 { 4235 if (files == nil) 4236 { 4237 files = [NSMutableArray array]; 4238 } 4239 [files addObject: file]; 4240 } 4241 else 4242 { 4243 break; 4244 } 4245 } 4246 4247 return files; 4248} 4249 4250- (NSMenu *) _dockMenu 4251{ 4252 NSUInteger i, j, n; 4253 NSMenu *dockMenu, *windowsMenu; 4254 4255 // ask delegate for a dock menu, if none create a new one 4256 dockMenu = nil; 4257 if ([_delegate respondsToSelector: @selector(applicationDockMenu:)]) 4258 { 4259 // NB we make a copy of the menu since we are going to modify it 4260 dockMenu = [[_delegate applicationDockMenu: self] copy]; 4261 } 4262 if (dockMenu == nil) 4263 { 4264 dockMenu = [[NSMenu alloc] initWithTitle:@""]; 4265 } 4266 4267 // copy window menu entries to the top of the menu 4268 windowsMenu = [NSApp windowsMenu]; 4269 for (i = j = 0, n = [windowsMenu numberOfItems]; i < n; i++) 4270 { 4271 NSMenuItem *item = [windowsMenu itemAtIndex: i]; 4272 if ([[item target] isKindOfClass:[NSWindow class]] && 4273 sel_isEqual([item action], @selector(makeKeyAndOrderFront:))) 4274 { 4275 [[dockMenu insertItemWithTitle: [item title] 4276 action: @selector(makeKeyAndOrderFront:) 4277 keyEquivalent: @"" 4278 atIndex: j++] 4279 setTarget: [item target]]; 4280 } 4281 } 4282 if (j > 0) 4283 { 4284 [dockMenu insertItem: [NSMenuItem separatorItem] atIndex: j++]; 4285 } 4286 4287 // insert standard entries to show or hide and to quit the application at 4288 // the bottom 4289 if (j < [dockMenu numberOfItems]) 4290 { 4291 [dockMenu addItem: [NSMenuItem separatorItem]]; 4292 } 4293 if ([self isHidden]) 4294 { 4295 [dockMenu addItemWithTitle:_(@"Show") 4296 action:@selector(unhide:) 4297 keyEquivalent:@""]; 4298 } 4299 else 4300 { 4301 [dockMenu addItemWithTitle:_(@"Hide") 4302 action:@selector(hide:) 4303 keyEquivalent:@""]; 4304 } 4305 [dockMenu addItemWithTitle:_(@"Quit") 4306 action:@selector(terminate:) 4307 keyEquivalent:@""]; 4308 4309 // return the menu 4310 return dockMenu; 4311} 4312 4313@end // NSApplication (Private) 4314 4315 4316@implementation NSApplication (GSGUIInternal) 4317 4318- (void) _windowWillDealloc: (NSWindow *)window 4319{ 4320 if (window == _key_window) 4321 _key_window = nil; 4322 if (window == _main_window) 4323 _main_window = nil; 4324} 4325 4326@end 4327 4328