mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-25 12:21:07 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/branches/gnustep_testplant_branch@36953 72102866-910b-0410-8b05-ffd578937521
544 lines
13 KiB
Objective-C
544 lines
13 KiB
Objective-C
/** <title>NSWindowController</title>
|
|
|
|
Copyright (C) 2000 Free Software Foundation, Inc.
|
|
|
|
Author: Carl Lindberg <Carl.Lindberg@hbo.com>
|
|
Date: 1999
|
|
Author: Fred Kiefer <FredKiefer@gmx.de>
|
|
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 <http://www.gnu.org/licenses/> or write to the
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#import <Foundation/NSArray.h>
|
|
#import <Foundation/NSBundle.h>
|
|
#import <Foundation/NSArray.h>
|
|
#import <Foundation/NSDictionary.h>
|
|
#import <Foundation/NSEnumerator.h>
|
|
#import <Foundation/NSException.h>
|
|
#import <Foundation/NSNotification.h>
|
|
#import <Foundation/NSString.h>
|
|
|
|
#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;
|
|
_owner = self;
|
|
|
|
[self setWindow: window];
|
|
if (_window != nil)
|
|
{
|
|
[self _windowDidLoad];
|
|
[self setDocument: nil];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) init
|
|
{
|
|
return [self initWithWindow: nil];
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
// Window Controllers are expect to release their own top-level objects
|
|
[_top_level_objects makeObjectsPerformSelector: @selector(release)];
|
|
[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
|
|
{
|
|
if ([self isWindowLoaded])
|
|
[[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.
|
|
|
|
// Note: The docs say that windowController{Will,Did}LoadNib: are sent
|
|
// to the window controller's document, but Apple's implementation
|
|
// really sends them to the owner of the nib. Since this behavior is
|
|
// more useful, in particular when non-document classes use a window
|
|
// controller, we implement it here too.
|
|
[self windowWillLoad];
|
|
if (_owner != self &&
|
|
[_owner respondsToSelector: @selector(windowControllerWillLoadNib:)])
|
|
{
|
|
[_owner windowControllerWillLoadNib: self];
|
|
}
|
|
|
|
[self loadWindow];
|
|
if ([self isWindowLoaded])
|
|
{
|
|
[self _windowDidLoad];
|
|
if (_owner != self &&
|
|
[_owner respondsToSelector: @selector(windowControllerDidLoadNib:)])
|
|
{
|
|
[_owner 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];
|
|
[_window setDocumentEdited: [_document isDocumentEdited]];
|
|
}
|
|
|
|
/* Make sure window sizes itself right */
|
|
if ([_window_frame_autosave_name length] > 0)
|
|
{
|
|
[_window setFrameAutosaveName: _window_frame_autosave_name];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (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's 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];
|
|
|
|
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
|