Files
cappuccino/AppKit/CPTheme.j
T
2009-03-16 17:00:02 -07:00

368 lines
8.8 KiB
Plaintext

/*
* CPTheme.j
* AppKit
*
* Created by Francisco Tolmasky.
* Copyright 2009, 280 North, Inc.
*
* 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.1 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; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import <Foundation/CPObject.j>
@import <AppKit/_CPCibCustomResource.j>
@import <AppKit/_CPCibKeyedUnarchiver.j>
@import "CPThemedAttribute.j"
@implementation CPBlend : CPObject
{
CPBundle _bundle;
CPArray _themes @accessors(readonly);
id _loadDelegate;
}
- (id)initWithContentsOfURL:(CPURL)aURL
{
self = [super init];
if (self)
{
_bundle = [[CPBundle alloc] initWithPath:aURL + "/Info.plist"];
}
return self;
}
- (void)loadWithDelegate:(id)aDelegate
{
_loadDelegate = aDelegate;
[_bundle loadWithDelegate:self];
}
- (void)bundleDidFinishLoading:(CPBundle)aBundle
{
var paths = [_bundle objectForInfoDictionaryKey:@"CPBundleReplacedFiles"],
index = 0,
count = paths.length,
bundlePath = [_bundle bundlePath];
while (count--)
{
var path = paths[count];
if ([path pathExtension] === "keyedtheme")
{
var unarchiver = [[_CPThemeKeyedUnarchiver alloc]
initForReadingWithData:[CPData dataWithString:objj_files[bundlePath + '/' + path].contents]
bundle:_bundle];
[unarchiver decodeObjectForKey:@"root"];
[unarchiver finishDecoding];
}
}
[_loadDelegate blendDidFinishLoading:self];
}
@end
var CPThemesByName = nil,
CPThemeDefaultTheme = nil,
CPThemeViewClass = Nil,
CPThemeWindowClass = Nil;
@implementation CPTheme : CPObject
{
CPString _name;
Class _activeClass;
JSObject _classTable;
}
+ (void)initialize
{
if (self !== [CPTheme class])
return;
CPThemesByName = {};
CPThemeViewClass = [CPView class];
CPThemeWindowClass = [CPWindow class];
var bundle = [CPBundle bundleForClass:self];
defaultThemePath = [bundle objectForInfoDictionaryKey:@"Default Themes Path"],
defaultThemeName = [bundle objectForInfoDictionaryKey:@"Default Theme Name"];
if (!defaultThemePath)
return;
defaultThemePath = [bundle pathForResource:defaultThemePath];
var file = objj_files[defaultThemePath + "/Info.plist"];
if (!file)
return;
// Oh boy are we tricky.
var infoDictionary = CPPropertyListCreateFromData([CPData dataWithString:objj_files[defaultThemePath + "/Info.plist"].contents]);
themes = [infoDictionary objectForKey:@"CPBundleReplacedFiles"],
themeIndex = 0,
themeCount = [themes count];
for (; themeIndex < themeCount; ++themeIndex)
{
var unarchiver = [[_CPThemeKeyedUnarchiver alloc]
initForReadingWithData:[CPData dataWithString:objj_files[defaultThemePath + '/' + themes[themeIndex]].contents]
bundle:file.bundle];
[unarchiver decodeObjectForKey:@"root"];
[unarchiver finishDecoding];
}
[self setDefaultTheme:[CPTheme themeNamed:defaultThemeName]];
}
+ (void)setDefaultTheme:(CPTheme)aTheme
{
CPThemeDefaultTheme = aTheme;
}
+ (CPTheme)defaultTheme
{
return CPThemeDefaultTheme;
}
+ (CPTheme)themeNamed:(CPString)aName
{
return CPThemesByName[aName];
}
+ (void)loadThemesAtURL:(CPURL)aURL delegate:(id)aLoadDelegate
{
}
- (id)initWithName:(CPString)aName
{
self = [super init];
if (self)
{
_name = aName;
_classTable = {};
_classTable[[[self class] className]] = {};
CPThemesByName[_name] = aName;
}
return self;
}
- (CPString)name
{
return _name;
}
- (void)setActiveClass:(Class)aClass
{
_activeClass = aClass;
}
- (Class)activeClass
{
return _activeClass;
}
- (id)valueForAttributeName:(CPString)anAttributeName inClass:(CPClass)aClass
{
if (!aClass)
return nil;
var className = [aClass className],
table = _classTable[className];
if (!table)
return nil;
var value = table[anAttributeName];
if (!value)
return nil;
return value;
}
- (void)takeThemeFromObject:(id)anObject
{
var attributes = [anObject _themedAttributes],
attributeName = nil,
attributeNames = [attributes keyEnumerator],
objectClass = [anObject class];
while (attributeName = [attributeNames nextObject])
[self addValue:[attributes objectForKey:attributeName] forAttributeName:attributeName inClass:objectClass];
}
- (void)setDefaultValue:(id)aValue forAttributeName:(CPString)anAttributeName
{
[self addValue:aValue forAttributeName:anAttributeName inClass:[self class]];
}
- (void)addValue:(id)aValue forAttributeName:(CPString)anAttributeName inClass:(Class)aClass
{
if (!aValue)
return;
var className = [aClass className],
table = _classTable[className];
if (!table)
{
var classNames = [];
while (!table && (aClass !== CPThemeViewClass) && (aClass !== CPThemeWindowClass))
{
classNames.push(className);
aClass = [aClass superclass];
className = [aClass className];
table = _classTable[className];
}
if (!table)
table = _classTable[[[self class] className]];
var count = [classNames count];
while (count--)
{
className = classNames[count];
_classTable[className] = {};
_classTable[className].prototype = table;
table = _classTable[className];
}
}
var existingAttribute = table[anAttributeName];
if (existingAttribute)
table[anAttributeName] = [existingAttribute themedAttributeMergedWithThemedAttribute:aValue];
else
table[anAttributeName] = aValue;
}
@end
var CPThemeNameKey = @"CPThemeNameKey",
CPThemeClassNamesArrayKey = @"CPThemeClassNamesArrayKey";
@implementation CPTheme (CPCoding)
- (id)initWithCoder:(CPCoder)aCoder
{
self = [super init];
_name = [aCoder decodeObjectForKey:CPThemeNameKey];
_classTable = {};
CPThemesByName[_name] = self;
if (self)
{
var classNamesArray = [aCoder decodeObjectForKey:CPThemeClassNamesArrayKey],
count = classNamesArray.length;
while (count--)
{
var className = classNamesArray[count],
theClass = objj_getClass(className),
values = [aCoder decodeObjectForKey:className],
keys = [values allKeys],
keyCount = keys.length;
while (keyCount--)
{
var key = keys[keyCount];
[self addValue:[values objectForKey:key] forAttributeName:key inClass:theClass];
}
}
}
return self;
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
var classNamesArray = [];
for (var className in _classTable)
{
if (_classTable.hasOwnProperty(className))
{
var values = _classTable[className],
valuesDictionary = [CPDictionary dictionary];
for (var key in values)
if (key !== "prototype" && values.hasOwnProperty(key))
[valuesDictionary setObject:values[key] forKey:key];
[aCoder encodeObject:valuesDictionary forKey:className];
classNamesArray.push(className);
}
}
[aCoder encodeObject:_name forKey:CPThemeNameKey];
[aCoder encodeObject:classNamesArray forKey:CPThemeClassNamesArrayKey];
}
@end
@implementation _CPThemeKeyedUnarchiver : CPKeyedUnarchiver
{
CPBundle _bundle;
}
- (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle
{
self = [super initForReadingWithData:data];
if (self)
_bundle = aBundle;
return self;
}
- (CPBundle)bundle
{
return _bundle;
}
- (BOOL)awakenCustomResources
{
return YES;
}
@end