libs-gui/Source/NSWindowController.m
Wolfgang Lux 04e0376187 Show a document's represented file name in the title of its window
also when its display name is equal to the last path component of the
file name, since that is what NSDocument uses by default for
compatibility with OS X.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@30052 72102866-910b-0410-8b05-ffd578937521
2010-03-27 21:06:45 +00:00

531 lines
12 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.
*/
#include <Foundation/NSBundle.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSEnumerator.h>
#include <Foundation/NSException.h>
#include <Foundation/NSNotification.h>
#include <Foundation/NSString.h>
#include "AppKit/NSNibLoading.h"
#include "AppKit/NSPanel.h"
#include "AppKit/NSWindowController.h"
#include "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: @"NSOwner"];
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