/**
Processes one event for a modal session described by the theSession variable. Before processing the event, it makes the session window key and orders the window front, so there is no need to do this separately. When finished, it returns the state of the session (i.e. whether it is still running or has been stopped, etc)
See Also: -runModalForWindow: */ - (int) runModalSession: (NSModalSession)theSession { NSAutoreleasePool *pool; GSDisplayServer *srv; BOOL found = NO; NSEvent *event; NSDate *limit; if (theSession != _session) { [NSException raise: NSInvalidArgumentException format: @"runModalSession: with wrong session"]; } IF_NO_GC(pool = [arpClass new]); [theSession->window orderFrontRegardless]; if ([theSession->window canBecomeKeyWindow] == YES) { [theSession->window makeKeyWindow]; } else if ([theSession->window canBecomeMainWindow] == YES) { [theSession->window makeMainWindow]; } // Use the default context for all events. srv = GSCurrentServer(); /* * Set a limit date in the distant future so we wait until we get an * event. We discard events that are not for this window. When we * find one for this window, we push it back at the start of the queue. */ limit = [NSDate distantFuture]; do { event = DPSGetEvent(srv, NSAnyEventMask, limit, NSDefaultRunLoopMode); if (event != nil) { NSWindow *eventWindow = [event window]; if (eventWindow == theSession->window || [eventWindow worksWhenModal]) { DPSPostEvent(srv, event, YES); found = YES; } else if ([event type] == NSAppKitDefined) { /* Handle resize and other window manager events now */ [self sendEvent: event]; } } } while (found == NO && theSession->runState == NSRunContinuesResponse); RELEASE (pool); /* * Deal with the events in the queue. */ while (found == YES && theSession->runState == NSRunContinuesResponse) { IF_NO_GC(pool = [arpClass new]); event = DPSGetEvent(srv, NSAnyEventMask, limit, NSDefaultRunLoopMode); if (event != nil) { NSWindow *eventWindow = [event window]; if (eventWindow == theSession->window || [eventWindow worksWhenModal]) { ASSIGN(_current_event, event); } else { found = NO; } } else { found = NO; } if (found == YES) { NSEventType type = [_current_event type]; [self sendEvent: _current_event]; // update (en/disable) the services menu's items if (type != NSPeriodic && type != NSMouseMoved) { [_listener updateServicesMenu]; [_main_menu update]; } /* * Check to see if the window has gone away - if so, end session. */ if ([[self windows] indexOfObjectIdenticalTo: _session->window] == NSNotFound) { [self stopModal]; } if (_windows_need_update) { [self updateWindows]; } } RELEASE (pool); } NSAssert(_session == theSession, @"Session was changed while running"); return theSession->runState; } /**
Returns the window that is part of the current modal session, if any.
See -runModalForWindow: */ - (NSWindow *) modalWindow { if (_session != 0) return (_session->window); else return nil; } - (void) stop: (id)sender { if (_session != 0) [self stopModal]; else { _app_is_running = NO; /* * add dummy event to queue to assure loop cycles * at least one more time */ DPSPostEvent(GSCurrentServer(), null_event, NO); } } - (void) stopModal { [self stopModalWithCode: NSRunStoppedResponse]; } - (void) stopModalWithCode: (int)returnCode { if (_session == 0) { [NSException raise: NSInvalidArgumentException format: @"stopModalWithCode: when not in a modal session"]; } else if (returnCode == NSRunContinuesResponse) { [NSException raise: NSInvalidArgumentException format: @"stopModalWithCode: with NSRunContinuesResponse"]; } _session->runState = returnCode; } - (int) runModalForWindow: (NSWindow *)theWindow relativeToWindow: (NSWindow *)docWindow { // FIXME return [self runModalForWindow: theWindow]; } - (void) beginSheet: (NSWindow *)sheet modalForWindow: (NSWindow *)docWindow modalDelegate: (id)modalDelegate didEndSelector: (SEL)didEndSelector contextInfo: (void *)contextInfo { // FIXME int ret; ret = [self runModalForWindow: sheet relativeToWindow: docWindow]; if ([modalDelegate respondsToSelector: didEndSelector]) // FIXME Those this work on all platforms??? [modalDelegate performSelector: didEndSelector withObject: (NSObject*)ret withObject: contextInfo]; } - (void) endSheet: (NSWindow *)sheet { // FIXME [self stopModal]; } - (void) endSheet: (NSWindow *)sheet returnCode: (int)returnCode { // FIXME [self stopModalWithCode: returnCode]; } /* * Getting, removing, and posting events */ - (void) sendEvent: (NSEvent *)theEvent { NSEventType type; type = [theEvent type]; switch (type) { case NSPeriodic: /* NSApplication traps the periodic events */ break; case NSKeyDown: { NSDebugLLog(@"NSEvent", @"send key down event\n"); if ([theEvent modifierFlags] & NSCommandKeyMask) { NSArray *window_list = [self windows]; unsigned i; unsigned count = [window_list count]; for (i = 0; i < count; i++) { NSWindow *window = [window_list objectAtIndex: i]; if ([window performKeyEquivalent: theEvent] == YES) break; } } else [[theEvent window] sendEvent: theEvent]; break; } case NSKeyUp: { NSDebugLLog(@"NSEvent", @"send key up event\n"); [[theEvent window] sendEvent: theEvent]; break; } default: /* pass all other events to the event's window */ { NSWindow *window = [theEvent window]; if (!theEvent) NSDebugLLog(@"NSEvent", @"NSEvent is nil!\n"); if (type == NSMouseMoved) NSDebugLLog(@"NSMotionEvent", @"Send move (%d) to window %@", type, ((window != nil) ? [window description] : @"No window")); else NSDebugLLog(@"NSEvent", @"Send NSEvent type: %d to window %@", type, ((window != nil) ? [window description] : @"No window")); if (window) [window sendEvent: theEvent]; else if (type == NSRightMouseDown) [self rightMouseDown: theEvent]; } } } - (NSEvent*) currentEvent { return _current_event; } - (void) discardEventsMatchingMask: (unsigned int)mask beforeEvent: (NSEvent *)lastEvent { DPSDiscardEvents(GSCurrentServer(), mask, lastEvent); } - (NSEvent*) nextEventMatchingMask: (unsigned int)mask untilDate: (NSDate*)expiration inMode: (NSString*)mode dequeue: (BOOL)flag { NSEvent *event; if (!expiration) expiration = [NSDate distantFuture]; if (flag) event = DPSGetEvent(GSCurrentServer(), mask, expiration, mode); else event = DPSPeekEvent(GSCurrentServer(), mask, expiration, mode); if (event) { IF_NO_GC(NSAssert([event retainCount] > 0, NSInternalInconsistencyException)); /* * If we are not in a tracking loop, we may want to unhide a hidden * because the mouse has been moved. */ if (mode != NSEventTrackingRunLoopMode) { if ([NSCursor isHiddenUntilMouseMoves]) { NSEventType type = [event type]; if ((type == NSLeftMouseDown) || (type == NSLeftMouseUp) || (type == NSOtherMouseDown) || (type == NSOtherMouseUp) || (type == NSRightMouseDown) || (type == NSRightMouseUp) || (type == NSMouseMoved)) { [NSCursor unhide]; } } } ASSIGN(_current_event, event); } return event; } - (void) postEvent: (NSEvent *)event atStart: (BOOL)flag { DPSPostEvent(GSCurrentServer(), event, flag); } /* * Sending action messages */ - (BOOL) sendAction: (SEL)aSelector to: aTarget from: sender { /* * If target responds to the selector then have it perform it. */ if (aTarget && [aTarget respondsToSelector: aSelector]) { [aTarget performSelector: aSelector withObject: sender]; return YES; } else { id resp = [self targetForAction: aSelector]; if (resp) { [resp performSelector: aSelector withObject: sender]; return YES; } } return NO; } - (id)targetForAction:(SEL)theAction to:(id)theTarget from:(id)sender { // TODO: This is not fully documented, if it ever gets, we should // call this in sendAction:to:from: if (theTarget && [theTarget respondsToSelector: theAction]) { return theTarget; } else { return [self targetForAction: theAction]; } } /**
Returns the target object that will respond to aSelector, if any. The method first checks if any of the key window's first responders, the key window or its delegate responds. Next it checks the main window in the same way. Finally it checks the receiver (NSApplication) and it's delegate.
*/ - (id) targetForAction: (SEL)aSelector { NSWindow *keyWindow; NSWindow *mainWindow; id resp; keyWindow = [self keyWindow]; if (keyWindow != nil) { resp = [keyWindow firstResponder]; while (resp != nil && resp != keyWindow) { if ([resp respondsToSelector: aSelector]) { return resp; } resp = [resp nextResponder]; } if ([keyWindow respondsToSelector: aSelector]) { return keyWindow; } resp = [keyWindow delegate]; if (resp != nil && [resp respondsToSelector: aSelector]) { return resp; } } if (_session != 0) return nil; mainWindow = [self mainWindow]; if (keyWindow != mainWindow && mainWindow != nil) { resp = [mainWindow firstResponder]; while (resp != nil && resp != mainWindow) { if ([resp respondsToSelector: aSelector]) { return resp; } resp = [resp nextResponder]; } if ([mainWindow respondsToSelector: aSelector]) { return mainWindow; } resp = [mainWindow delegate]; if (resp != nil && [resp respondsToSelector: aSelector]) { return resp; } } if ([self respondsToSelector: aSelector]) { return self; } if (_delegate != nil && [_delegate respondsToSelector: aSelector]) { return _delegate; } return nil; } - (BOOL) tryToPerform: (SEL)aSelector with: (id)anObject { if ([super tryToPerform: aSelector with: anObject] == YES) { return YES; } if (_delegate != nil && [_delegate respondsToSelector: aSelector]) { [_delegate performSelector: aSelector withObject: anObject]; return YES; } return NO; } // Set the app's icon - (void) setApplicationIconImage: (NSImage*)anImage { [_app_icon setName: nil]; [anImage setName: @"NSApplicationIcon"]; ASSIGN(_app_icon, anImage); if (_app_icon_window != nil) { [[_app_icon_window contentView] setImage: anImage]; } } - (NSImage*) applicationIconImage { return _app_icon; } - (NSWindow*) iconWindow { return _app_icon_window; } /* * Hiding and arranging windows */ - (void) hide: (id)sender { if (_app_is_hidden == NO) { NSArray *windows_list = [self windows]; unsigned count = [windows_list count]; unsigned i; [nc postNotificationName: NSApplicationWillHideNotification object: self]; if ([self keyWindow] != nil) { _hidden_key = [self keyWindow]; [_hidden_key resignKeyWindow]; [GSServerForWindow(_app_icon_window) setinputfocus: [_app_icon_window windowNumber]]; } for (i = 0; i < count; i++) { NSWindow *win = [windows_list objectAtIndex: i]; if ([win isVisible] == NO) { continue; /* Already invisible */ } if (win == _app_icon_window) { continue; /* can't hide the app icon. */ } if (_app_is_active == YES && [win hidesOnDeactivate] == YES) { continue; /* Will be hidden by deactivation */ } [_hidden addObject: win]; [win orderOut: self]; } _app_is_hidden = YES; /* * On hiding we also deactivate the application which will make the menus * go away too. */ [self deactivate]; _unhide_on_activation = YES; [nc postNotificationName: NSApplicationDidHideNotification object: self]; } } - (BOOL) isHidden { return _app_is_hidden; } - (void) unhide: (id)sender { if (_app_is_hidden) { [self unhideWithoutActivation]; _unhide_on_activation = NO; } if (_app_is_active == NO) { /* * Activation should make the applications menus visible. */ [self activateIgnoringOtherApps: YES]; } } - (void) unhideWithoutActivation { if (_app_is_hidden == YES) { unsigned count; unsigned i; [nc postNotificationName: NSApplicationWillUnhideNotification object: self]; count = [_hidden count]; for (i = 0; i < count; i++) { [[_hidden objectAtIndex: i] orderFrontRegardless]; } [_hidden removeAllObjects]; if (_hidden_key != nil && [[self windows] indexOfObjectIdenticalTo: _hidden_key] != NSNotFound) { [_hidden_key makeKeyAndOrderFront: self]; _hidden_key = nil; } _app_is_hidden = NO; [nc postNotificationName: NSApplicationDidUnhideNotification object: self]; } } - (void) arrangeInFront: (id)sender { NSMenu *menu; menu = [self windowsMenu]; if (menu) { NSArray *itemArray; unsigned count; unsigned i; itemArray = [menu itemArray]; count = [itemArray count]; for (i = 0; i < count; i++) { id win = [[itemArray objectAtIndex: i] target]; if ([win isKindOfClass: [NSWindow class]]) { [win orderFront: sender]; } } } } /* * Managing windows */ - (NSWindow*) keyWindow { return _key_window; } - (NSWindow*) mainWindow { return _main_window; } - (NSWindow*) makeWindowsPerform: (SEL)aSelector inOrder: (BOOL)flag { NSArray *window_list = [self windows]; unsigned i; if (flag) { unsigned count = [window_list count]; for (i = 0; i < count; i++) { NSWindow *window = [window_list objectAtIndex: i]; if ([window performSelector: aSelector] != nil) { return window; } } } else { i = [window_list count]; while (i-- > 0) { NSWindow *window = [window_list objectAtIndex: i]; if ([window performSelector: aSelector] != nil) { return window; } } } return nil; } - (void) miniaturizeAll: sender { NSArray *window_list = [self windows]; unsigned i, count; for (i = 0, count = [window_list count]; i < count; i++) [[window_list objectAtIndex: i] miniaturize: sender]; } - (void) preventWindowOrdering { //TODO } - (void) setWindowsNeedUpdate: (BOOL)flag { _windows_need_update = flag; } - (void) updateWindows { NSArray *window_list = [self windows]; unsigned count = [window_list count]; unsigned i; _windows_need_update = NO; [nc postNotificationName: NSApplicationWillUpdateNotification object: self]; for (i = 0; i < count; i++) { NSWindow *win = [window_list objectAtIndex: i]; if ([win isVisible]) [win update]; } [nc postNotificationName: NSApplicationDidUpdateNotification object: self]; } - (NSArray*) windows { return GSAllWindows(); } - (NSWindow *) windowWithWindowNumber: (int)windowNum { return GSWindowWithNumber(windowNum); } /* * Showing Standard Panels */ - (void) orderFrontColorPanel: sender { NSColorPanel *colorPanel = [NSColorPanel sharedColorPanel]; if (colorPanel) [colorPanel orderFront: nil]; else NSBeep(); } - (void) orderFrontDataLinkPanel: sender { NSDataLinkPanel *dataLinkPanel = [NSDataLinkPanel sharedDataLinkPanel]; if (dataLinkPanel) [dataLinkPanel orderFront: nil]; else NSBeep(); } - (void) orderFrontHelpPanel: sender { // This is implemented in NSHelpManager.m [self showHelp: sender]; } - (void) runPageLayout: sender { [[NSPageLayout pageLayout] runModal]; } /* infoPanel, macosx API -- Deprecated */ - (void) orderFrontStandardAboutPanel: sender { [self orderFrontStandardInfoPanel: sender]; } - (void) orderFrontStandardAboutPanelWithOptions: (NSDictionary *)dictionary { [self orderFrontStandardInfoPanelWithOptions: dictionary]; } /* infoPanel, GNUstep API */ - (void) orderFrontStandardInfoPanel: sender { [self orderFrontStandardInfoPanelWithOptions: nil]; } - (void) orderFrontStandardInfoPanelWithOptions: (NSDictionary *)dictionary { if (_infoPanel == nil) _infoPanel = [[GSInfoPanel alloc] initWithDictionary: dictionary]; [_infoPanel setTitle: GSGuiLocalizedString (@"Info", @"Title of the Info Panel")]; [_infoPanel orderFront: self]; } /* * Getting the main menu */ - (NSMenu*) mainMenu { return _main_menu; } - (void) setMainMenu: (NSMenu*)aMenu { if (_main_menu != nil && _main_menu != aMenu) { [_main_menu close]; [[_main_menu window] setLevel: NSSubmenuWindowLevel]; } ASSIGN(_main_menu, aMenu); [_main_menu setTitle: [[NSProcessInfo processInfo] processName]]; // Set the title of the window also. // This wont be displayed, but the window manager may need it. [[_main_menu window] setTitle: [[NSProcessInfo processInfo] processName]]; [[_main_menu window] setLevel: NSMainMenuWindowLevel]; [_main_menu sizeToFit]; if ([self isActive]) { [_main_menu update]; [_main_menu display]; } } - (void) rightMouseDown: (NSEvent*)theEvent { // On right mouse down display the main menu transient if (_main_menu != nil) [_main_menu _rightMouseDisplay: theEvent]; else [super rightMouseDown: theEvent]; } - (void) setAppleMenu: (NSMenu*)aMenu { //TODO: Unclear, what this should do. } /* * Managing the Windows menu */ - (void) addWindowsItem: (NSWindow*)aWindow title: (NSString*)aString filename: (BOOL)isFilename { [self changeWindowsItem: aWindow title: aString filename: isFilename]; } - (void) changeWindowsItem: (NSWindow*)aWindow title: (NSString*)aString filename: (BOOL)isFilename { NSArray *itemArray; unsigned count; unsigned i; id item; if (![aWindow isKindOfClass: [NSWindow class]]) [NSException raise: NSInvalidArgumentException format: @"Object of bad type passed as window"]; if (isFilename) { NSRange r = [aString rangeOfString: @" -- "]; if (r.length > 0) { aString = [aString substringToIndex: r.location]; } } /* * If there is no menu and nowhere to put one, we can't do anything. */ if (_windows_menu == nil) return; /* * Check if the window is already in the menu. */ itemArray = [_windows_menu itemArray]; count = [itemArray count]; for (i = 0; i < count; i++) { id item = [itemArray objectAtIndex: i]; if ([item target] == aWindow) { /* * If our menu item already exists and with the correct * title, we need not continue. */ if ([[item title] isEqualToString: aString]) { return; } else { /* * Else, we need to remove the old item and add it again * with the new title. Then new item might be located * somewhere else in the menu than the old one (because * items in the menu are sorted by title) ... this is * why we remove the old one and then insert it again. */ [_windows_menu removeItem: item]; break; } } } /* * Can't permit an untitled window in the window menu ... so if the * window has not title, we don't add it to the menu. */ if (aString == nil || [aString isEqualToString: @""]) return; /* * Now we insert a menu item for the window in the correct order. * Make special allowance for menu entries to 'arrangeInFront: ' * 'performMiniaturize: ' and 'performClose: '. If these exist the * window entries should stay after the first one and before the * other two. */ itemArray = [_windows_menu itemArray]; count = [itemArray count]; i = 0; if (count > 0 && sel_eq([[itemArray objectAtIndex: 0] action], @selector(arrangeInFront:))) i++; if (count > i && sel_eq([[itemArray objectAtIndex: count-1] action], @selector(performClose:))) count--; if (count > i && sel_eq([[itemArray objectAtIndex: count-1] action], @selector(performMiniaturize:))) count--; while (i < count) { item = [itemArray objectAtIndex: i]; if ([[item title] compare: aString] == NSOrderedDescending) break; i++; } item = [_windows_menu insertItemWithTitle: aString action: @selector(makeKeyAndOrderFront:) keyEquivalent: @"" atIndex: i]; [item setTarget: aWindow]; // TODO: When changing for a window with a file, we should also set the image. } - (void) removeWindowsItem: (NSWindow*)aWindow { if (_windows_menu) { NSArray *itemArray; unsigned count; itemArray = [_windows_menu itemArray]; count = [itemArray count]; while (count-- > 0) { id item = [itemArray objectAtIndex: count]; if ([item target] == aWindow) { [_windows_menu removeItemAtIndex: count]; return; } } } } - (void) setWindowsMenu: (NSMenu*)aMenu { if (_windows_menu == aMenu) { return; } /* * Remove all the windows from the old windows menu. */ if (_windows_menu != nil) { NSArray *itemArray = [_windows_menu itemArray]; unsigned i, count = [itemArray count]; for (i = 0; i < count; i++) { NSMenuItem *anItem = [itemArray objectAtIndex: i]; id win = [anItem target]; if ([win isKindOfClass: [NSWindow class]]) { [_windows_menu removeItem: anItem]; } } } /* Set the new _windows_menu. */ ASSIGN (_windows_menu, aMenu); { /* * Now use [-changeWindowsItem:title:filename:] to build the new menu. */ NSArray * windows = [self windows]; unsigned i, count = [windows count]; for (i = 0; i < count; i++) { NSWindow *win = [windows objectAtIndex: i]; if ([win isExcludedFromWindowsMenu] == NO) { NSString *t = [win title]; NSString *f = [win representedFilename]; [self changeWindowsItem: win title: t filename: [t isEqual: f]]; } } } } - (void) updateWindowsItem: (NSWindow*)aWindow { NSMenu *menu; NSMenuView *view; menu = [self windowsMenu]; if (menu != nil) { NSArray *itemArray; unsigned count; unsigned i; BOOL found = NO; view = [menu menuRepresentation]; itemArray = [menu itemArray]; count = [itemArray count]; for (i = 0; i < count; i++) { id item = [itemArray objectAtIndex: i]; if ([item target] == aWindow) { NSMenuItemCell *cell; NSCellImagePosition oldPos; NSImage *oldImage; NSImage *newImage; BOOL changed; found = YES; cell = [view menuItemCellForItemAtIndex: i]; oldPos = [cell imagePosition]; oldImage = [cell image]; newImage = oldImage; changed = NO; if (oldPos != NSImageLeft) { [cell setImagePosition: NSImageLeft]; changed = YES; } if ([aWindow isDocumentEdited]) { newImage = [NSImage imageNamed: @"common_WMCloseBroken"]; } else { newImage = [NSImage imageNamed: @"common_WMClose"]; } if (newImage != oldImage) { [item setImage: newImage]; [cell setImage: newImage]; changed = YES; } if (changed) { [menu sizeToFit]; [view setNeedsDisplayForItemAtIndex: i]; } break; } } if (found == NO) { NSString *t = [aWindow title]; NSString *f = [aWindow representedFilename]; [self changeWindowsItem: aWindow title: t filename: [t isEqual: f]]; } } } - (NSMenu*) windowsMenu { return _windows_menu; } /* * Managing the Service menu */ - (void) registerServicesMenuSendTypes: (NSArray *)sendTypes returnTypes: (NSArray *)returnTypes { [_listener registerSendTypes: sendTypes returnTypes: returnTypes]; } - (NSMenu *) servicesMenu { return [_listener servicesMenu]; } - (id) servicesProvider { return [_listener servicesProvider]; } - (void) setServicesMenu: (NSMenu *)aMenu { [_listener setServicesMenu: aMenu]; } - (void) setServicesProvider: (id)anObject { [_listener setServicesProvider: anObject]; } - (id) validRequestorForSendType: (NSString *)sendType returnType: (NSString *)returnType { if (_delegate != nil && ![_delegate isKindOfClass: [NSResponder class]] && [_delegate respondsToSelector: @selector(validRequestorForSendType:returnType:)]) return [_delegate validRequestorForSendType: sendType returnType: returnType]; return nil; } - (NSGraphicsContext *) context { return _default_context; } - (void) reportException: (NSException *)anException { if (anException) NSLog(GSGuiLocalizedString (@"reported exception - %@", nil), anException); } /* * Terminating the application */ - (void) terminate: (id)sender { BOOL shouldTerminate = YES; if ([_delegate respondsToSelector: @selector(applicationShouldTerminate:)]) { shouldTerminate = [_delegate applicationShouldTerminate: sender]; } else { shouldTerminate = [[NSDocumentController sharedDocumentController] reviewUnsavedDocumentsWithAlertTitle: GSGuiLocalizedString (@"Quit", nil) cancellable:YES]; } if (shouldTerminate) { NSDictionary *userInfo; NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; [nc postNotificationName: NSApplicationWillTerminateNotification object: self]; _app_is_running = NO; [[self windows] makeObjectsPerformSelector: @selector(close)]; /* Store our user information. */ [[NSUserDefaults standardUserDefaults] synchronize]; /* Tell the Workspace that we really did terminate. */ userInfo = [NSDictionary dictionaryWithObject: [[NSProcessInfo processInfo] processName] forKey: @"NSApplicationName"]; [[workspace notificationCenter] postNotificationName: NSWorkspaceDidTerminateApplicationNotification object: workspace userInfo: userInfo]; /* Destroy the main run loop pool (this also destroys any nested pools which might have been created inside this one). */ DESTROY (_runLoopPool); /* Now free the NSApplication object. Enclose the operation into an autorelease pool, in case some -dealloc method needs to use any temporary object. */ { NSAutoreleasePool *pool; IF_NO_GC(pool = [arpClass new]); DESTROY(NSApp); DESTROY(pool); } /* And finally, stop the program. */ exit(0); } } - (id) delegate { return _delegate; } - (void) setDelegate: (id)anObject { if (_delegate) [nc removeObserver: _delegate name: nil object: self]; _delegate = anObject; #define SET_DELEGATE_NOTIFICATION(notif_name) \ if ([_delegate respondsToSelector: @selector(application##notif_name:)]) \ [nc addObserver: _delegate \ selector: @selector(application##notif_name:) \ name: NSApplication##notif_name##Notification object: self] SET_DELEGATE_NOTIFICATION(DidBecomeActive); SET_DELEGATE_NOTIFICATION(DidFinishLaunching); SET_DELEGATE_NOTIFICATION(DidHide); SET_DELEGATE_NOTIFICATION(DidResignActive); SET_DELEGATE_NOTIFICATION(DidUnhide); SET_DELEGATE_NOTIFICATION(DidUpdate); SET_DELEGATE_NOTIFICATION(WillBecomeActive); SET_DELEGATE_NOTIFICATION(WillFinishLaunching); SET_DELEGATE_NOTIFICATION(WillHide); SET_DELEGATE_NOTIFICATION(WillResignActive); SET_DELEGATE_NOTIFICATION(WillTerminate); SET_DELEGATE_NOTIFICATION(WillUnhide); SET_DELEGATE_NOTIFICATION(WillUpdate); } /* * NSCoding protocol */ - (void) encodeWithCoder: (NSCoder*)aCoder { [super encodeWithCoder: aCoder]; [aCoder encodeConditionalObject: _delegate]; [aCoder encodeObject: _main_menu]; [aCoder encodeConditionalObject: _windows_menu]; } - (id) initWithCoder: (NSCoder*)aDecoder { id obj; [super initWithCoder: aDecoder]; obj = [aDecoder decodeObject]; [self setDelegate: obj]; obj = [aDecoder decodeObject]; [self setMainMenu: obj]; obj = [aDecoder decodeObject]; [self setWindowsMenu: obj]; return self; } @end /* NSApplication */ @implementation NSApplication (Private) - _appIconInit { NSAppIconView *iv; if (_app_icon == nil) _app_icon = RETAIN([NSImage imageNamed: @"GNUstep"]); _app_icon_window = [[NSIconWindow alloc] initWithContentRect: NSMakeRect(0,0,64,64) styleMask: NSIconWindowMask backing: NSBackingStoreRetained defer: NO screen: nil]; iv = [[NSAppIconView alloc] initWithFrame: NSMakeRect(0,0,64,64)]; [iv setImage: _app_icon]; [_app_icon_window setContentView: iv]; RELEASE(iv); [_app_icon_window orderFrontRegardless]; [GSServerForWindow(_app_icon_window) setinputfocus: [_app_icon_window windowNumber]]; return self; } - (void) _openDocument: (NSString*)filePath { if ([_delegate respondsToSelector: @selector(application:openFile:)]) { [_delegate application: self openFile: filePath]; } else { [[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile: filePath display: YES]; } } - (void) _windowDidBecomeKey: (NSNotification*) notification { id obj = [notification object]; if (_key_window == nil && [obj isKindOfClass: [NSWindow class]]) { _key_window = obj; } else { NSLog(@"Bogus attempt to set key window"); } } - (void) _windowDidBecomeMain: (NSNotification*) notification { id obj = [notification object]; if (_main_window == nil && [obj isKindOfClass: [NSWindow class]]) { _main_window = obj; } else { NSLog(@"Bogus attempt to set main window"); } } - (void) _windowDidResignKey: (NSNotification*) notification { id obj = [notification object]; if (_key_window == obj) { _key_window = nil; } else { NSLog(@"Bogus attempt to resign key window"); } } - (void) _windowDidResignMain: (NSNotification*) notification { id obj = [notification object]; if (_main_window == obj) { _main_window = nil; } else { NSLog(@"Bogus attempt to resign key window"); } } - (void) _windowWillClose: (NSNotification*) notification { NSWindow *win = [notification object]; NSArray *windows_list = [self windows]; unsigned count = [windows_list count]; unsigned i; NSMutableArray *list = [NSMutableArray arrayWithCapacity: count]; BOOL wasKey = [win isKeyWindow]; BOOL wasMain = [win isMainWindow]; for (i = 0; i < count; i++) { NSWindow *tmp = [windows_list objectAtIndex: i]; if ([tmp canBecomeMainWindow] == YES && [tmp isVisible] == YES) { [list addObject: tmp]; } } [list removeObjectIdenticalTo: win]; count = [list count]; /* If there's only one window left, and that's the one being closed, then we ask the delegate if the app is to be terminated. */ if (wasMain && count == 0 && _app_is_running) { NSDebugLog(@"asking delegate whether to terminate app..."); if ([_delegate respondsToSelector: @selector(applicationShouldTerminateAfterLastWindowClosed:)]) { if ([_delegate applicationShouldTerminateAfterLastWindowClosed: self]) { [self terminate: self]; } } } if (wasMain == YES) { [win resignMainWindow]; } if (wasKey == YES) { [win resignKeyWindow]; } if (_app_is_running) { /* * If we are not quitting, we may need to find a new key/main window. */ if (wasKey == YES && [self keyWindow] == nil) { win = [self mainWindow]; if (win != nil && [win canBecomeKeyWindow] == YES) { /* * We have a main window that can become key, so do it. */ [win makeKeyAndOrderFront: self]; } else if (win != nil) { /* * We have a main window that can't become key, so we just * find a new window to make into our key window. */ for (i = 0; i < count; i++) { win = [list objectAtIndex: i]; if ([win canBecomeKeyWindow] == YES) { [win makeKeyAndOrderFront: self]; } } } else { /* * Find a window that can be made key and main - and do it. */ for (i = 0; i < count; i++) { win = [list objectAtIndex: i]; if ([win canBecomeKeyWindow] && [win canBecomeMainWindow]) { break; } } if (i < count) { [win makeMainWindow]; [win makeKeyAndOrderFront: self]; } else { /* * No window we can use, so just find any candidate to * be main window and another to be key window. */ for (i = 0; i < count; i++) { win = [list objectAtIndex: i]; if ([win canBecomeMainWindow] == YES) { [win makeMainWindow]; break; } } for (i = 0; i < count; i++) { win = [list objectAtIndex: i]; if ([win canBecomeKeyWindow] == YES) { [win makeKeyAndOrderFront: self]; break; } } } } } else if ([self mainWindow] == nil) { win = [self keyWindow]; if ([win canBecomeMainWindow] == YES) { [win makeMainWindow]; } else { for (i = 0; i < count; i++) { win = [list objectAtIndex: i]; if ([win canBecomeMainWindow] == YES) { [win makeMainWindow]; break; } } } } /* * If the app has no key window - we must make sure the icon window * has keyboard focus, even though it doesn't actually use kb events. */ if ([self keyWindow] == nil) { [GSServerForWindow(_app_icon_window) setinputfocus: [_app_icon_window windowNumber]]; } } } @end // NSApplication (Private)