2003-08-23 01:03:40 +00:00
|
|
|
/** <title>GSNibTemplates</title>
|
|
|
|
|
|
|
|
<abstract>Contains all of the private classes used in .gorm files.</abstract>
|
|
|
|
|
|
|
|
Copyright (C) 2003 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
Author: Gregory John Casamento
|
|
|
|
Date: July 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 Library 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
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
License along with this library;
|
|
|
|
If not, write to the Free Software Foundation,
|
|
|
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <Foundation/NSArray.h>
|
|
|
|
#include <Foundation/NSBundle.h>
|
|
|
|
#include <Foundation/NSCoder.h>
|
|
|
|
#include <Foundation/NSData.h>
|
|
|
|
#include <Foundation/NSDictionary.h>
|
|
|
|
#include <Foundation/NSDebug.h>
|
|
|
|
#include <Foundation/NSEnumerator.h>
|
|
|
|
#include <Foundation/NSException.h>
|
|
|
|
#include <Foundation/NSInvocation.h>
|
|
|
|
#include <Foundation/NSObjCRuntime.h>
|
|
|
|
#include <Foundation/NSPathUtilities.h>
|
|
|
|
#include <Foundation/NSFileManager.h>
|
|
|
|
#include <Foundation/NSString.h>
|
|
|
|
#include <Foundation/NSUserDefaults.h>
|
|
|
|
#include <Foundation/NSKeyValueCoding.h>
|
2003-12-30 05:45:40 +00:00
|
|
|
#include <Foundation/NSNotification.h>
|
2004-01-10 23:46:59 +00:00
|
|
|
#include <Foundation/NSArchiver.h>
|
2003-12-29 05:38:46 +00:00
|
|
|
#include <AppKit/NSMenu.h>
|
|
|
|
#include <AppKit/NSView.h>
|
|
|
|
#include <AppKit/NSTextView.h>
|
|
|
|
#include <AppKit/NSWindow.h>
|
2003-08-23 01:03:40 +00:00
|
|
|
#include <AppKit/NSNibLoading.h>
|
|
|
|
#include <AppKit/NSNibConnector.h>
|
|
|
|
#include <AppKit/NSApplication.h>
|
2003-08-30 05:01:47 +00:00
|
|
|
#include <GNUstepBase/GSObjCRuntime.h>
|
2003-08-23 01:03:40 +00:00
|
|
|
#include <GNUstepGUI/GSNibTemplates.h>
|
|
|
|
|
|
|
|
static const int currentVersion = 1; // GSNibItem version number...
|
|
|
|
|
|
|
|
@interface NSApplication (GSNibContainer)
|
|
|
|
- (void)_deactivateVisibleWindow: (NSWindow *)win;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSApplication (GSNibContainer)
|
|
|
|
/* Since awakeWithContext often gets called before the the app becomes
|
|
|
|
active, [win -orderFront:] requests get ignored, so we add the window
|
|
|
|
to the inactive list, so it gets sent an -orderFront when the app
|
|
|
|
becomes active. */
|
|
|
|
- (void) _deactivateVisibleWindow: (NSWindow *)win
|
|
|
|
{
|
|
|
|
if (_inactive)
|
|
|
|
[_inactive addObject: win];
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The GSNibContainer class manages the internals of a nib file.
|
|
|
|
*/
|
|
|
|
@implementation GSNibContainer
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSNibContainer class])
|
|
|
|
{
|
|
|
|
[self setVersion: GNUSTEP_NIB_VERSION];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-12-30 05:45:40 +00:00
|
|
|
- (void) awakeWithContext: (NSDictionary *)context
|
|
|
|
topLevelItems: (NSArray *)items
|
2003-08-23 01:03:40 +00:00
|
|
|
{
|
|
|
|
if (_isAwake == NO)
|
|
|
|
{
|
|
|
|
NSEnumerator *enumerator;
|
|
|
|
NSNibConnector *connection;
|
|
|
|
NSString *key;
|
|
|
|
NSArray *visible;
|
|
|
|
NSMenu *menu;
|
2003-12-23 18:45:18 +00:00
|
|
|
NSMutableArray *topLevelObjects;
|
|
|
|
id obj;
|
2003-08-23 01:03:40 +00:00
|
|
|
|
|
|
|
_isAwake = YES;
|
|
|
|
/*
|
|
|
|
* Add local entries into name table.
|
|
|
|
*/
|
|
|
|
if ([context count] > 0)
|
|
|
|
{
|
|
|
|
[nameTable addEntriesFromDictionary: context];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now establish all connections by taking the names
|
|
|
|
* stored in the connection objects, and replaciong them
|
|
|
|
* with the corresponding values from the name table
|
|
|
|
* before telling the connections to establish themselves.
|
|
|
|
*/
|
|
|
|
enumerator = [connections objectEnumerator];
|
|
|
|
while ((connection = [enumerator nextObject]) != nil)
|
|
|
|
{
|
|
|
|
id val;
|
|
|
|
|
|
|
|
val = [nameTable objectForKey: [connection source]];
|
|
|
|
[connection setSource: val];
|
|
|
|
val = [nameTable objectForKey: [connection destination]];
|
|
|
|
[connection setDestination: val];
|
|
|
|
[connection establishConnection];
|
|
|
|
}
|
|
|
|
|
2003-09-01 09:21:46 +00:00
|
|
|
/*
|
|
|
|
* See if there is a main menu to be set. Report #4815, mainMenu
|
|
|
|
* should be initialized before awakeFromNib is called.
|
|
|
|
*/
|
|
|
|
menu = [nameTable objectForKey: @"NSMenu"];
|
|
|
|
if (menu != nil && [menu isKindOfClass: [NSMenu class]] == YES)
|
|
|
|
{
|
|
|
|
[NSApp setMainMenu: menu];
|
|
|
|
}
|
|
|
|
|
2003-09-28 23:25:16 +00:00
|
|
|
/*
|
|
|
|
* Set the Services menu.
|
|
|
|
* Report #5205, Services/Window menu does not behave correctly.
|
|
|
|
*/
|
|
|
|
menu = [nameTable objectForKey: @"NSServicesMenu"];
|
|
|
|
if (menu != nil && [menu isKindOfClass: [NSMenu class]] == YES)
|
|
|
|
{
|
|
|
|
[NSApp setServicesMenu: menu];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the Services menu.
|
|
|
|
* Report #5205, Services/Window menu does not behave correctly.
|
|
|
|
*/
|
|
|
|
menu = [nameTable objectForKey: @"NSWindowsMenu"];
|
|
|
|
if (menu != nil && [menu isKindOfClass: [NSMenu class]] == YES)
|
|
|
|
{
|
|
|
|
[NSApp setWindowsMenu: menu];
|
|
|
|
}
|
|
|
|
|
2003-08-23 01:03:40 +00:00
|
|
|
/*
|
|
|
|
* Now tell all the objects that they have been loaded from
|
|
|
|
* a nib.
|
|
|
|
*/
|
|
|
|
enumerator = [nameTable keyEnumerator];
|
|
|
|
while ((key = [enumerator nextObject]) != nil)
|
|
|
|
{
|
|
|
|
if ([context objectForKey: key] == nil ||
|
2004-02-09 01:50:45 +00:00
|
|
|
[key isEqualToString: @"NSOwner"]) // we want to send the message to the owner
|
2003-08-23 01:03:40 +00:00
|
|
|
{
|
2003-12-29 05:38:46 +00:00
|
|
|
if([key isEqualToString: @"NSWindowsMenu"] == NO && // we don't want to send a message to these menus twice,
|
|
|
|
[key isEqualToString: @"NSServicesMenu"] == NO && // if they're custom classes.
|
|
|
|
[key isEqualToString: @"NSVisible"] == NO && // also exclude any other special parts of the nameTable.
|
|
|
|
[key isEqualToString: @"NSDeferred"] == NO &&
|
2004-02-09 01:50:45 +00:00
|
|
|
[key isEqualToString: @"NSTopLevelObjects"] == NO &&
|
2003-12-29 05:38:46 +00:00
|
|
|
[key isEqualToString: @"GSCustomClassMap"] == NO)
|
2003-08-23 01:03:40 +00:00
|
|
|
{
|
2003-12-29 05:38:46 +00:00
|
|
|
id o = [nameTable objectForKey: key];
|
|
|
|
if ([o respondsToSelector: @selector(awakeFromNib)])
|
|
|
|
{
|
|
|
|
[o awakeFromNib];
|
|
|
|
}
|
2003-08-23 01:03:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-12-23 18:45:18 +00:00
|
|
|
|
|
|
|
/*
|
2004-02-09 01:50:45 +00:00
|
|
|
* See if the user has passed in the NSTopLevelObjects key.
|
2003-12-23 18:45:18 +00:00
|
|
|
* This is an implementation of an undocumented, but commonly used feature
|
|
|
|
* of nib files to allow the release of the top level objects in the nib
|
|
|
|
* file.
|
|
|
|
*/
|
2004-02-09 01:50:45 +00:00
|
|
|
obj = [context objectForKey: @"NSTopLevelObjects"];
|
2003-12-23 18:45:18 +00:00
|
|
|
if([obj isKindOfClass: [NSMutableArray class]])
|
|
|
|
{
|
|
|
|
topLevelObjects = obj;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
topLevelObjects = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Retain all "top level" items so that, when the container is released, they will remain.
|
2003-12-30 05:45:40 +00:00
|
|
|
* The GSNibItems instantiated in the gorm need to be retained,
|
|
|
|
* since we are deallocating the container.
|
2003-12-23 18:45:18 +00:00
|
|
|
*/
|
|
|
|
enumerator = [nameTable keyEnumerator];
|
|
|
|
while ((key = [enumerator nextObject]) != nil)
|
|
|
|
{
|
2003-12-29 05:38:46 +00:00
|
|
|
if ([context objectForKey: key] == nil &&
|
|
|
|
[key isEqualToString: @"NSWindowsMenu"] == NO && // exclude special sections.
|
|
|
|
[key isEqualToString: @"NSServicesMenu"] == NO &&
|
|
|
|
[key isEqualToString: @"NSVisible"] == NO &&
|
|
|
|
[key isEqualToString: @"NSDeferred"] == NO &&
|
2004-02-09 01:50:45 +00:00
|
|
|
[key isEqualToString: @"NSTopLevelObjects"] == NO &&
|
2003-12-29 05:38:46 +00:00
|
|
|
[key isEqualToString: @"GSCustomClassMap"] == NO)
|
2003-12-23 18:45:18 +00:00
|
|
|
{
|
2003-12-29 05:38:46 +00:00
|
|
|
id o = [nameTable objectForKey: key];
|
2003-12-23 18:45:18 +00:00
|
|
|
// RETAIN all top-level items...
|
|
|
|
if (([o isKindOfClass: [NSMenu class]] == YES &&
|
|
|
|
[key isEqualToString: @"NSMenu"] == YES) || // the main menu...
|
|
|
|
([o isKindOfClass: [NSWindow class]] == YES) || // any windows...
|
2003-12-30 05:45:40 +00:00
|
|
|
([items containsObject: o] == YES))
|
2003-12-23 18:45:18 +00:00
|
|
|
{
|
|
|
|
if(topLevelObjects == nil)
|
|
|
|
{
|
2004-02-09 01:50:45 +00:00
|
|
|
// It is expected, if the NSTopLevelObjects key is not passed in,
|
2003-12-23 18:45:18 +00:00
|
|
|
// that the user has opted to either allow these objects to leak or
|
|
|
|
// to release them explicitly.
|
|
|
|
RETAIN(o);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We don't want to do the extra retain if the items are added to the
|
|
|
|
// array, since the array will do the retain for us. When the array
|
|
|
|
// is released, the top level objects should be released as well.
|
|
|
|
[topLevelObjects addObject: o];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-12-29 05:38:46 +00:00
|
|
|
|
2003-08-23 01:03:40 +00:00
|
|
|
/*
|
|
|
|
* See if there are objects that should be made visible.
|
|
|
|
*/
|
|
|
|
visible = [nameTable objectForKey: @"NSVisible"];
|
|
|
|
if (visible != nil
|
|
|
|
&& [visible isKindOfClass: [NSArray class]] == YES)
|
|
|
|
{
|
|
|
|
unsigned pos = [visible count];
|
|
|
|
|
|
|
|
while (pos-- > 0)
|
|
|
|
{
|
|
|
|
NSWindow *win = [visible objectAtIndex: pos];
|
|
|
|
if ([NSApp isActive])
|
|
|
|
[win orderFront: self];
|
|
|
|
else
|
|
|
|
[NSApp _deactivateVisibleWindow: win];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now remove any objects added from the context dictionary.
|
|
|
|
*/
|
|
|
|
if ([context count] > 0)
|
|
|
|
{
|
|
|
|
[nameTable removeObjectsForKeys: [context allKeys]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSMutableArray*) connections
|
|
|
|
{
|
|
|
|
return connections;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
RELEASE(nameTable);
|
|
|
|
RELEASE(connections);
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
|
|
|
{
|
|
|
|
[aCoder encodeObject: nameTable];
|
|
|
|
[aCoder encodeObject: connections];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]) != nil)
|
|
|
|
{
|
|
|
|
nameTable = [[NSMutableDictionary alloc] initWithCapacity: 8];
|
|
|
|
connections = [[NSMutableArray alloc] initWithCapacity: 8];
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder*)aCoder
|
|
|
|
{
|
|
|
|
int version = [aCoder versionForClassName: @"GSNibContainer"];
|
|
|
|
|
|
|
|
if(version == GNUSTEP_NIB_VERSION)
|
|
|
|
{
|
|
|
|
[aCoder decodeValueOfObjCType: @encode(id) at: &nameTable];
|
|
|
|
[aCoder decodeValueOfObjCType: @encode(id) at: &connections];
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSMutableDictionary*) nameTable
|
|
|
|
{
|
|
|
|
return nameTable;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
// The first standin objects here are for views and normal objects like controllers
|
|
|
|
// or data sources.
|
|
|
|
@implementation GSNibItem
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSNibItem class])
|
|
|
|
{
|
|
|
|
[self setVersion: currentVersion];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
RELEASE(theClass);
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
|
|
|
{
|
|
|
|
[aCoder encodeObject: theClass];
|
|
|
|
[aCoder encodeRect: theFrame];
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int)
|
|
|
|
at: &autoresizingMask];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder*)aCoder
|
|
|
|
{
|
|
|
|
int version = [aCoder versionForClassName:
|
|
|
|
NSStringFromClass([self class])];
|
2003-12-30 05:45:40 +00:00
|
|
|
id obj = nil;
|
2003-08-23 01:03:40 +00:00
|
|
|
|
|
|
|
if (version == 1)
|
|
|
|
{
|
|
|
|
Class cls;
|
|
|
|
unsigned int mask;
|
|
|
|
|
|
|
|
[aCoder decodeValueOfObjCType: @encode(id) at: &theClass];
|
|
|
|
theFrame = [aCoder decodeRect];
|
|
|
|
[aCoder decodeValueOfObjCType: @encode(unsigned int)
|
|
|
|
at: &mask];
|
|
|
|
|
|
|
|
cls = NSClassFromString(theClass);
|
|
|
|
if (cls == nil)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
format: @"Unable to find class '%@'", theClass];
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = [cls allocWithZone: [self zone]];
|
|
|
|
if (theFrame.size.height > 0 && theFrame.size.width > 0)
|
2003-12-29 05:38:46 +00:00
|
|
|
{
|
|
|
|
obj = [obj initWithFrame: theFrame];
|
|
|
|
}
|
2003-08-23 01:03:40 +00:00
|
|
|
else
|
2003-12-29 05:38:46 +00:00
|
|
|
{
|
|
|
|
obj = [obj init];
|
|
|
|
}
|
2003-08-23 01:03:40 +00:00
|
|
|
|
|
|
|
if ([obj respondsToSelector: @selector(setAutoresizingMask:)])
|
|
|
|
{
|
|
|
|
[obj setAutoresizingMask: mask];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (version == 0)
|
|
|
|
{
|
|
|
|
Class cls;
|
|
|
|
|
|
|
|
[aCoder decodeValueOfObjCType: @encode(id) at: &theClass];
|
|
|
|
theFrame = [aCoder decodeRect];
|
|
|
|
|
|
|
|
cls = NSClassFromString(theClass);
|
|
|
|
if (cls == nil)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
format: @"Unable to find class '%@'", theClass];
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = [cls allocWithZone: [self zone]];
|
|
|
|
if (theFrame.size.height > 0 && theFrame.size.width > 0)
|
2003-12-29 05:38:46 +00:00
|
|
|
{
|
|
|
|
obj = [obj initWithFrame: theFrame];
|
|
|
|
}
|
2003-08-23 01:03:40 +00:00
|
|
|
else
|
2003-12-29 05:38:46 +00:00
|
|
|
{
|
|
|
|
obj = [obj init];
|
|
|
|
}
|
2003-08-23 01:03:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSLog(@"no initWithCoder for this version");
|
|
|
|
}
|
2003-12-30 05:45:40 +00:00
|
|
|
|
|
|
|
// If this is a nib item and not a custom view, then we need to add it to
|
|
|
|
// the set of things to be retained.
|
|
|
|
if(obj != nil)
|
|
|
|
{
|
|
|
|
if([self isKindOfClass: [GSNibItem class]] == YES &&
|
|
|
|
[self isKindOfClass: [GSCustomView class]] == NO)
|
|
|
|
{
|
|
|
|
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
|
|
|
[nc postNotificationName: @"__GSInternalNibItemAddedNotification"
|
|
|
|
object: obj];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// release self and return the object this represents...
|
|
|
|
RELEASE(self);
|
|
|
|
return obj;
|
2003-08-23 01:03:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSCustomView
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSCustomView class])
|
|
|
|
{
|
|
|
|
[self setVersion: currentVersion];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
|
|
|
{
|
|
|
|
[super encodeWithCoder: aCoder];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder*)aCoder
|
|
|
|
{
|
|
|
|
return [super initWithCoder: aCoder];
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
/*
|
|
|
|
These stand-ins are here for use by GUI elements within Gorm. Since each gui element
|
|
|
|
has it's own "designated initializer" it's important to provide a division between these
|
|
|
|
so that when they are loaded, the application will call the correct initializer.
|
|
|
|
|
|
|
|
Some "tricks" are employed in this code. For instance the use of initWithCoder and
|
|
|
|
encodeWithCoder directly as opposed to using the encodeObjC.. methods is the obvious
|
|
|
|
standout. To understand this it's necessary to explain a little about how encoding itself
|
|
|
|
works.
|
|
|
|
|
|
|
|
When the model is saved by the Interface Builder (whether Gorm or another
|
|
|
|
IB equivalent) these classes should be used to substitute for the actual classes. The actual
|
|
|
|
classes are encoded as part of it, but since they are being replaced we can't use the normal
|
|
|
|
encode methods to do it and must encode it directly.
|
|
|
|
|
|
|
|
Also, the reason for encoding the superclass itself is that by doing so the unarchiver knows
|
|
|
|
what version is referred to by the encoded object. This way we can replace the object with
|
|
|
|
a substitute class which will allow it to create itself as the custom class when read it by
|
|
|
|
the application, and using the encoding system to do it in a clean way.
|
|
|
|
*/
|
|
|
|
@implementation GSClassSwapper
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSClassSwapper class])
|
|
|
|
{
|
|
|
|
[self setVersion: GSSWAPPER_VERSION];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithObject: (id)object className: (NSString *)className superClassName: (NSString *)superClassName
|
|
|
|
{
|
|
|
|
if((self = [self init]) != nil)
|
|
|
|
{
|
2003-08-24 17:22:11 +00:00
|
|
|
NSDebugLog(@"Created template %@ -> %@",NSStringFromClass([self class]), className);
|
2003-08-23 01:03:40 +00:00
|
|
|
ASSIGN(_object, object);
|
2003-08-25 04:50:09 +00:00
|
|
|
ASSIGN(_className, [className copy]);
|
2003-08-24 17:22:11 +00:00
|
|
|
NSAssert(![className isEqualToString: superClassName], NSInvalidArgumentException);
|
2003-08-23 01:03:40 +00:00
|
|
|
_superClass = NSClassFromString(superClassName);
|
|
|
|
if(_superClass == nil)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
format: @"Unable to find class '%@'", superClassName];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- init
|
|
|
|
{
|
|
|
|
if((self = [super init]) != nil)
|
|
|
|
{
|
|
|
|
_className = nil;
|
|
|
|
_superClass = nil;
|
|
|
|
_object = nil;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setClassName: (NSString *)name
|
|
|
|
{
|
2003-08-25 04:50:09 +00:00
|
|
|
ASSIGN(_className, [name copy]);
|
2003-08-23 01:03:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)className
|
|
|
|
{
|
|
|
|
return _className;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder *)coder
|
|
|
|
{
|
|
|
|
id obj = nil;
|
|
|
|
int version = [coder versionForClassName: @"GSClassSwapper"];
|
|
|
|
if(version == 0)
|
|
|
|
{
|
|
|
|
if((self = [super init]) != nil)
|
|
|
|
{
|
2004-01-10 23:46:59 +00:00
|
|
|
NSUnarchiver *unarchiver = (NSUnarchiver *)coder;
|
|
|
|
|
2003-08-23 01:03:40 +00:00
|
|
|
// decode class/superclass...
|
|
|
|
[coder decodeValueOfObjCType: @encode(id) at: &_className];
|
|
|
|
[coder decodeValueOfObjCType: @encode(Class) at: &_superClass];
|
|
|
|
|
|
|
|
// if we are living within the interface builder app, then don't try to
|
|
|
|
// morph into the subclass.
|
|
|
|
if([self respondsToSelector: @selector(isInInterfaceBuilder)])
|
|
|
|
{
|
2004-01-10 23:46:59 +00:00
|
|
|
obj = [_superClass alloc];
|
2003-08-23 01:03:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Class aClass = NSClassFromString(_className);
|
|
|
|
if(aClass == 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
format: @"Unable to find class '%@'", _className];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the object... dont call decode, since this wont
|
|
|
|
// allow us to instantiate the class we want.
|
2004-01-10 23:46:59 +00:00
|
|
|
obj = [aClass alloc];
|
2003-08-23 01:03:40 +00:00
|
|
|
}
|
2004-01-10 23:46:59 +00:00
|
|
|
|
|
|
|
// inform the coder that this object is to replace the template in all cases.
|
|
|
|
[unarchiver replaceObject: self withObject: obj];
|
|
|
|
obj = [obj initWithCoder: coder]; // unarchive the object...
|
2003-08-23 01:03:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do this here since we are not using decode to do this.
|
|
|
|
// Normally decode does a retain, so we must do it here.
|
2003-12-30 01:52:26 +00:00
|
|
|
// RETAIN(obj);
|
2003-08-23 01:03:40 +00:00
|
|
|
|
|
|
|
// change the class of the instance to the one we want to see...
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) encodeWithCoder: (NSCoder *)aCoder
|
|
|
|
{
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(id) at: &_className];
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(Class) at: &_superClass];
|
|
|
|
|
|
|
|
if(_object != nil)
|
|
|
|
{
|
|
|
|
// Don't call encodeValue, the way templates are used will prevent
|
|
|
|
// it from being saved correctly. Just call encodeWithCoder directly.
|
|
|
|
[_object encodeWithCoder: aCoder];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSWindowTemplate
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSWindowTemplate class])
|
|
|
|
{
|
|
|
|
[self setVersion: GSWINDOWT_VERSION];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)deferFlag
|
|
|
|
{
|
|
|
|
return _deferFlag;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setDeferFlag: (BOOL)flag
|
|
|
|
{
|
|
|
|
_deferFlag = flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NSCoding...
|
|
|
|
- (id) initWithCoder: (NSCoder *)coder
|
|
|
|
{
|
|
|
|
id obj = [super initWithCoder: coder];
|
|
|
|
if(obj != nil)
|
|
|
|
{
|
|
|
|
NSView *contentView = nil;
|
|
|
|
|
|
|
|
// decode the defer flag...
|
|
|
|
[coder decodeValueOfObjCType: @encode(BOOL) at: &_deferFlag];
|
|
|
|
|
2003-08-25 04:50:09 +00:00
|
|
|
if(![self respondsToSelector: @selector(isInInterfaceBuilder)])
|
|
|
|
{
|
2003-08-30 05:01:47 +00:00
|
|
|
if(GSGetInstanceMethodNotInherited([obj class], @selector(initWithContentRect:styleMask:backing:defer:)) != NULL)
|
|
|
|
{
|
|
|
|
// if we are not in interface builder, call
|
|
|
|
// designated initializer per spec...
|
|
|
|
contentView = [obj contentView];
|
|
|
|
obj = [obj initWithContentRect: [obj frame]
|
|
|
|
styleMask: [obj styleMask]
|
|
|
|
backing: [obj backingType]
|
|
|
|
defer: _deferFlag];
|
|
|
|
|
|
|
|
// set the content view back
|
|
|
|
[obj setContentView: contentView];
|
|
|
|
}
|
2003-08-25 04:50:09 +00:00
|
|
|
}
|
2003-08-28 04:38:22 +00:00
|
|
|
RELEASE(self);
|
2003-08-23 01:03:40 +00:00
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) encodeWithCoder: (NSCoder *)coder
|
|
|
|
{
|
|
|
|
[super encodeWithCoder: coder];
|
|
|
|
[coder encodeValueOfObjCType: @encode(BOOL) at: &_deferFlag];
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSViewTemplate
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSViewTemplate class])
|
|
|
|
{
|
|
|
|
[self setVersion: GSVIEWT_VERSION];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder *)coder
|
|
|
|
{
|
|
|
|
id obj = [super initWithCoder: coder];
|
|
|
|
if(obj != nil)
|
|
|
|
{
|
2003-08-28 04:38:22 +00:00
|
|
|
if(![self respondsToSelector: @selector(isInInterfaceBuilder)])
|
|
|
|
{
|
2003-08-30 05:01:47 +00:00
|
|
|
if(GSGetInstanceMethodNotInherited([obj class],@selector(initWithFrame:)) != NULL)
|
|
|
|
{
|
|
|
|
NSRect theFrame = [obj frame];
|
|
|
|
obj = [obj initWithFrame: theFrame];
|
|
|
|
}
|
2003-08-28 04:38:22 +00:00
|
|
|
}
|
2003-08-23 01:03:40 +00:00
|
|
|
RELEASE(self);
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
// Template for any classes which derive from NSText
|
|
|
|
@implementation GSTextTemplate
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSTextTemplate class])
|
|
|
|
{
|
|
|
|
[self setVersion: GSTEXTT_VERSION];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder *)coder
|
|
|
|
{
|
|
|
|
id obj = [super initWithCoder: coder];
|
|
|
|
if(obj != nil)
|
|
|
|
{
|
2003-08-28 04:38:22 +00:00
|
|
|
if(![self respondsToSelector: @selector(isInInterfaceBuilder)])
|
|
|
|
{
|
2003-08-30 05:01:47 +00:00
|
|
|
if(GSGetInstanceMethodNotInherited([obj class],@selector(initWithFrame:)) != NULL)
|
|
|
|
{
|
|
|
|
NSRect theFrame = [obj frame];
|
|
|
|
obj = [obj initWithFrame: theFrame];
|
|
|
|
}
|
2003-08-28 04:38:22 +00:00
|
|
|
}
|
2003-08-23 01:03:40 +00:00
|
|
|
RELEASE(self);
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
// Template for any classes which derive from GSTextView
|
|
|
|
@implementation GSTextViewTemplate
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSTextViewTemplate class])
|
|
|
|
{
|
|
|
|
[self setVersion: GSTEXTVIEWT_VERSION];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder *)coder
|
|
|
|
{
|
|
|
|
id obj = [super initWithCoder: coder];
|
|
|
|
if(obj != nil)
|
|
|
|
{
|
2003-08-28 04:38:22 +00:00
|
|
|
if(![self respondsToSelector: @selector(isInInterfaceBuilder)])
|
|
|
|
{
|
2003-08-30 05:01:47 +00:00
|
|
|
if(GSGetInstanceMethodNotInherited([obj class],@selector(initWithFrame:textContainer:)) != NULL)
|
|
|
|
{
|
|
|
|
NSRect theFrame = [obj frame];
|
|
|
|
id textContainer = [obj textContainer];
|
|
|
|
obj = [obj initWithFrame: theFrame
|
|
|
|
textContainer: textContainer];
|
|
|
|
}
|
2003-08-28 04:38:22 +00:00
|
|
|
}
|
2003-08-23 01:03:40 +00:00
|
|
|
RELEASE(self);
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
// Template for any classes which derive from NSMenu.
|
|
|
|
@implementation GSMenuTemplate
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSMenuTemplate class])
|
|
|
|
{
|
|
|
|
[self setVersion: GSMENUT_VERSION];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder *)coder
|
|
|
|
{
|
|
|
|
id obj = [super initWithCoder: coder];
|
|
|
|
if(obj != nil)
|
|
|
|
{
|
2003-08-28 04:38:22 +00:00
|
|
|
if(![self respondsToSelector: @selector(isInInterfaceBuilder)])
|
|
|
|
{
|
2003-08-30 05:01:47 +00:00
|
|
|
if(GSGetInstanceMethodNotInherited([obj class],@selector(initWithTitle:)) != NULL)
|
|
|
|
{
|
|
|
|
NSString *theTitle = [obj title];
|
|
|
|
obj = [obj initWithTitle: theTitle];
|
|
|
|
}
|
2003-08-28 04:38:22 +00:00
|
|
|
}
|
2003-08-23 01:03:40 +00:00
|
|
|
RELEASE(self);
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
// Template for any classes which derive from NSControl
|
|
|
|
@implementation GSControlTemplate
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSControlTemplate class])
|
|
|
|
{
|
|
|
|
[self setVersion: GSCONTROLT_VERSION];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder *)coder
|
|
|
|
{
|
|
|
|
id obj = [super initWithCoder: coder];
|
|
|
|
if(obj != nil)
|
|
|
|
{
|
2003-10-25 20:50:08 +00:00
|
|
|
/*
|
2003-08-28 04:38:22 +00:00
|
|
|
if(![self respondsToSelector: @selector(isInInterfaceBuilder)])
|
|
|
|
{
|
2003-08-30 05:01:47 +00:00
|
|
|
if(GSGetInstanceMethodNotInherited([obj class],@selector(initWithFrame:)) != NULL)
|
|
|
|
{
|
|
|
|
NSRect theFrame = [obj frame];
|
|
|
|
obj = [obj initWithFrame: theFrame];
|
|
|
|
}
|
2003-08-28 04:38:22 +00:00
|
|
|
}
|
2003-10-25 20:50:08 +00:00
|
|
|
*/
|
2003-08-25 04:50:09 +00:00
|
|
|
RELEASE(self);
|
2003-08-23 01:03:40 +00:00
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSObjectTemplate
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [GSObjectTemplate class])
|
|
|
|
{
|
|
|
|
[self setVersion: GSOBJECTT_VERSION];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder *)coder
|
|
|
|
{
|
|
|
|
id obj = [super initWithCoder: coder];
|
|
|
|
if(obj != nil)
|
|
|
|
{
|
2003-08-30 05:01:47 +00:00
|
|
|
if(![self respondsToSelector: @selector(isInInterfaceBuilder)])
|
|
|
|
{
|
|
|
|
if(GSGetInstanceMethodNotInherited([obj class],@selector(init)) != NULL)
|
|
|
|
{
|
|
|
|
obj = [self init];
|
|
|
|
}
|
|
|
|
}
|
2003-08-23 01:03:40 +00:00
|
|
|
RELEASE(self);
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSObject (NibInstantiation)
|
|
|
|
- (id) nibInstantiate;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSObject (NibInstantiation)
|
|
|
|
- (id) nibInstantiate
|
|
|
|
{
|
|
|
|
// default implementation of nibInstantiate
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
// Order in this factory method is very important. Which template to create must be determined
|
|
|
|
// in sequence because of the class hierarchy.
|
|
|
|
@implementation GSTemplateFactory
|
|
|
|
+ (id) templateForObject: (id) object
|
|
|
|
withClassName: (NSString *)className
|
|
|
|
withSuperClassName: (NSString *)superClassName
|
|
|
|
{
|
|
|
|
id template = nil;
|
|
|
|
if(object != nil)
|
|
|
|
{
|
|
|
|
// NSData *objectData = nil;
|
|
|
|
// [archiver encodeRootObject: object];
|
|
|
|
// objectData = [archiver archiverData];
|
|
|
|
if ([object isKindOfClass: [NSWindow class]])
|
|
|
|
{
|
|
|
|
template = [[GSWindowTemplate alloc] initWithObject: object
|
|
|
|
className: className
|
|
|
|
superClassName: superClassName];
|
|
|
|
}
|
|
|
|
else if ([object isKindOfClass: [NSTextView class]])
|
|
|
|
{
|
|
|
|
template = [[GSTextViewTemplate alloc] initWithObject: object
|
|
|
|
className: className
|
|
|
|
superClassName: superClassName];
|
|
|
|
}
|
|
|
|
else if ([object isKindOfClass: [NSText class]])
|
|
|
|
{
|
|
|
|
template = [[GSTextTemplate alloc] initWithObject: object
|
|
|
|
className: className
|
|
|
|
superClassName: superClassName];
|
|
|
|
}
|
|
|
|
else if ([object isKindOfClass: [NSControl class]])
|
|
|
|
{
|
|
|
|
template = [[GSControlTemplate alloc] initWithObject: object
|
|
|
|
className: className
|
|
|
|
superClassName: superClassName];
|
|
|
|
}
|
|
|
|
else if ([object isKindOfClass: [NSView class]])
|
|
|
|
{
|
|
|
|
template = [[GSViewTemplate alloc] initWithObject: object
|
|
|
|
className: className
|
|
|
|
superClassName: superClassName];
|
|
|
|
}
|
|
|
|
else if ([object isKindOfClass: [NSMenu class]])
|
|
|
|
{
|
|
|
|
template = [[GSMenuTemplate alloc] initWithObject: object
|
|
|
|
className: className
|
|
|
|
superClassName: superClassName];
|
|
|
|
}
|
|
|
|
else if ([object isKindOfClass: [NSObject class]]) // for gui elements derived from NSObject
|
|
|
|
{
|
|
|
|
template = [[GSObjectTemplate alloc] initWithObject: object
|
|
|
|
className: className
|
|
|
|
superClassName: superClassName];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return template;
|
|
|
|
}
|
|
|
|
@end
|