/** NSWindowController Copyright (C) 2000 Free Software Foundation, Inc. Author: Carl Lindberg Date: 1999 Author: Fred Kiefer Date: Aug 2003 This file is part of the GNUstep GUI Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, see or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #import #import #import #import #import #import #import "AppKit/NSNib.h" #import "AppKit/NSNibLoading.h" #import "AppKit/NSPanel.h" #import "AppKit/NSWindowController.h" #import "NSDocumentFrameworkPrivate.h" @implementation NSWindowController + (void) initialize { if (self == [NSWindowController class]) { [self setVersion: 1]; } } - (id) initWithWindowNibName: (NSString *)windowNibName { return [self initWithWindowNibName: windowNibName owner: self]; } - (id) initWithWindowNibName: (NSString *)windowNibName owner: (id)owner { if (windowNibName == nil) { [NSException raise: NSInvalidArgumentException format: @"attempt to init NSWindowController with nil windowNibName"]; } if (owner == nil) { [NSException raise: NSInvalidArgumentException format: @"attempt to init NSWindowController with nil owner"]; } self = [self initWithWindow: nil]; if (!self) return nil; ASSIGN(_window_nib_name, windowNibName); _owner = owner; return self; } - (id) initWithWindowNibPath: (NSString *)windowNibPath owner: (id)owner { if (windowNibPath == nil) { [NSException raise: NSInvalidArgumentException format: @"attempt to init NSWindowController with nil windowNibPath"]; } if (owner == nil) { [NSException raise: NSInvalidArgumentException format: @"attempt to init NSWindowController with nil owner"]; } self = [self initWithWindow: nil]; if (!self) return nil; ASSIGN(_window_nib_path, windowNibPath); _owner = owner; return self; } - (id) initWithWindow: (NSWindow *)window { self = [super init]; if (!self) return nil; ASSIGN(_window_frame_autosave_name, @""); _wcFlags.should_cascade = YES; //_wcFlags.should_close_document = NO; [self setWindow: window]; if (_window != nil) { [self _windowDidLoad]; } [self setDocument: nil]; return self; } - (id) init { return [self initWithWindow: nil]; } - (void) dealloc { [self setWindow: nil]; RELEASE(_window_nib_name); RELEASE(_window_nib_path); RELEASE(_window_frame_autosave_name); RELEASE(_top_level_objects); [super dealloc]; } - (NSString *) windowNibName { if ((_window_nib_name == nil) && (_window_nib_path != nil)) { return [[_window_nib_path lastPathComponent] stringByDeletingPathExtension]; } return _window_nib_name; } - (NSString *)windowNibPath { if ((_window_nib_name != nil) && (_window_nib_path == nil)) { NSString *path; path = [[NSBundle bundleForClass: [_owner class]] pathForNibResource: _window_nib_name]; if (path == nil) path = [[NSBundle mainBundle] pathForNibResource: _window_nib_name]; return path; } return _window_nib_path; } - (id) owner { return _owner; } /** Sets the document associated with this controller. A document automatically calls this method when adding a window controller to its list of window controllers. You should not call this method directly when using NSWindowController with an NSDocument or subclass. */ - (void) setDocument: (NSDocument *)document { // As the document retains us, we only keep a week reference. _document = document; [self synchronizeWindowTitleWithDocumentName]; if (_document == nil) { /* If you want the window to be deallocated when closed, you need to observe the NSWindowWillCloseNotification (or implement the window's delegate windowWillClose: method) and autorelease the window controller in that method. That will then release the window when the window controller is released. */ [_window setReleasedWhenClosed: NO]; } else { /* When a window owned by a document is closed, it is released and the window controller is removed from the documents list of controllers. */ [_window setReleasedWhenClosed: YES]; } } - (id) document { return _document; } - (void) setDocumentEdited: (BOOL)flag { [[self window] setDocumentEdited: flag]; } - (void) setWindowFrameAutosaveName:(NSString *)name { ASSIGN(_window_frame_autosave_name, name); if ([self isWindowLoaded]) { [[self window] setFrameAutosaveName: name ? (id)name : (id)@""]; } } - (NSString *) windowFrameAutosaveName { return _window_frame_autosave_name; } - (void) setShouldCloseDocument: (BOOL)flag { _wcFlags.should_close_document = flag; } - (BOOL) shouldCloseDocument { return _wcFlags.should_close_document; } - (void) setShouldCascadeWindows: (BOOL)flag { _wcFlags.should_cascade = flag; } - (BOOL) shouldCascadeWindows { return _wcFlags.should_cascade; } - (void) close { [_window close]; } - (void) _windowWillClose: (NSNotification *)notification { if ([notification object] == _window) { /* We only need to do something if the window is set to be released when closed (which should only happen if _document != nil). In this case, we release everything; otherwise, well the window is closed but nothing is released so there's nothing to do here. */ if ([_window isReleasedWhenClosed]) { RETAIN(self); if ([_window delegate] == self) { [_window setDelegate: nil]; } if ([_window windowController] == self) { [_window setWindowController: nil]; } /* * If the window is set to isReleasedWhenClosed, it will release * itself, so we have to retain it once more. * * Apple's implementation doesn't seem to deal with this case, and * crashes if isReleaseWhenClosed is set. */ RETAIN(_window); [self setWindow: nil]; [_document _removeWindowController: self]; AUTORELEASE(self); } } } - (NSWindow *) window { if (_window == nil && ![self isWindowLoaded]) { // Do all the notifications. Yes, the docs say this should // be implemented here instead of in -loadWindow itself. [self windowWillLoad]; if ([_document respondsToSelector: @selector(windowControllerWillLoadNib:)]) { [_document windowControllerWillLoadNib:self]; } [self loadWindow]; [self _windowDidLoad]; if ([_document respondsToSelector: @selector(windowControllerDidLoadNib:)]) { [_document windowControllerDidLoadNib:self]; } } return _window; } /** Sets the window that this controller managers to aWindow. The old window is released. */ - (void) setWindow: (NSWindow *)aWindow { NSNotificationCenter *nc; if (_window == aWindow) { return; } nc = [NSNotificationCenter defaultCenter]; if (_window != nil) { NSResponder *responder; [nc removeObserver: self name: NSWindowWillCloseNotification object: _window]; // Remove self from the responder chain responder = _window; while (responder && [responder nextResponder] != self) { responder = [responder nextResponder]; } [responder setNextResponder: [self nextResponder]]; [_window setWindowController: nil]; } ASSIGN(_window, aWindow); if (_window != nil) { [_window setWindowController: self]; // Put self into the responder chain [self setNextResponder: [_window nextResponder]]; [_window setNextResponder: self]; [nc addObserver: self selector: @selector(_windowWillClose:) name: NSWindowWillCloseNotification object: _window]; /* For information on the following, see the description in -setDocument: */ if (_document == nil) { [_window setReleasedWhenClosed: NO]; } else { [_window setReleasedWhenClosed: YES]; } } } - (IBAction) showWindow: (id)sender { NSWindow *window = [self window]; if ([window isKindOfClass: [NSPanel class]] && [(NSPanel*)window becomesKeyOnlyIfNeeded]) { [window orderFront: sender]; } else { [window makeKeyAndOrderFront: sender]; } } - (NSString *) windowTitleForDocumentDisplayName: (NSString *)displayName { return displayName; } - (void) synchronizeWindowTitleWithDocumentName { if ((_document != nil) && [self isWindowLoaded]) { NSString *filename = [_document fileName]; NSString *displayName = [_document displayName]; NSString *title = [self windowTitleForDocumentDisplayName: displayName]; /* If they just want to display the filename, use the fancy method */ /* NB For compatibility with Mac OS X, a document display name is equal to its last path component, so we check for that here too */ if (filename != nil && ([title isEqualToString: filename] || [title isEqualToString: [filename lastPathComponent]])) { [_window setTitleWithRepresentedFilename: filename]; } else { if (filename) [_window setRepresentedFilename: filename]; [_window setTitle: title]; } } } - (BOOL) isWindowLoaded { return _wcFlags.nib_is_loaded; } - (void) windowDidLoad { } - (void) windowWillLoad { } - (void) _windowDidLoad { _wcFlags.nib_is_loaded = YES; [self synchronizeWindowTitleWithDocumentName]; /* Make sure window sizes itself right */ if ([_window_frame_autosave_name length] > 0) { [_window setFrameUsingName: _window_frame_autosave_name]; [_window setFrameAutosaveName: _window_frame_autosave_name]; } if ([self shouldCascadeWindows]) { static NSPoint nextWindowLocation = { 0.0, 0.0 }; /* * cascadeTopLeftFromPoint will "wrap" the point back to the * top left if the normal cascading will cause the window to go * off the screen. In Apple's implementation, this wraps to the * extreme top of the screen, and offset only a small amount * from the left. */ nextWindowLocation = [_window cascadeTopLeftFromPoint: nextWindowLocation]; } [self windowDidLoad]; } - (void) loadWindow { NSDictionary *table; if ([self isWindowLoaded]) { return; } table = [NSDictionary dictionaryWithObject: _owner forKey: NSNibOwner]; if ([NSBundle loadNibFile: [self windowNibPath] externalNameTable: table withZone: [_owner zone]]) { _wcFlags.nib_is_loaded = YES; if (_window == nil && _document != nil && _owner == _document) { [self setWindow: [_document _transferWindowOwnership]]; } else { // The window was already retained by the NIB loading. RELEASE(_window); } } else { if (_window_nib_name != nil) { NSLog (@"%@: could not load nib named %@.nib", [self class], _window_nib_name); } } } - (id) initWithCoder: (NSCoder *)coder { if ([coder allowsKeyedCoding] || [coder versionForClassName: @"NSWindowController"] >= 1) { self = [super initWithCoder: coder]; if (!self) return nil; ASSIGN(_window_frame_autosave_name, @""); _wcFlags.should_cascade = YES; //_wcFlags.should_close_document = NO; return self; } else { /* backward compatibility: old NSWindowController instances are not subclasses of NSResponder, but of NSObject */ return [self init]; } } - (void) encodeWithCoder: (NSCoder *)coder { // What are we supposed to encode? Window nib name? Or should these // be empty, just to conform to NSCoding, so we do an -init on // unarchival. ? [super encodeWithCoder: coder]; } @end