Fixed: Converter was copying too much state from Nib2Cib, framework resources could not be referenced in Xcode.

* State *
Previously the Converter class needlessly copied a whole bunch of state from the Nib2Cib class, which was not only poor factoring, but made for easy omissions of state when instantiating a new Converter, such as was done in NSNib.j.

This commit pushes all state not specific to Converter up to Nib2Cib. This allowed removal of redundant code in NSNib -NS_initWithCoder.

* Resources *
Previously it was not possible to use custom images that were located in a framework, because only the app's Resources directory was searched.

This commit automatically searches all frameworks for Resources directories. When a custom image is specified in Xcode, first the app's Resources directory is searched, then all framework Resource directories. Alternately, the specific framework to use can be specified in Xcode by using the form framework@image.

At runtime, the framework is loaded by its identifier (as specified in Info.plist), and if there is no identifier, by searching next to the current AppKit framework.

All of this renders the nib2cib -R option completely obsolete, so it was removed from the NibApplication/Jakefile template.
This commit is contained in:
Aparajita Fishman committed 2013-04-08 15:48:45 -04:00
1 parent b3c60e56a6
commit d9117bd9a0
18 files changed
+353 -247

No files matched your search

+60 -24
View File
@@ -31,7 +31,7 @@
@import "CPTheme.j"
@class CPButtonBar
@class CPView
var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
_CPCibCustomResourceResourceNameKey = @"_CPCibCustomResourceResourceNameKey",
@@ -42,6 +42,7 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
CPString _className;
CPString _resourceName;
CPDictionary _properties;
CPBundle _bundle;
}
+ (id)imageResourceWithName:(CPString)aResourceName size:(CGSize)aSize
@@ -63,6 +64,7 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
_className = aClassName;
_resourceName = aResourceName;
_properties = properties;
_bundle = nil;
}
return self;
@@ -77,6 +79,7 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
_className = [aCoder decodeObjectForKey:_CPCibCustomResourceClassNameKey];
_resourceName = [aCoder decodeObjectForKey:_CPCibCustomResourceResourceNameKey];
_properties = [aCoder decodeObjectForKey:_CPCibCustomResourcePropertiesKey];
_bundle = nil;
}
return self;
@@ -100,39 +103,72 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
else if (_resourceName == "CPRemoveTemplate")
return [[CPTheme defaultTheme] valueForAttributeWithName:@"button-image-minus" forClass:[CPButtonBar class]];
return [self imageFromBundle:[aCoder bundle]];
return [self imageFromCoder:aCoder];
}
return self;
}
- (CPImage)imageFromBundle:(CPBundle)aBundle
{
if (!aBundle)
{
var bundleClass = _properties.valueForKey(@"bundleClass");
if (bundleClass)
{
bundleClass = CPClassFromString(bundleClass);
if (bundleClass)
aBundle = [CPBundle bundleForClass:bundleClass];
}
else
aBundle = [CPBundle mainBundle];
}
return [[CPImage alloc] initWithContentsOfFile:[aBundle pathForResource:_resourceName] size:_properties.valueForKey(@"size")];
}
@end
@implementation _CPCibCustomResource (CPImage)
- (CPBundle)imageBundleWithCoder:(CPCoder)aCoder
{
if (_bundle)
return _bundle;
var bundleIdentifier = [_properties valueForKey:@"bundleIdentifier"];
if (bundleIdentifier)
_bundle = [CPBundle bundleWithIdentifier:bundleIdentifier];
else
{
var bundleClass = [_properties valueForKey:@"bundleClass"];
if (bundleClass)
{
bundleClass = CPClassFromString(bundleClass);
if (bundleClass)
_bundle = [CPBundle bundleForClass:bundleClass];
}
}
if (!_bundle)
{
var framework = [_properties valueForKey:@"framework"];
if (framework)
{
// Get AppKit and hope the framework is in the same directory
var appKit = [CPBundle bundleForClass:[CPView class]],
url = [[appKit bundleURL] URLByDeletingLastPathComponent];
url = [CPURL URLWithString:framework relativeToURL:url];
_bundle = [CPBundle bundleWithURL:url];
}
}
if (!_bundle)
{
if (aCoder)
_bundle = [aCoder bundle];
else
_bundle = [CPBundle mainBundle];
}
return _bundle;
}
- (CPImage)imageFromCoder:(CPCoder)aCoder
{
return [[CPImage alloc] initWithContentsOfFile:[[self imageBundleWithCoder:aCoder] pathForResource:_resourceName] size:[_properties valueForKey:@"size"]];
}
- (CPString)filename
{
return [[CPBundle mainBundle] pathForResource:_resourceName];
return [[self imageBundleWithCoder:nil] pathForResource:_resourceName];
}
- (CGSize)size
@@ -162,7 +198,7 @@ var _CPCibCustomResourceClassNameKey = @"_CPCibCustomResourceClassNameKey",
- (CPString)description
{
var image = [self imageFromBundle:nil];
var image = [self imageFromCoder:nil];
return [image description];
}
@@ -30,7 +30,6 @@ app ("__project.nameasidentifier__", function(task)
task.setResources(new FileList("Resources/**"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
task.setNib2CibFlags("-R Resources/");
if (configuration === "Debug")
task.setCompilerFlags("-DDEBUG -g");
+6 -3
View File
@@ -24,12 +24,14 @@
@import "Nib2CibKeyedUnarchiver.j"
@import "NSFont.j"
@class Nib2Cib
@implementation Converter (Mac)
- (void)convertedDataFromMacData:(CPData)data resourcesPath:(CPString)aResourcesPath
- (void)convertedDataFromMacData:(CPData)data
{
// Unarchive the NS data
var unarchiver = [[Nib2CibKeyedUnarchiver alloc] initForReadingWithData:data resourcesPath:aResourcesPath],
var unarchiver = [[Nib2CibKeyedUnarchiver alloc] initForReadingWithData:data],
objectData = [unarchiver decodeObjectForKey:@"IB.objectdata"],
objects = [unarchiver allObjects],
count = [objects count];
@@ -106,7 +108,8 @@
// nil cibFont means try to use theme font
if (!cibFont)
{
var bold = [nibFont isBold];
var bold = [nibFont isBold],
themes = [[Nib2Cib sharedNib2Cib] themes];
for (var i = 0; i < themes.length; ++i)
{
+25 -37
View File
@@ -30,6 +30,8 @@
@import "NSFoundation.j"
@import "NSAppKit.j"
@class Nib2Cib
@global java
var FILE = require("file"),
@@ -48,13 +50,8 @@ ConverterConversionException = @"ConverterConversionException";
@implementation Converter : CPObject
{
CPString inputPath @accessors(readonly);
CPString outputPath @accessors;
CPString resourcesPath @accessors;
NibFormat format @accessors(readonly);
CPArray themes @accessors(readonly);
CPArray userNSClasses @accessors;
BOOL compileNib @accessors;
CPString inputPath @accessors(readonly);
CPString outputPath @accessors;
}
+ (Converter)sharedConverter
@@ -62,7 +59,7 @@ ConverterConversionException = @"ConverterConversionException";
return SharedConverter;
}
- (id)initWithInputPath:(CPString)aPath format:(NibFormat)nibFormat themes:(CPArray)themeList
- (id)initWithInputPath:(CPString)anInputPath outputPath:(CPString)anOutputPath
{
self = [super init];
@@ -71,52 +68,43 @@ ConverterConversionException = @"ConverterConversionException";
if (!SharedConverter)
SharedConverter = self;
inputPath = aPath;
format = nibFormat;
themes = themeList;
compileNib = YES;
inputPath = anInputPath;
outputPath = anOutputPath;
}
return self;
}
- (void)convert
- (CPData)convert
{
if ([resourcesPath length] && !FILE.isReadable(resourcesPath))
[CPException raise:ConverterConversionException reason:@"Could not read Resources at path \"" + resourcesPath + "\""];
// Assume its a Mac file.
var inferredFormat = NibFormatMac;
var inferredFormat = format;
// Some .xibs are iPhone nibs, check the actual contents in this case.
if (FILE.extension(inputPath) !== ".nib" && FILE.isFile(inputPath) &&
FILE.read(inputPath, { charset:"UTF-8" }).indexOf("<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\"") !== -1)
inferredFormat = NibFormatIPhone;
if (inferredFormat === NibFormatUndetermined)
{
// Assume its a Mac file.
inferredFormat = NibFormatMac;
if (inferredFormat === NibFormatMac)
CPLog.info("Auto-detected Cocoa nib or xib File");
else
CPLog.info("Auto-detected CocoaTouch xib File");
// Some .xibs are iPhone nibs, check the actual contents in this case.
if (FILE.extension(inputPath) !== ".nib" && FILE.isFile(inputPath) &&
FILE.read(inputPath, { charset:"UTF-8" }).indexOf("<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\"") !== -1)
inferredFormat = NibFormatIPhone;
if (inferredFormat === NibFormatMac)
CPLog.info("Auto-detected Cocoa Nib or Xib File");
else
CPLog.info("Auto-detected CocoaTouch Xib File");
}
CPLog.info("Converting Xib file to plist...");
CPLog.info("Converting xib file to plist...");
var nibData = [self CPCompliantNibDataAtFilePath:inputPath];
if (inferredFormat === NibFormatMac)
var convertedData = [self convertedDataFromMacData:nibData resourcesPath:resourcesPath];
var convertedData = [self convertedDataFromMacData:nibData];
else
[CPException raise:ConverterConversionException reason:@"nib2cib does not understand this nib format."];
if (![outputPath length])
outputPath = inputPath.substr(0, inputPath.length - FILE.extension(inputPath).length) + ".cib";
if ([outputPath length])
FILE.write(outputPath, [convertedData rawString], { charset:"UTF-8" });
FILE.write(outputPath, [convertedData rawString], { charset:"UTF-8" });
CPLog.info(CPLogColorize("Conversion successful", "warn"));
return convertedData;
}
- (CPData)CPCompliantNibDataAtFilePath:(CPString)aFilePath
@@ -126,7 +114,7 @@ ConverterConversionException = @"ConverterConversionException";
try
{
if (compileNib)
if ([outputPath length])
{
// Compile xib or nib to make sure we have a non-new format nib.
temporaryNibFilePath = FILE.join("/tmp", FILE.basename(aFilePath) + ".tmp.nib");
+2 -2
View File
@@ -30,7 +30,7 @@
@import "NSCell.j"
@import "NSControl.j"
@class Converter
@class Nib2Cib
@global NIB_CONNECTION_EQUIVALENCY_TABLE
@@ -217,7 +217,7 @@ var NSButtonIsBorderedMask = 0x00800000,
- If there is just a max height, use that for only for fixed height buttons.
- If there is no max height either, don't do any height adjustments.
*/
var theme = [[Converter sharedConverter] themes][0],
var theme = [Nib2Cib defaultTheme],
minSize = [theme valueForAttributeWithName:@"min-size" forClass:[self class]],
maxSize = [theme valueForAttributeWithName:@"max-size" forClass:[self class]],
adjustHeight = NO;
+2 -2
View File
@@ -24,7 +24,7 @@
@import <Foundation/CPString.j>
@import <AppKit/_CPCibClassSwapper.j>
@class Converter
@class Nib2Cib
@global CP_NSMapClassName
@@ -48,7 +48,7 @@ var _CPCibClassSwapperClassNameKey = @"_CPCibClassSwapperClassNameKey",
// If this is a userland NS class, call its KVC methods directly
var nsClass = nil;
if ([[[Converter sharedConverter] userNSClasses] containsObject:aClassName])
if ([[[Nib2Cib sharedNib2Cib] userNSClasses] containsObject:aClassName])
nsClass = objj_lookUpClass("NS_" + aClassName);
var originalClass = nsClass || objj_lookUpClass(anOriginalClassName);
+4 -3
View File
@@ -25,7 +25,7 @@
@import "NSTextField.j"
@class Converter
@class Nib2Cib
@implementation CPComboBox (NSCoding)
@@ -48,12 +48,13 @@
[self setEnabled:[cell isEnabled]];
// Make sure the height is clipped to the max given by the theme
var maxSize = [[[Converter sharedConverter] themes][0] valueForAttributeWithName:@"max-size" forClass:[CPComboBox class]],
var theme = [Nib2Cib defaultTheme],
maxSize = [theme valueForAttributeWithName:@"max-size" forClass:[CPComboBox class]],
size = [self frameSize],
widthOffset = -3;
// Adjust for differences between Cocoa and Cappuccino widget framing.
if ([[[Converter sharedConverter] themes][0] name] == @"Aristo")
if ([theme name] == @"Aristo")
{
_frame.origin.x += 1;
widthOffset = -5;
+36 -9
View File
@@ -41,7 +41,8 @@ var FILE = require("file"),
_className = CP_NSMapClassName([aCoder decodeObjectForKey:@"NSClassName"]);
_resourceName = [aCoder decodeObjectForKey:@"NSResourceName"];
var size = CGSizeMakeZero();
var size = CGSizeMakeZero(),
framework = @"";
if (_resourceName == "NSSwitch")
return nil;
@@ -52,21 +53,47 @@ var FILE = require("file"),
}
else
{
var resourcePath = [aCoder resourcePathForName:_resourceName];
var match = /^(.+)@(.+)$/.exec(_resourceName),
framework = @"",
bundleIdentifier = @"";
if (!resourcePath)
CPLog.warn("Resource \"" + _resourceName + "\" not found in the resources path: " + [aCoder resourcesPath]);
if (match)
{
framework = match[1];
_resourceName = match[2];
}
var resourceInfo = [aCoder resourceInfoForName:_resourceName inFramework:framework];
if (!resourceInfo)
CPLog.warn("Resource \"" + _resourceName + "\" not found in the Resources directories");
else
size = imageSize(FILE.canonical(resourcePath)) || CGSizeMakeZero();
{
size = imageSize(FILE.canonical(resourceInfo.path)) || CGSizeMakeZero();
framework = resourceInfo.framework;
}
// Account for the fact that an extension may have been inferred.
if (resourcePath && FILE.extension(resourcePath) !== FILE.extension(_resourceName))
_resourceName += FILE.extension(resourcePath);
if (resourceInfo &&
resourceInfo.path &&
FILE.extension(resourceInfo.path) !== FILE.extension(_resourceName))
{
_resourceName += FILE.extension(resourceInfo.path);
}
CPLog.debug(" Rsrc: %s\n Path: %s\n Size: %d x %d", _resourceName, FILE.canonical(resourcePath), size.width, size.height);
CPLog.debug(" Resource: %s\n Framework: %s\n Path: %s\n Size: %d x %d", _resourceName, framework, resourceInfo ? FILE.canonical(resourceInfo.path) : "", size.width, size.height);
}
_properties = [CPDictionary dictionaryWithObject:size forKey:@"size"];
if (resourceInfo && resourceInfo.path && resourceInfo.framework)
{
var frameworkPath = FILE.dirname(FILE.dirname(resourceInfo.path)),
bundle = [CPBundle bundleWithPath:frameworkPath];
[bundle loadWithDelegate:nil];
bundleIdentifier = [bundle bundleIdentifier] || @"";
}
_properties = @{ @"size":size, @"bundleIdentifier":bundleIdentifier, @"framework":framework };
}
return self;
+3 -3
View File
@@ -24,7 +24,7 @@
@import "NSCell.j"
@class Converter
@class Nib2Cib
@global CPTextFieldDatePickerStyle
@global CPTextFieldAndStepperDatePickerStyle
@@ -57,7 +57,7 @@ var NSDatePickerDefaultSize = 22,
[self setBezeled:[cell isBezeled]];
[self setBordered:[cell isBordered]];
var theme = [[Converter sharedConverter] themes][0];
var theme = [Nib2Cib defaultTheme];
if (_datePickerStyle != CPClockAndCalendarDatePickerStyle)
{
@@ -225,4 +225,4 @@ var NSDatePickerDefaultSize = 22,
return [CPDate class];
}
@end
@end
+4 -12
View File
@@ -34,23 +34,15 @@ var FILE = require("file"),
{
self = [super init];
// FIXME: change /tmp/ to os.tmpDir() in Node
var nibPath = @"/tmp/" + UUID.uuid() + ".nib",
data = [aCoder decodeObjectForKey:@"NSNibFileData"],
sharedConverter = [Converter sharedConverter];
data = [aCoder decodeObjectForKey:@"NSNibFileData"];
FILE.write(nibPath, data.bytes(), { charset:"UTF-16" });
var converter = [[Converter alloc] initWithInputPath:@""
format:[sharedConverter format]
themes:[sharedConverter themes]];
[converter setCompileNib:NO];
[converter setResourcesPath:[sharedConverter resourcesPath]];
[converter setUserNSClasses:[sharedConverter userNSClasses]];
var converter = [[Converter alloc] initWithInputPath:nibPath outputPath:nil];
CPLog.info("Converting sub nib to plist...");
var nibData = [converter CPCompliantNibDataAtFilePath:nibPath];
_data = [converter convertedDataFromMacData:nibData resourcesPath:[sharedConverter resourcesPath]];
_data = [converter convert];
return self;
}
+2 -2
View File
@@ -22,7 +22,7 @@
@import <AppKit/CPProgressIndicator.j>
@class Converter
@class Nib2Cib
var NSProgressIndicatorSpinningFlag = 1 << 12;
@@ -59,7 +59,7 @@ var NSProgressIndicatorSpinningFlag = 1 << 12;
if (_style !== CPProgressIndicatorSpinningStyle)
{
var theme = [[Converter sharedConverter] themes][0],
var theme = [Nib2Cib defaultTheme],
height = [theme valueForAttributeWithName:@"default-height" forClass:CPProgressIndicator];
currentFrameSize.height = height;
+2 -2
View File
@@ -24,7 +24,7 @@
@import "NSTextField.j"
@class Converter
@class Nib2Cib
@implementation CPSearchField (NSCoding)
@@ -43,7 +43,7 @@
[self setSendsWholeSearchString:[cell sendsWholeSearchString]];
[self setSendsSearchStringImmediately:[cell sendsSearchStringImmediately]];
if ([[[Converter sharedConverter] themes][0] name] == @"Aristo" && [self isBezeled])
if ([[Nib2Cib defaultTheme] name] === @"Aristo" && [self isBezeled])
{
// NSTextField.j makes the field +7.0 pixels tall. We want +8.0 to go to 30.
var frame = [self frame];
+2 -2
View File
@@ -24,7 +24,7 @@
@import "NSCell.j"
@class Converter
@class Nib2Cib
@implementation CPSegmentedControl (CPCoding)
@@ -68,7 +68,7 @@
// Adjust for differences between Cocoa and Cappuccino widget framing.
frame.origin.x += 6;
if ([[[Converter sharedConverter] themes][0] name] == @"Aristo2")
if ([[Nib2Cib defaultTheme] name] == @"Aristo2")
frame.size.height += 1;
frame.size.width = originalWidth;
+3 -2
View File
@@ -31,6 +31,7 @@
@import "NSTextField.j"
@class Converter
@class Nib2Cib
var IBDefaultFontSizeTableHeader = 11.0;
@@ -63,7 +64,7 @@ var IBDefaultFontSizeTableHeader = 11.0;
var headerCell = [aCoder decodeObjectForKey:@"NSHeaderCell"],
headerView = [[_CPTableColumnHeaderView alloc] initWithFrame:CGRectMakeZero()],
theme = [[Converter sharedConverter] themes][0];
theme = [Nib2Cib defaultTheme];
[headerView setStringValue:[headerCell objectValue]];
[headerView setFont:[headerCell font]];
@@ -124,7 +125,7 @@ var IBDefaultFontSizeTableHeader = 11.0;
- (id)valueForDataViewThemeAttribute:(CPString)attribute inState:(int)state
{
var themes = [[Converter sharedConverter] themes];
var themes = [[Nib2Cib sharedNib2Cib] themes];
for (var i = 0; i < themes.length; ++i)
{
+2 -2
View File
@@ -22,8 +22,8 @@
@import <AppKit/CPTableHeaderView.j>
@class Converter
@class CPTableView
@class Nib2Cib
@implementation CPTableHeaderView (NSCoding)
@@ -37,7 +37,7 @@
// change the default height
if (_bounds.size.height === 17)
{
var theme = [[Converter sharedConverter] themes][0],
var theme = [Nib2Cib defaultTheme],
height = [theme valueForAttributeWithName:@"default-row-height" forClass:CPTableView];
_bounds.size.height = height;
+2 -2
View File
@@ -22,7 +22,7 @@
@import <AppKit/CPTableView.j>
@class Converter
@class Nib2Cib
@implementation CPTableView (NSCoding)
@@ -43,7 +43,7 @@
// Convert xib default to cib default
if (_rowHeight == 17)
{
var theme = [[Converter sharedConverter] themes][0],
var theme = [Nib2Cib defaultTheme],
height = [theme valueForAttributeWithName:@"default-row-height" forClass:CPTableView];
_rowHeight = height;
+152 -123
View File
@@ -42,7 +42,9 @@ var FILE = require("file"),
BuildTypes = ["Debug", "Release"],
DefaultFile = "MainMenu",
AllowedStoredOptionsRe = new RegExp("^(defaultTheme|auxThemes|verbosity|quiet|frameworks|format)$"),
ArgsRe = /"[^\"]+"|'[^\']+'|\S+/g;
ArgsRe = /"[^\"]+"|'[^\']+'|\S+/g,
SharedNib2Cib = nil;
@implementation Nib2Cib : CPObject
@@ -50,10 +52,22 @@ var FILE = require("file"),
CPArray commandLineArgs;
JSObject parser;
JSObject nibInfo;
CPString appDirectory;
CPString resourcesDirectory;
CPDictionary infoPlist;
CPArray userNSClasses;
CPString appDirectory @accessors(readonly);
CPDictionary frameworks @accessors(readonly);
CPString appResourceDirectory @accessors(readonly);
CPDictionary infoPlist @accessors(readonly);
CPArray userNSClasses @accessors(readonly);
CPArray themes @accessors(readonly);
}
+ (Nib2Cib)sharedNib2Cib
{
return SharedNib2Cib;
}
+ (CPTheme)defaultTheme
{
return [SharedNib2Cib themes][0];
}
- (id)initWithArgs:(CPArray)theArgs
@@ -62,13 +76,18 @@ var FILE = require("file"),
if (self)
{
if (!SharedNib2Cib)
SharedNib2Cib = self;
commandLineArgs = theArgs;
parser = new (require("narwhal/args").Parser)();
nibInfo = {};
appDirectory = @"";
resourcesDirectory = @"";
frameworks = [CPDictionary dictionary];
appResourceDirectory = @"";
infoPlist = @{};
userNSClasses = [];
themes = [];
}
return self;
@@ -86,7 +105,7 @@ var FILE = require("file"),
if (options.watch)
[self watchWithOptions:options];
else
[self convertWithOptions:options inputFile:nil];
[self convertWithOptions:options inputPath:nil];
}
catch (anException)
{
@@ -104,25 +123,55 @@ var FILE = require("file"),
[self failWithMessage:@"fontinfo does not appear to be installed"];
}
- (BOOL)convertWithOptions:(JSObject)options inputFile:(CPString)inputFile
- (void)enumerateFrameworks
{
var frameworksDirectory = FILE.join(appDirectory, "Frameworks"),
debugFrameworksDirectory = FILE.join(frameworksDirectory, "Debug");
[debugFrameworksDirectory, frameworksDirectory].forEach(function(directory)
{
if (FILE.isDirectory(directory))
{
var frameworkList = FILE.list(directory);
frameworkList.forEach(function(framework)
{
if (framework !== @"Debug" && ![frameworks containsKey:framework])
{
var resourceDirectory = FILE.join(directory, framework, "Resources");
if (FILE.isDirectory(resourceDirectory))
resourceDirectory = FILE.canonical(resourceDirectory);
else
resourceDirectory = @"";
[frameworks setValue:{resourceDirectory:resourceDirectory, loaded:false} forKey:framework];
}
});
}
});
}
- (BOOL)convertWithOptions:(JSObject)options inputPath:(CPString)inputPath
{
try
{
inputFile = inputFile || [self getInputFile:options.args];
inputPath = inputPath || [self getInputPath:options.args];
[self getAppAndResourceDirectoriesFromInputFile:inputFile options:options];
[self getAppAndResourceDirectoriesFromInputPath:inputPath options:options];
[self enumerateFrameworks];
if (options.readStoredOptions)
{
options = [self mergeOptionsWithStoredOptions:options inputFile:inputFile];
options = [self mergeOptionsWithStoredOptions:options inputPath:inputPath];
[self setLogLevel:options.quiet ? -1 : options.verbosity];
}
if (!options.quiet && options.verbosity > 0)
[self printVersion];
var outputFile = [self getOutputFileFromInputFile:inputFile args:options.args],
configInfo = [self readConfigFile:options.configFile || @"" inputFile:inputFile];
var configInfo = [self readConfigFile:options.configFile || @"" inputPath:inputPath],
outputPath = [self getOutputPathFromInputPath:inputPath args:options.args];
infoPlist = configInfo.plist;
@@ -141,37 +190,40 @@ var FILE = require("file"),
else
infoPlist = @{};
var themeList = [self getThemeList:options],
themes = [self loadThemesFromList:themeList];
var themeList = [self getThemeList:options];
[self loadThemesFromList:themeList];
[self loadFrameworks:options.frameworks verbosity:options.verbosity];
[self loadNSClassesFromBundle:[CPBundle mainBundle]];
var frameworkList = [];
[frameworks allKeys].forEach(function(name)
{
var info = [frameworks valueForKey:name];
if (info.resourceDirectory)
name += "*";
if (info.loaded)
name += "+";
frameworkList.push(name);
});
CPLog.info("\n-------------------------------------------------------------");
CPLog.info("Input : " + inputFile);
CPLog.info("Output : " + outputFile);
CPLog.info("Format : " + ["Auto", "Mac", "iPhone"][options.format]);
CPLog.info("Input : " + inputPath);
CPLog.info("Output : " + outputPath);
CPLog.info("Application : " + appDirectory);
CPLog.info("Resources : " + resourcesDirectory);
CPLog.info("Frameworks : " + (options.frameworks || ""));
CPLog.info("Frameworks : " + (frameworkList.join(", ") || ""));
CPLog.info("Default theme : " + themeList[0]);
CPLog.info("Aux themes : " + themeList.slice(1).join(", "));
CPLog.info("Config file : " + (configInfo.path || ""));
CPLog.info("System Font : " + [CPFont systemFontSize] + "px " + [CPFont systemFontFace]);
CPLog.info("-------------------------------------------------------------\n");
var converter = [[Converter alloc] initWithInputPath:inputFile
format:options.format
themes:themes];
[converter setOutputPath:outputFile];
[converter setResourcesPath:resourcesDirectory];
var loadFrameworksCallback = function()
{
[self loadNSClassesFromBundle:[CPBundle mainBundle]];
[converter setUserNSClasses:userNSClasses];
[converter convert];
};
[self loadFrameworks:options.frameworks verbosity:options.verbosity callback:loadFrameworksCallback];
var converter = [[Converter alloc] initWithInputPath:inputPath outputPath:outputPath];
[converter convert];
return YES;
}
@@ -250,7 +302,7 @@ var FILE = require("file"),
// Let the converter log however the user configured it
[self setLogLevel:verbosity];
var success = [self convertWithOptions:options inputFile:nib];
var success = [self convertWithOptions:options inputPath:nib];
[self setLogLevel:1];
@@ -275,11 +327,6 @@ var FILE = require("file"),
.set(true)
.help("Ask nib2cib to watch a directory for changes");
parser.option("-R", "resourcesDir")
.set()
.displayName("directory")
.help("Set the Resources directory, usually unnecessary as it is inferred from the input path");
parser.option("--default-theme", "defaultTheme")
.set()
.displayName("name")
@@ -312,15 +359,15 @@ var FILE = require("file"),
.def(true)
.help("Do not read stored options");
parser.option("--mac", "format")
.set(NibFormatMac)
.def(NibFormatUndetermined)
.help("Set format to Mac");
parser.option("--version", "showVersion")
.action(function() { [self printVersionAndExit]; })
.help("Show the version of nib2cib and quit");
parser.option("-R", "deprecatedResourcesDir")
.set()
.displayName("resources directory")
.help("This option is deprecated.");
parser.helpful();
var options = parser.parse(theArgs, null, null, true);
@@ -334,13 +381,13 @@ var FILE = require("file"),
return options;
}
- (JSObject)mergeOptionsWithStoredOptions:(JSObject)options inputFile:(CPString)inputFile
- (JSObject)mergeOptionsWithStoredOptions:(JSObject)options inputPath:(CPString)inputPath
{
// We have to clone options
var userOptions = [self readStoredOptionsAtPath:FILE.join(SYS.env["HOME"], ".nib2cibconfig")],
appOptions = [self readStoredOptionsAtPath:FILE.join(appDirectory, "nib2cib.conf")],
filename = FILE.basename(inputFile, FILE.extension(inputFile)) + ".conf",
fileOptions = [self readStoredOptionsAtPath:FILE.join(FILE.dirname(inputFile), filename)];
filename = FILE.basename(inputPath, FILE.extension(inputPath)) + ".conf",
fileOptions = [self readStoredOptionsAtPath:FILE.join(FILE.dirname(inputPath), filename)];
// At this point we have an array of args without the initial command in args[0],
// add the command and parse the options.
@@ -445,31 +492,31 @@ var FILE = require("file"),
CPLogRegister(CPLogPrint, null, logFormatter);
}
- (CPString)getInputFile:(CPArray)theArgs
- (CPString)getInputPath:(CPArray)theArgs
{
var inputFile = theArgs[0] || DefaultFile,
var inputPath = theArgs[0] || DefaultFile,
path = "";
if (!/^.+\.[nx]ib$/.test(inputFile))
if (!/^.+\.[nx]ib$/.test(inputPath))
{
if (path = [self findInputFile:inputFile extension:@".xib"])
inputFile = path;
else if (path = [self findInputFile:inputFile extension:@".nib"])
inputFile = path;
if (path = [self findInputPath:inputPath extension:@".xib"])
inputPath = path;
else if (path = [self findInputPath:inputPath extension:@".nib"])
inputPath = path;
else
[self failWithMessage:@"Cannot find the input file (.xib or .nib): " + FILE.canonical(inputFile)];
[self failWithMessage:@"Cannot find the input file (.xib or .nib): " + FILE.canonical(inputPath)];
}
else if (path = [self findInputFile:inputFile extension:nil])
inputFile = path;
else if (path = [self findInputPath:inputPath extension:nil])
inputPath = path;
else
[self failWithMessage:@"Could not read the input file: " + FILE.canonical(inputFile)];
[self failWithMessage:@"Could not read the input file: " + FILE.canonical(inputPath)];
return FILE.canonical(inputFile);
return FILE.canonical(inputPath);
}
- (void)findInputFile:(CPString)inputFile extension:(CPString)extension
- (void)findInputPath:(CPString)inputPath extension:(CPString)extension
{
var path = inputFile;
var path = inputPath;
if (extension)
path += extension;
@@ -477,7 +524,7 @@ var FILE = require("file"),
if (FILE.isReadable(path))
return path;
if (FILE.basename(FILE.dirname(inputFile)) !== "Resources" && FILE.isDirectory("Resources"))
if (FILE.basename(FILE.dirname(inputPath)) !== "Resources" && FILE.isDirectory("Resources"))
{
path = FILE.resolve(path, FILE.join("Resources", FILE.basename(path)));
@@ -488,71 +535,59 @@ var FILE = require("file"),
return null;
}
- (void)getAppAndResourceDirectoriesFromInputFile:(CPString)inputFile options:(JSObject)options
- (void)getAppAndResourceDirectoriesFromInputPath:(CPString)aPath options:(JSObject)options
{
appDirectory = resourcesDirectory = "";
appDirectory = @"";
if (options.resourcesDir)
{
var path = FILE.canonical(options.resourcesDir);
if (!FILE.isDirectory(path))
[self failWithMessage:@"Cannot read resources at: " + path];
resourcesDirectory = path;
}
var parentDir = FILE.dirname(inputFile);
var parentDir = FILE.dirname(aPath);
if (FILE.basename(parentDir) === "Resources")
{
appDirectory = FILE.dirname(parentDir);
resourcesDirectory = resourcesDirectory || parentDir;
appResourceDirectory = parentDir;
}
else
{
appDirectory = parentDir;
if (!resourcesDirectory)
if (!appResourceDirectory)
{
var path = FILE.join(appDirectory, "Resources");
if (FILE.isDirectory(path))
resourcesDirectory = path;
appResourceDirectory = path;
}
}
}
- (CPString)getOutputFileFromInputFile:(CPString)inputFile args:(CPArray)theArgs
- (CPString)getOutputPathFromInputPath:(CPString)aPath args:(CPArray)theArgs
{
var outputFile = null;
var outputPath = null;
if (theArgs.length > 1)
{
outputFile = theArgs[1];
outputPath = theArgs[1];
if (!/^.+\.cib$/.test(outputFile))
outputFile += ".cib";
if (!/^.+\.cib$/.test(outputPath))
outputPath += ".cib";
}
else
outputFile = FILE.join(FILE.dirname(inputFile), FILE.basename(inputFile, FILE.extension(inputFile))) + ".cib";
outputPath = FILE.join(FILE.dirname(aPath), FILE.basename(aPath, FILE.extension(aPath))) + ".cib";
outputFile = FILE.canonical(outputFile);
outputPath = FILE.canonical(outputPath);
if (!FILE.isWritable(FILE.dirname(outputFile)))
[self failWithMessage:@"Cannot write the output file at: " + outputFile];
if (!FILE.isWritable(FILE.dirname(outputPath)))
[self failWithMessage:@"Cannot write the output file at: " + outputPath];
return outputFile;
return outputPath;
}
- (void)loadFrameworks:(CPArray)frameworks verbosity:(int)verbosity callback:(JSObject)aCallback
- (void)loadFrameworks:(CPArray)frameworksToLoad verbosity:(int)verbosity
{
if (!frameworks || frameworks.length === 0)
return aCallback();
if (!frameworksToLoad || frameworksToLoad.length === 0)
return;
var returnPath = function(path) { return path; };
frameworks.forEach(function(aFramework)
frameworksToLoad.forEach(function(aFramework)
{
[self setLogLevel:verbosity];
@@ -561,19 +596,13 @@ var FILE = require("file"),
// If it is just a name with no path components, try to locate it
if (aFramework.indexOf("/") === -1)
{
frameworkPath = [self findInCappBuild:aFramework isDirectory:YES callback:returnPath];
if (!frameworkPath)
frameworkPath = [self findInFrameworks:FILE.join(appDirectory, "Frameworks")
path:aFramework
isDirectory:YES
callback:returnPath];
if (!frameworkPath)
frameworkPath = [self findInInstalledFrameworks:aFramework isDirectory:YES callback:returnPath];
frameworkPath = [self findInFrameworks:FILE.join(appDirectory, "Frameworks")
path:aFramework
isDirectory:YES
callback:function(path) { return path; }];
}
else
frameworkPath = FILE.canonical(aFramework);
[self failWithMessage:@"-F should be used only with a framework name that is in the app's Framework directory"];
if (!frameworkPath)
[self failWithMessage:@"Cannot find the framework \"" + aFramework + "\""];
@@ -592,6 +621,11 @@ var FILE = require("file"),
[self setLogLevel:verbosity];
[self loadNSClassesFromBundle:frameworkBundle];
var frameworkName = FILE.basename(frameworkPath),
info = [frameworks valueForKey:frameworkName];
info.loaded = true;
}
finally
{
@@ -600,8 +634,6 @@ var FILE = require("file"),
require("browser/timeout").serviceTimeouts();
});
aCallback();
}
- (void)loadNSClassesFromBundle:(CPBundle)aBundle
@@ -634,20 +666,20 @@ var FILE = require("file"),
if (!defaultTheme)
defaultTheme = [self getAppKitDefaultThemeName];
var themes = [CPSet setWithObject:defaultTheme];
var themeList = [CPSet setWithObject:defaultTheme];
if (options.auxThemes)
[themes addObjectsFromArray:options.auxThemes];
[themeList addObjectsFromArray:options.auxThemes];
var auxThemes = infoPlist.valueForKey("CPAuxiliaryThemes");
if (auxThemes)
[themes addObjectsFromArray:auxThemes];
[themeList addObjectsFromArray:auxThemes];
// Now remove the default theme, get the list as an array, and insert the default at the beginning
[themes removeObject:defaultTheme];
[themeList removeObject:defaultTheme];
var allThemes = [themes allObjects];
var allThemes = [themeList allObjects];
[allThemes insertObject:defaultTheme atIndex:0];
@@ -735,23 +767,20 @@ var FILE = require("file"),
return themeName;
}
- (CPArray)loadThemesFromList:(CPArray)themeList
- (void)loadThemesFromList:(CPArray)themeList
{
var themes = [];
for (var i = 0; i < themeList.length; ++i)
themes.push([self loadThemeNamed:themeList[i] directory:resourcesDirectory]);
return themes;
themes.push([self loadThemeNamed:themeList[i]]);
}
- (CPTheme)loadThemeNamed:(CPString)themeName directory:(CPString)themeDir
- (CPTheme)loadThemeNamed:(CPString)themeName
{
if (/^.+\.blend$/.test(themeName))
themeName = themeName.substr(0, themeName.length - ".blend".length);
var blendName = themeName + ".blend",
themePath = "";
themePath = "",
themeDir = appResourceDirectory;
if (themeDir)
{
@@ -820,7 +849,7 @@ var FILE = require("file"),
return theme;
}
- (JSObject)readConfigFile:(CPString)configFile inputFile:(CPString)inputFile
- (JSObject)readConfigFile:(CPString)configFile inputPath:(CPString)inputPath
{
var configPath = null,
path;
+46 -16
View File
@@ -22,22 +22,13 @@
@import <Foundation/CPKeyedUnarchiver.j>
@class Nib2Cib
var FILE = require("file");
@implementation Nib2CibKeyedUnarchiver : CPKeyedUnarchiver
{
CPString resourcesPath @accessors(readonly);
}
- (id)initForReadingWithData:(CPData)data resourcesPath:(CPString)aResourcesPath
{
self = [super initForReadingWithData:data];
if (self)
resourcesPath = aResourcesPath;
return self;
}
- (CPArray)allObjects
@@ -45,12 +36,51 @@ var FILE = require("file");
return _objects;
}
- (CPString)resourcePathForName:(CPString)aName
- (JSObject)resourceInfoForName:(CPString)aName inFramework:(CPString)framework
{
if (!resourcesPath)
return NULL;
var nib2cib = [Nib2Cib sharedNib2Cib],
frameworks = [nib2cib frameworks];
var pathGroups = [FILE.listPaths(resourcesPath)];
if (framework)
{
var info = [frameworks valueForKey:framework];
if (!info || !info.resourceDirectory)
return nil;
return { path:[self _resourcePathForName:aName inDirectory:info.resourceDirectory], framework:framework };
}
else
{
// Try the app's resource directory first
var resourcesDirectory = [nib2cib appResourceDirectory],
path = [self _resourcePathForName:aName inDirectory:resourcesDirectory];
if (path)
return { path:path, framework:framework };
var enumerator = [frameworks keyEnumerator];
while ((framework = [enumerator nextObject]))
{
var info = [frameworks valueForKey:framework];
if (!info || !info.resourceDirectory)
continue;
path = [self _resourcePathForName:aName inDirectory:info.resourceDirectory];
if (path)
return { path:path, framework:framework };
}
}
return nil;
}
- (CPString)_resourcePathForName:(CPString)aName inDirectory:(CPString)directory
{
var pathGroups = [FILE.listPaths(directory)];
while (pathGroups.length > 0)
{
@@ -73,7 +103,7 @@ var FILE = require("file");
}
}
return NULL;
return nil;
}
@end