mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-30 22:27:42 +00:00
Merge branch 'aparajita-nib2cib-man'
This commit is contained in:
@@ -132,7 +132,16 @@
|
||||
{
|
||||
var bold = [nibFont isBold];
|
||||
|
||||
cibFont = [theme valueForAttributeWithName:@"font" inState:[object themeState] forClass:[object class]];
|
||||
for (var i = 0; i < themes.length; ++i)
|
||||
{
|
||||
cibFont = [themes[i] valueForAttributeWithName:@"font" inState:[object themeState] forClass:[object class]];
|
||||
|
||||
if (cibFont)
|
||||
{
|
||||
source = " (from " + [themes[i] name] + ")";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Substitute legacy theme fonts for the current system font
|
||||
if (!cibFont || [cibFont familyName] === CPFontDefaultSystemFontFace)
|
||||
@@ -144,7 +153,6 @@
|
||||
size = [CPFont systemFontSize];
|
||||
|
||||
cibFont = bold ? [CPFont boldSystemFontOfSize:size] : [CPFont systemFontOfSize:size];
|
||||
source = " (from theme)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ ConverterConversionException = @"ConverterConversionException";
|
||||
CPString outputPath @accessors;
|
||||
CPString resourcesPath @accessors;
|
||||
NibFormat format @accessors(readonly);
|
||||
CPTheme theme @accessors(readonly);
|
||||
CPArray themes @accessors(readonly);
|
||||
}
|
||||
|
||||
+ (Converter)sharedConverter
|
||||
@@ -55,7 +55,7 @@ ConverterConversionException = @"ConverterConversionException";
|
||||
return SharedConverter;
|
||||
}
|
||||
|
||||
- (id)initWithInputPath:(CPString)aPath format:(NibFormat)nibFormat theme:(CPTheme)aTheme
|
||||
- (id)initWithInputPath:(CPString)aPath format:(NibFormat)nibFormat themes:(CPArray)themeList
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
@@ -63,7 +63,7 @@ ConverterConversionException = @"ConverterConversionException";
|
||||
{
|
||||
inputPath = aPath;
|
||||
format = nibFormat;
|
||||
theme = aTheme;
|
||||
themes = themeList;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
||||
@@ -36,6 +36,8 @@ filedir($BUILD_CJS_NIB2CIB, ["nib2cib"], function()
|
||||
make_objj_executable($BUILD_CJS_NIB2CIB);
|
||||
});
|
||||
|
||||
copyManPage("nib2cib", 1);
|
||||
|
||||
task ("build", ["nib2cib", $BUILD_CJS_NIB2CIB]);
|
||||
|
||||
CLOBBER.include($BUILD_CJS_NIB2CIB);
|
||||
|
||||
@@ -47,9 +47,9 @@ var FILE = require("file");
|
||||
var resourcePath = [aCoder resourcePathForName:_resourceName];
|
||||
|
||||
if (!resourcePath)
|
||||
CPLog.warn("Resource named " + _resourceName + " not found in the supplied resources path.");
|
||||
CPLog.warn("Resource \"" + _resourceName + "\" not found in the resources path: " + [aCoder resourcesPath]);
|
||||
else
|
||||
size = imageSize(FILE.join(FILE.cwd(), resourcePath));
|
||||
size = imageSize(FILE.canonical(resourcePath));
|
||||
|
||||
// Account for the fact that an extension may have been inferred.
|
||||
if (resourcePath && FILE.extension(resourcePath) !== FILE.extension(_resourceName))
|
||||
|
||||
+302
-145
@@ -35,13 +35,16 @@ var FILE = require("file"),
|
||||
SYS = require("system"),
|
||||
FileList = require("jake").FileList,
|
||||
stream = require("narwhal/term").stream,
|
||||
StaticResource = require("objective-j").StaticResource,
|
||||
|
||||
DefaultTheme = "Aristo",
|
||||
BuildTypes = ["Debug", "Release"],
|
||||
DefaultXibFile = "MainMenu.xib";
|
||||
DefaultFile = "MainMenu";
|
||||
|
||||
var parser = new (require("narwhal/args").Parser)(),
|
||||
nibInfo = {};
|
||||
nibInfo = {},
|
||||
appDirectory = "",
|
||||
resourcesDirectory = "";
|
||||
|
||||
|
||||
function main(args)
|
||||
@@ -57,7 +60,7 @@ function main(args)
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
CPLog.fatal([anException reason]);
|
||||
CPLog.fatal(exceptionReason(anException));
|
||||
OS.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -68,50 +71,52 @@ function convert(options, inputFile)
|
||||
{
|
||||
inputFile = inputFile || getInputFile(options.args);
|
||||
|
||||
getAppAndResourceDirectoriesFromInputFile(inputFile, options);
|
||||
|
||||
var outputFile = getOutputFile(inputFile, options.args),
|
||||
resourcesPath = "";
|
||||
configInfo = readConfigFile(options.configFile || "", inputFile),
|
||||
defaultTheme = null;
|
||||
|
||||
if (options.resources)
|
||||
if (configInfo.plist)
|
||||
{
|
||||
resourcesPath = FILE.canonical(options.resources);
|
||||
var systemFontFace = configInfo.plist.valueForKey("CPSystemFontFace");
|
||||
|
||||
if (!FILE.isDirectory(resourcesPath) || !FILE.isReadable(resourcesPath))
|
||||
fail("Cannot read resources at: " + resourcesPath);
|
||||
if (systemFontFace)
|
||||
[CPFont setSystemFontFace:systemFontFace];
|
||||
|
||||
var systemFontSize = configInfo.plist.valueForKey("CPSystemFontSize");
|
||||
|
||||
if (systemFontSize)
|
||||
[CPFont setSystemFontSize:parseFloat(systemFontSize, 10)];
|
||||
|
||||
if (!options.defaultTheme)
|
||||
defaultTheme = configInfo.plist.valueForKey("CPDefaultTheme");
|
||||
}
|
||||
|
||||
var configPath = setSystemFontAndSize(options.configFile || "", inputFile),
|
||||
themeName = "",
|
||||
themeDir = options.themeDir || "";
|
||||
|
||||
if (themeDir)
|
||||
themeName = FILE.basename(themeDir, FILE.extension(themeDir));
|
||||
|
||||
themeName = themeName || getDefaultThemeName();
|
||||
|
||||
if (!themeName)
|
||||
fail("Could not determine the theme name.");
|
||||
|
||||
var theme = loadTheme(themeName, themeDir);
|
||||
var themeList = getThemeList(defaultTheme || options.defaultTheme, options),
|
||||
themes = loadThemes(themeList);
|
||||
|
||||
CPLog.info("\n-------------------------------------------------------------");
|
||||
CPLog.info("Input : " + inputFile);
|
||||
CPLog.info("Output : " + outputFile);
|
||||
CPLog.info("Format : " + ["Auto", "Mac", "iPhone"][options.format]);
|
||||
CPLog.info("Resources : " + resourcesPath);
|
||||
CPLog.info("Frameworks : " + (options.frameworks || ""));
|
||||
CPLog.info("Theme : " + themeName);
|
||||
CPLog.info("Config file : " + (configPath || ""));
|
||||
CPLog.info("System Font : " + [CPFont systemFontSize] + "px " + [CPFont systemFontFace]);
|
||||
CPLog.info("Input : " + inputFile);
|
||||
CPLog.info("Output : " + outputFile);
|
||||
CPLog.info("Format : " + ["Auto", "Mac", "iPhone"][options.format]);
|
||||
CPLog.info("Application : " + appDirectory);
|
||||
CPLog.info("Resources : " + resourcesDirectory);
|
||||
CPLog.info("Frameworks : " + (options.frameworks || ""));
|
||||
CPLog.info("Default theme : " + themeList[0]);
|
||||
CPLog.info("Extra 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
|
||||
theme:theme];
|
||||
themes:themes];
|
||||
|
||||
[converter setOutputPath:outputFile];
|
||||
[converter setResourcesPath:resourcesPath];
|
||||
[converter setResourcesPath:resourcesDirectory];
|
||||
|
||||
loadFrameworks(options.frameworks, function()
|
||||
loadFrameworks(options.frameworks, options.verbosity, function()
|
||||
{
|
||||
[converter convert];
|
||||
});
|
||||
@@ -120,7 +125,7 @@ function convert(options, inputFile)
|
||||
}
|
||||
catch (anException)
|
||||
{
|
||||
CPLog.fatal([anException reason]);
|
||||
CPLog.fatal(exceptionReason(anException));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -128,39 +133,72 @@ function convert(options, inputFile)
|
||||
function watch(options)
|
||||
{
|
||||
var verbosity = options.quiet ? -1 : options.verbosity,
|
||||
directory = options.args[0];
|
||||
watchDir = options.args[0];
|
||||
|
||||
if (!directory)
|
||||
directory = FILE.isDirectory("Resources") ? "Resources" : ".";
|
||||
if (!watchDir)
|
||||
watchDir = FILE.canonical(FILE.isDirectory("Resources") ? "Resources" : ".");
|
||||
else
|
||||
{
|
||||
watchDir = FILE.canonical(watchDir);
|
||||
|
||||
directory = FILE.canonical(directory);
|
||||
if (FILE.basename(watchDir) !== "Resources")
|
||||
{
|
||||
var path = FILE.join(watchDir, "Resources");
|
||||
|
||||
if (!FILE.isDirectory(directory))
|
||||
fail("Cannot find the directory: " + directory);
|
||||
if (FILE.isDirectory(path))
|
||||
watchDir = path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FILE.isDirectory(watchDir))
|
||||
fail("Cannot find the directory: " + watchDir);
|
||||
|
||||
// Turn on info messages
|
||||
setLogLevel(1);
|
||||
|
||||
CPLog.info("Watching: " + CPLogColorize(directory, "debug"));
|
||||
var nibs = new FileList(FILE.join(watchDir, "*.[nx]ib")).items(),
|
||||
count = nibs.length;
|
||||
|
||||
// First time through only IB files with no corresponding cib
|
||||
// or a cib with an earlier or equal mtime are converted.
|
||||
while (count--)
|
||||
{
|
||||
var nib = nibs[count],
|
||||
cib = nib.substr(0, nib.length - 4) + ".cib";
|
||||
|
||||
if (FILE.exists(cib) && (FILE.mtime(nib) - FILE.mtime(cib)) <= 0)
|
||||
nibInfo[nib] = FILE.mtime(nib);
|
||||
}
|
||||
|
||||
CPLog.info("Watching: " + CPLogColorize(watchDir, "debug"));
|
||||
CPLog.info("Press Control-C to stop...");
|
||||
|
||||
while (true)
|
||||
{
|
||||
var modifiedNibs = getModifiedNibs(directory);
|
||||
var modifiedNibs = getModifiedNibs(watchDir);
|
||||
|
||||
for (var i = 0; i < modifiedNibs.length; ++i)
|
||||
{
|
||||
var action = modifiedNibs[i][0],
|
||||
path = modifiedNibs[i][1],
|
||||
label = action === "add" ? "Added:" : "Modified:",
|
||||
nib = modifiedNibs[i][1],
|
||||
label = action === "add" ? "Added" : "Modified",
|
||||
level = action === "add" ? "info" : "debug";
|
||||
|
||||
CPLog.info(">> %s %s", CPLogColorize(label, level), path);
|
||||
CPLog.info(">> %s: %s", CPLogColorize(label, level), nib);
|
||||
|
||||
// Don't convert an add if there is an existing cib with a later mtime
|
||||
if (action === "add")
|
||||
{
|
||||
var cib = nib.substr(0, nib.length - 4) + ".cib";
|
||||
|
||||
if (FILE.exists(cib) && (FILE.mtime(nib) - FILE.mtime(cib)) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Let the converter log however the user configured it
|
||||
setLogLevel(verbosity);
|
||||
|
||||
var success = convert(options, path);
|
||||
var success = convert(options, nib);
|
||||
|
||||
setLogLevel(1);
|
||||
|
||||
@@ -185,25 +223,24 @@ function parseOptions(args)
|
||||
.set(true)
|
||||
.help("Ask nib2cib to watch a directory for changes");
|
||||
|
||||
parser.option("-F", "framework", "frameworks")
|
||||
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")
|
||||
.help("Specify a custom default theme which is not set in your Info.plist");
|
||||
|
||||
parser.option("-t", "--theme", "extraThemes")
|
||||
.push()
|
||||
.help("Add a framework to load");
|
||||
|
||||
parser.option("-R", "resources")
|
||||
.set()
|
||||
.help("Set the Resources directory");
|
||||
|
||||
parser.option("--mac", "format")
|
||||
.set(NibFormatMac)
|
||||
.def(NibFormatUndetermined)
|
||||
.help("Set format to Mac");
|
||||
|
||||
parser.option("-t", "--theme-dir", "themeDir")
|
||||
.set()
|
||||
.help("A <theme>.build directory to use for theme attribute values");
|
||||
.displayName("name")
|
||||
.help("An additional theme loaded dynamically by your application");
|
||||
|
||||
parser.option("--config", "configFile")
|
||||
.set()
|
||||
.displayName("path")
|
||||
.help("A path to an Info.plist file from which the system font and/or size can be retrieved");
|
||||
|
||||
// parser.option("--iphone", "format")
|
||||
@@ -218,6 +255,15 @@ function parseOptions(args)
|
||||
.set(true)
|
||||
.help("No output");
|
||||
|
||||
parser.option("-F", "framework", "frameworks")
|
||||
.push()
|
||||
.help("Add a framework to load");
|
||||
|
||||
parser.option("--mac", "format")
|
||||
.set(NibFormatMac)
|
||||
.def(NibFormatUndetermined)
|
||||
.help("Set format to Mac");
|
||||
|
||||
parser.option("--version", "showVersion")
|
||||
.action(printVersionAndExit)
|
||||
.help("Show the version of nib2cib and quit");
|
||||
@@ -254,21 +300,80 @@ function setLogLevel(level)
|
||||
|
||||
function getInputFile(args)
|
||||
{
|
||||
var inputFile = args[0] || DefaultXibFile;
|
||||
var inputFile = args[0] || DefaultFile,
|
||||
path = "";
|
||||
|
||||
if (!/^.+\.xib$/.test(inputFile))
|
||||
inputFile += ".xib";
|
||||
if (!/^.+\.[nx]ib$/.test(inputFile))
|
||||
{
|
||||
if (path = findInputFile(inputFile, ".xib"))
|
||||
inputFile = path;
|
||||
else if (path = findInputFile(inputFile, ".nib"))
|
||||
inputFile = path;
|
||||
else
|
||||
fail("Cannot find the input file (.xib or .nib): " + FILE.canonical(inputFile));
|
||||
}
|
||||
else if (path = findInputFile(inputFile))
|
||||
inputFile = path;
|
||||
else
|
||||
fail("Could not read the input file: " + FILE.canonical(inputFile));
|
||||
|
||||
inputFile = FILE.canonical(inputFile);
|
||||
return FILE.canonical(inputFile);
|
||||
}
|
||||
|
||||
if (!FILE.exists(inputFile) && FILE.basename(FILE.dirname(inputFile)) !== "Resources")
|
||||
if (FILE.isDirectory("Resources"))
|
||||
inputFile = FILE.resolve(inputFile, FILE.join("Resources", FILE.basename(inputFile)));
|
||||
function findInputFile(inputFile, extension)
|
||||
{
|
||||
var path = inputFile;
|
||||
|
||||
if (!FILE.isReadable(inputFile))
|
||||
fail("Cannot read the input file: " + inputFile);
|
||||
if (extension)
|
||||
path += extension;
|
||||
|
||||
return inputFile;
|
||||
if (FILE.isReadable(path))
|
||||
return path;
|
||||
|
||||
if (FILE.basename(FILE.dirname(inputFile)) !== "Resources" && FILE.isDirectory("Resources"))
|
||||
{
|
||||
path = FILE.resolve(path, FILE.join("Resources", FILE.basename(path)));
|
||||
|
||||
if (FILE.isReadable(path))
|
||||
return path;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getAppAndResourceDirectoriesFromInputFile(inputFile, options)
|
||||
{
|
||||
appDirectory = resourcesDirectory = "";
|
||||
|
||||
if (options.resourcesDir)
|
||||
{
|
||||
var path = FILE.canonical(options.resourcesDir);
|
||||
|
||||
if (!FILE.isDirectory(path))
|
||||
fail("Cannot read resources at: " + path);
|
||||
|
||||
resourcesDirectory = path;
|
||||
}
|
||||
|
||||
var parentDir = FILE.dirname(inputFile);
|
||||
|
||||
if (FILE.basename(parentDir) === "Resources")
|
||||
{
|
||||
appDirectory = FILE.dirname(parentDir);
|
||||
resourcesDirectory = resourcesDirectory || parentDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
appDirectory = parentDir;
|
||||
|
||||
if (!resourcesDirectory)
|
||||
{
|
||||
var path = FILE.join(appDirectory, "Resources");
|
||||
|
||||
if (FILE.isDirectory(path))
|
||||
resourcesDirectory = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getOutputFile(inputFile, args)
|
||||
@@ -276,14 +381,16 @@ function getOutputFile(inputFile, args)
|
||||
var outputFile = null;
|
||||
|
||||
if (args.length > 1)
|
||||
{
|
||||
outputFile = args[1];
|
||||
|
||||
if (!/^.+\.cib$/.test(outputFile))
|
||||
outputFile += ".cib";
|
||||
}
|
||||
else
|
||||
outputFile = FILE.basename(inputFile, FILE.extension(inputFile));
|
||||
outputFile = FILE.join(FILE.dirname(inputFile), FILE.basename(inputFile, FILE.extension(inputFile))) + ".cib";
|
||||
|
||||
if (!/^.+\.cib$/.test(outputFile))
|
||||
outputFile += ".cib";
|
||||
|
||||
outputFile = FILE.absolute(outputFile);
|
||||
outputFile = FILE.canonical(outputFile);
|
||||
|
||||
if (!FILE.isWritable(FILE.dirname(outputFile)))
|
||||
fail("Cannot write the output file at: " + outputFile);
|
||||
@@ -291,18 +398,30 @@ function getOutputFile(inputFile, args)
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
function loadFrameworks(frameworkPaths, aCallback)
|
||||
function loadFrameworks(frameworkPaths, verbosity, aCallback)
|
||||
{
|
||||
if (!frameworkPaths || frameworkPaths.length === 0)
|
||||
return aCallback();
|
||||
|
||||
frameworkPaths.forEach(function(aFrameworkPath)
|
||||
{
|
||||
setLogLevel(verbosity);
|
||||
CPLog.info("Loading " + aFrameworkPath);
|
||||
|
||||
var frameworkBundle = [[CPBundle alloc] initWithPath:aFrameworkPath];
|
||||
try
|
||||
{
|
||||
// CPBundle is a bit loquacious with logging, we will defer
|
||||
// logging its exceptions till later.
|
||||
setLogLevel(-1);
|
||||
|
||||
[frameworkBundle loadWithDelegate:nil];
|
||||
var frameworkBundle = [[CPBundle alloc] initWithPath:aFrameworkPath];
|
||||
|
||||
[frameworkBundle loadWithDelegate:nil];
|
||||
}
|
||||
finally
|
||||
{
|
||||
setLogLevel(verbosity);
|
||||
}
|
||||
|
||||
require("browser/timeout").serviceTimeouts();
|
||||
});
|
||||
@@ -318,6 +437,18 @@ function logFormatter(aString, aLevel, aTitle)
|
||||
return CPLogColorize(aString, aLevel);
|
||||
}
|
||||
|
||||
function getThemeList(defaultTheme, options)
|
||||
{
|
||||
var themes = [defaultTheme || getDefaultThemeName()];
|
||||
|
||||
if (options.extraThemes)
|
||||
for (var i = 0; i < options.extraThemes.length; ++i)
|
||||
if (themes.indexOf(options.extraThemes[i]) < 0)
|
||||
themes.push(options.extraThemes[i]);
|
||||
|
||||
return themes;
|
||||
}
|
||||
|
||||
function getDefaultThemeName()
|
||||
{
|
||||
var themeName = nil,
|
||||
@@ -328,6 +459,7 @@ function getDefaultThemeName()
|
||||
for (var i = 0; i < BuildTypes.length; ++i)
|
||||
{
|
||||
var path = FILE.join(cappBuild, BuildTypes[i], "AppKit", "Info.plist");
|
||||
|
||||
themeName = themeNameFromPropertyList(path);
|
||||
|
||||
if (themeName)
|
||||
@@ -352,11 +484,36 @@ function themeNameFromPropertyList(path)
|
||||
return themeName;
|
||||
}
|
||||
|
||||
function loadThemes(themeList)
|
||||
{
|
||||
var themes = [];
|
||||
|
||||
for (var i = 0; i < themeList.length; ++i)
|
||||
themes.push(loadTheme(themeList[i], resourcesDirectory));
|
||||
|
||||
return themes;
|
||||
}
|
||||
|
||||
function loadTheme(themeName, themeDir)
|
||||
{
|
||||
if (/^.+\.blend$/.test(themeName))
|
||||
themeName = themeName.substr(0, themeName.length - ".blend".length);
|
||||
|
||||
var blendName = themeName + ".blend",
|
||||
themePath = "";
|
||||
|
||||
if (themeDir)
|
||||
{
|
||||
themePath = FILE.join(FILE.canonical(themeDir), blendName);
|
||||
|
||||
if (!FILE.isDirectory(themePath))
|
||||
themePath = themeDir = null;
|
||||
}
|
||||
|
||||
if (!themeDir)
|
||||
{
|
||||
cappBuild = SYS.env["CAPP_BUILD"];
|
||||
// Try in $CAPP_BUILD
|
||||
cappBuild = FILE.canonical(SYS.env["CAPP_BUILD"]);
|
||||
|
||||
if (!cappBuild)
|
||||
fail("$CAPP_BUILD is not set, exiting.");
|
||||
@@ -364,69 +521,76 @@ function loadTheme(themeName, themeDir)
|
||||
if (!FILE.isDirectory(cappBuild))
|
||||
fail("$CAPP_BUILD does not exist: " + cappBuild)
|
||||
|
||||
var baseThemeName = themeName,
|
||||
pos = themeName.indexOf("-");
|
||||
|
||||
if (pos > 0)
|
||||
baseThemeName = themeName.substr(0, pos);
|
||||
|
||||
themeDir = FILE.join(cappBuild, baseThemeName + ".build");
|
||||
}
|
||||
|
||||
themeDir = FILE.canonical(themeDir);
|
||||
|
||||
if (!FILE.isDirectory(themeDir))
|
||||
fail("Cannot find the theme directory: " + themeDir);
|
||||
|
||||
var themePath = null;
|
||||
|
||||
for (var i = 0; i < BuildTypes.length; ++i)
|
||||
{
|
||||
var path = FILE.join(themeDir, BuildTypes[i], "Browser.environment/Resources", themeName + ".keyedtheme");
|
||||
|
||||
if (FILE.isReadable(path))
|
||||
for (var i = 0; i < BuildTypes.length; ++i)
|
||||
{
|
||||
themePath = path;
|
||||
break;
|
||||
var path = FILE.join(cappBuild, BuildTypes[i], blendName);
|
||||
|
||||
if (FILE.isDirectory(path))
|
||||
{
|
||||
themePath = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Last resort, try the cwd
|
||||
if (!themePath)
|
||||
{
|
||||
var path = FILE.canonical(blendName);
|
||||
|
||||
if (FILE.isDirectory(path))
|
||||
themePath = path;
|
||||
}
|
||||
}
|
||||
|
||||
if (!themePath)
|
||||
fail("Could not find the keyed theme data for \"" + themeName + "\" in the directory: " + themeDir);
|
||||
fail("Cannot find the theme \"" + themeName + "\"");
|
||||
|
||||
themePath = FILE.canonical(themePath);
|
||||
var plist = FILE.read(themePath);
|
||||
return readTheme(themeName, themePath);
|
||||
}
|
||||
|
||||
if (!plist)
|
||||
fail("Could not read the keyed theme at: " + themePath);
|
||||
function readTheme(name, path)
|
||||
{
|
||||
var themeBundle = new CFBundle(path);
|
||||
|
||||
// The .keyedtheme file has a header that is data I don't need. Strip it off.
|
||||
var m = plist.match(/^t;\d+;/);
|
||||
// By default when we try to load the bundle it will use the CommonJS environment,
|
||||
// but we want the Browser environment. So we override mostEligibleEnvironment().
|
||||
themeBundle.mostEligibleEnvironment = function() { return "Browser"; }
|
||||
themeBundle.load();
|
||||
|
||||
if (!m || m.length === 0)
|
||||
fail("Invalid keyed theme data at: " + themePath);
|
||||
var keyedThemes = themeBundle.valueForInfoDictionaryKey("CPKeyedThemes");
|
||||
|
||||
plist = plist.substr(m[0].length);
|
||||
plist = CFPropertyList.propertyListFromString(plist);
|
||||
if (!keyedThemes)
|
||||
fail("Could not find the keyed themes in the theme: " + path);
|
||||
|
||||
var data = [CPData dataWithPlistObject:plist],
|
||||
theme = [CPKeyedUnarchiver unarchiveObjectWithData:data];
|
||||
var index = keyedThemes.indexOf(name + ".keyedtheme");
|
||||
|
||||
if (index < 0)
|
||||
fail("Could not find the main theme data (" + name + ".keyedtheme" + ") in the theme: " + path);
|
||||
|
||||
// Load the keyed theme data, making sure to resolve it
|
||||
var resourcePath = themeBundle.pathForResource(keyedThemes[index]),
|
||||
themeData = new CFMutableData();
|
||||
|
||||
themeData.setRawString(StaticResource.resourceAtURL(new CFURL(resourcePath), true).contents());
|
||||
|
||||
var theme = [CPKeyedUnarchiver unarchiveObjectWithData:themeData];
|
||||
|
||||
if (!theme)
|
||||
fail("Could not unarchive the theme at: " + themePath);
|
||||
fail("Could not unarchive the theme at: " + path);
|
||||
|
||||
CPLog.debug("Loaded theme: " + themePath);
|
||||
CPLog.debug("Loaded theme: " + path);
|
||||
return theme;
|
||||
}
|
||||
|
||||
function setSystemFontAndSize(configFile, inputFile)
|
||||
function readConfigFile(configFile, inputFile)
|
||||
{
|
||||
var configPath = null;
|
||||
var configPath = null,
|
||||
path;
|
||||
|
||||
// First see if the user passed a config file path
|
||||
if (configFile)
|
||||
{
|
||||
var path = FILE.canonical(configFile);
|
||||
path = FILE.canonical(configFile);
|
||||
|
||||
if (!FILE.isReadable(path))
|
||||
fail("Cannot find the config file: " + path);
|
||||
@@ -435,19 +599,14 @@ function setSystemFontAndSize(configFile, inputFile)
|
||||
}
|
||||
else
|
||||
{
|
||||
// See if we can find an Info.plist in the parent directory of the input file,
|
||||
// if the input file's directory is "Resources".
|
||||
var path = FILE.canonical(FILE.dirname(inputFile));
|
||||
path = FILE.join(appDirectory, "Info.plist");
|
||||
|
||||
if (FILE.basename(path) === "Resources")
|
||||
{
|
||||
path = FILE.join(FILE.dirname(path), "Info.plist");
|
||||
|
||||
if (FILE.isReadable(path))
|
||||
configPath = path;
|
||||
}
|
||||
if (FILE.isReadable(path))
|
||||
configPath = path;
|
||||
}
|
||||
|
||||
var plist = null;
|
||||
|
||||
if (configPath)
|
||||
{
|
||||
var plist = FILE.read(configPath);
|
||||
@@ -459,24 +618,14 @@ function setSystemFontAndSize(configFile, inputFile)
|
||||
|
||||
if (!plist)
|
||||
fail("Could not parse the Info.plist at: " + configPath);
|
||||
|
||||
var systemFontFace = plist.valueForKey("CPSystemFontFace");
|
||||
|
||||
if (systemFontFace)
|
||||
[CPFont setSystemFontFace:systemFontFace];
|
||||
|
||||
var systemFontSize = plist.valueForKey("CPSystemFontSize");
|
||||
|
||||
if (systemFontSize)
|
||||
[CPFont setSystemFontSize:parseFloat(systemFontSize, 10)];
|
||||
}
|
||||
|
||||
return configPath;
|
||||
return {path: configPath, plist: plist};
|
||||
}
|
||||
|
||||
function getModifiedNibs(path)
|
||||
{
|
||||
var nibs = new FileList(FILE.join(path, "*.xib")).items(),
|
||||
var nibs = new FileList(FILE.join(path, "*.[nx]ib")).items(),
|
||||
count = nibs.length,
|
||||
newNibInfo = {},
|
||||
modifiedNibs = [];
|
||||
@@ -501,10 +650,8 @@ function getModifiedNibs(path)
|
||||
}
|
||||
|
||||
for (var nib in nibInfo)
|
||||
{
|
||||
if (nibInfo.hasOwnProperty(nib))
|
||||
CPLog.info(">> %s %s", CPLogColorize("Deleted:", "warn"), nib);
|
||||
}
|
||||
CPLog.info(">> %s: %s", CPLogColorize("Deleted", "warn"), nib);
|
||||
|
||||
nibInfo = newNibInfo;
|
||||
|
||||
@@ -558,6 +705,16 @@ function printVersion()
|
||||
stream.print("<No version info available>");
|
||||
}
|
||||
|
||||
function exceptionReason(exception)
|
||||
{
|
||||
if (typeof(exception) === "string")
|
||||
return exception;
|
||||
else if (exception.isa && [exception respondsToSelector:@selector(reason)])
|
||||
return [exception reason];
|
||||
else
|
||||
return "An unknown error occurred";
|
||||
}
|
||||
|
||||
function fail(message)
|
||||
{
|
||||
[CPException raise:ConverterConversionException reason:message];
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
.Dd April 3, 2011
|
||||
.Os "Cappuccino"
|
||||
.Dt NIB2CIB 1 "PRM"
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Sh NAME
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Nm nib2cib
|
||||
.Nd convert Interface Builder files to Cappuccino cib files
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Sh SYNOPSIS
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Nm
|
||||
--watch
|
||||
.Op options
|
||||
.Op Pa directory
|
||||
.Nm
|
||||
.Op options
|
||||
.Op Pa input Op Pa output
|
||||
.Nm
|
||||
.Op --version | -h | --help
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Sh "DESCRIPTION"
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Nm
|
||||
converts an Interface Builder file into a Cappuccino cib file that can be loaded
|
||||
into a Cappuccino application.
|
||||
.Pp
|
||||
There are two main converting modes in which nib2cib can be used:
|
||||
.Sy watch mode
|
||||
and
|
||||
.Sy single mode.
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Ss "Watch Mode"
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
The easiest way to use nib2cib is in
|
||||
.Sy watch mode.
|
||||
This is done by using the syntax:
|
||||
.Pp
|
||||
.D1 nib2cib --watch [options] Op Pa directory
|
||||
.Pp
|
||||
If
|
||||
.Ar directory
|
||||
is omitted, a "Resources" directory in the current working directory is watched,
|
||||
and if no "Resources" directory exists, the current working directory is watched.
|
||||
.Pp
|
||||
If
|
||||
.Ar directory
|
||||
is passed, is not a path to a "Resources" directory, and there is a "Resources" directory
|
||||
within
|
||||
.Ar directory ,
|
||||
then that "Resources" directory is watched. Otherwise
|
||||
.Ar directory
|
||||
is watched. This allows you to pass the path to your application directory instead
|
||||
of its "Resources" directory.
|
||||
.Pp
|
||||
In watch mode,
|
||||
.Nm
|
||||
goes into an infinite loop and monitors the watched directory for changes to xib/nib
|
||||
files. There are three types of changes
|
||||
.Nm
|
||||
will respond to:
|
||||
.Bl -tag -hang
|
||||
.It add, modify
|
||||
When a new xib/nib file is detected or an existing xib/nib file is modified, it is converted into
|
||||
a cib file with the same base name if there is no corresponding cib file with a later
|
||||
modification time.
|
||||
.It delete
|
||||
When a xib/nib file is deleted, the corresponding cib file is deleted as well.
|
||||
.El
|
||||
.Pp
|
||||
Ordinarily in watch mode
|
||||
.Nm
|
||||
will only display a log line for each file that is added, modified or deleted, and a confirmation
|
||||
when the corresponding cib has been generated or deleted. You can use the
|
||||
.Fl v
|
||||
option in watch mode to display more detailed information when a cib is generated.
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Ss "Single Mode"
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
In
|
||||
.Sy single mode,
|
||||
you convert a single file xib/nib file into a cib. The only advantages of using single mode over
|
||||
watch mode are:
|
||||
.Bl -dash -width 0n
|
||||
.It
|
||||
You may want to view verbose output temporarily to debug the conversion, or use different
|
||||
options for different source files.
|
||||
.It
|
||||
You can specify an output name that is not based on the input name.
|
||||
.El
|
||||
.Pp
|
||||
Most of the time watch mode will be sufficient, but if you do need to use single mode,
|
||||
.Nm
|
||||
tries to make it as easy as possible. If you navigate to your application directory and invoke:
|
||||
.Pp
|
||||
.D1 nib2cib
|
||||
.Pp
|
||||
this will look for "MainMenu.xib" or "MainMenu.nib" in the current directory and then in a "Resources"
|
||||
directory within the current directory, if such a directory exists. Since these are
|
||||
the default names used in Cappuccino, this usage covers the default case with no extra work.
|
||||
.Pp
|
||||
If your xib/nib file is not named "MainMenu.xib" or "MainMenu.nib", or you want to run
|
||||
.Nm
|
||||
from a directory other than the application directory or its "Resources" directory,
|
||||
you can specify an input path:
|
||||
.Pp
|
||||
.D1 nib2cib Pa path
|
||||
.Pp
|
||||
Note that
|
||||
.Pa path
|
||||
does not need the ".xib" or ".nib" extension, since that is assumed. If you were to invoke:
|
||||
.Pp
|
||||
.D1 nib2cib foo
|
||||
.Pp
|
||||
this would look for "foo.xib" and "foo.nib" in the current directory and in a "Resources" directory
|
||||
within the current directory. The output of nib2cib in this case would be "foo.cib" in
|
||||
the same directory as the source file.
|
||||
.Pp
|
||||
If you want
|
||||
.Nm
|
||||
to generate a cib file with a different base name or in a different directory than
|
||||
the input file, you may also specify an output path:
|
||||
.Pp
|
||||
.D1 nib2cib Pa input Pa output
|
||||
.Pp
|
||||
As with input filenames, you do not need to add the ".cib" extension to the output path,
|
||||
this is assumed.
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Ss "Themes"
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
To do its conversions accurately,
|
||||
.Nm
|
||||
needs to access the themes you use. If you are using the standard Aristo theme, there is
|
||||
nothing extra to do.
|
||||
.Pp
|
||||
If you are using a custom default theme, usually you specify this using a
|
||||
.Ar CPDefaultTheme
|
||||
item in your application's Info.plist. In this case
|
||||
.Nm
|
||||
will read the theme name from the Info.plist. If you set the default theme programmatically
|
||||
and not in the Info.plist, then you should use the
|
||||
.Fl \-default-theme
|
||||
option to specify the theme name.
|
||||
.Pp
|
||||
If you are loading additional themes in your code using:
|
||||
.Pp
|
||||
.D1 [[CPThemeBlend alloc] initWithContentsOfURL:]
|
||||
.Pp
|
||||
then you should inform
|
||||
.Nm
|
||||
about it by passing the theme names as
|
||||
.Fl \-theme Ar name
|
||||
options, one for each theme that you load.
|
||||
.Pp
|
||||
Themes are searched for in the Resources, $CAPP_BUILD/Debug and
|
||||
$CAPP_BUILD/Release directories.
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Sh "COMMON OPTIONS"
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
The following options are available in watch mode or single mode:
|
||||
.Bl -tag -width 4n
|
||||
.It Fl v, \-verbose
|
||||
Displays more information about the internal workings of nib2cib. This can be set multiple
|
||||
times to increase the amount of information displayed. If passed once, general information
|
||||
is displayed. If passed more than once, detailed information about individual view conversions
|
||||
is displayed.
|
||||
.It Fl R Pa path
|
||||
NOTE: This option is for the most part no longer necessary.
|
||||
.Pp
|
||||
Specifies the path (relative or absolute) to a directory from which images
|
||||
and custom themes are retrieved. Formerly
|
||||
it was necessary to use this when your xib/nib contained references to images, but now
|
||||
.Nm
|
||||
infers the "Resources" directory from the input file, so usually you will not need to use
|
||||
this option.
|
||||
.It Fl \-default-theme Ar name
|
||||
Specifies the name of the default theme used by your application. This is only necessary
|
||||
if you are not using Aristo and you have not specified the default theme in your application's
|
||||
Info.plist. For more information see
|
||||
.Sy Themes
|
||||
above.
|
||||
.It Fl t, \-theme Ar name
|
||||
Specifies the name of an additional theme to load. May be used multiple times. For more
|
||||
information, see
|
||||
.Sy Themes
|
||||
above.
|
||||
.It Fl \-config Pa path
|
||||
Specifies the path to an Info.plist file from which to read configuration information about
|
||||
your application, such as the system font and default theme. Ordinarily you do not need to
|
||||
use this option, as
|
||||
.Nm
|
||||
uses the Info.plist in the application directory, which is inferred from
|
||||
the input file.
|
||||
.It Fl F
|
||||
Specify the path of a framework to load before converting. This is only useful if:
|
||||
.Bl -dash -width 0n -hang
|
||||
.It
|
||||
Your IB files use classes from the framework.
|
||||
.It
|
||||
Those classes need to serialize/deserialize more data than their superclasses.
|
||||
.It
|
||||
Those classes implement encodeWithCoder: and initWithCoder:.
|
||||
.El
|
||||
.Pp
|
||||
This option may be used multiple times to load multiple frameworks.
|
||||
.It Fl \-quiet
|
||||
Tells
|
||||
.Nm
|
||||
to output nothing. This is useful if you are using
|
||||
.Nm
|
||||
in a shell script and are only interested in the return value.
|
||||
.It Fl \-version
|
||||
Prints the current version of
|
||||
.Nm
|
||||
and immediately exits.
|
||||
.Nm
|
||||
in a shell script and are only interested in the return value.
|
||||
.It Fl h, \-help
|
||||
Displays
|
||||
.Nm
|
||||
usage and options.
|
||||
.El
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Sh "RETURN VALUES"
|
||||
.\"-----------------------------------------------------------------------------------------
|
||||
.Nm
|
||||
returns 0 for a successful conversion and >0 if an error occurred.
|
||||
Reference in New Issue
Block a user