diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j index 8699a7a52..c6046ad0b 100644 --- a/AppKit/CPApplication.j +++ b/AppKit/CPApplication.j @@ -107,7 +107,7 @@ CPRunContinuesResponse = -1002; CPPanel _aboutPanel; - CPThemeBlend _themeBlend @accessors(property=themeBlend); + CPArray _themeBlends @accessors(property=themeBlends); } /*! @@ -141,6 +141,8 @@ CPRunContinuesResponse = -1002; _windows = []; [_windows addObject:nil]; + + _themeBlends = []; } return self; @@ -338,7 +340,7 @@ CPRunContinuesResponse = -1002; Copyright - Human readable copyright information. - If you choose not the include any of the above keys, they will default + If you choose not the include any of the above keys, they will default to the following respective keys in your info.plist file.
@@ -1003,7 +1005,7 @@ CPRunContinuesResponse  = -1002;
 }
 
 /*!
-    Sets the arguments of your application. 
+    Sets the arguments of your application.
     That is, set the slash seperated values of an array as the window location hash.
 
     For example if you pass an array:
@@ -1156,6 +1158,16 @@ CPRunContinuesResponse  = -1002;
                                                       userInfo:nil];
 }
 
+- (CPThemeBlend)themeBlend
+{
+    return (_themeBlends.length > 0 ? _themeBlends[0] : nil);
+}
+
+- (void)setThemeBlend:(CPThemeBlend)aThemeBlend
+{
+    _themeBlends[0] = aThemeBlend;
+}
+
 + (CPString)defaultThemeName
 {
     return ([[CPBundle mainBundle] objectForInfoDictionaryKey:"CPDefaultTheme"] || @"Aristo");
@@ -1218,15 +1230,14 @@ function CPApplicationMain(args, namedArgs)
     [_CPAppBootstrapper performActions];
 }
 
-var _CPAppBootstrapperActions = nil;
+var _CPAppBootstrapperActions = nil,
+    _CPAppThemeURLsToLoad = [];
 
 @implementation _CPAppBootstrapper : CPObject
-{
-}
 
 + (CPArray)actions
 {
-    return [@selector(bootstrapPlatform), @selector(loadDefaultTheme), @selector(loadMainCibFile)];
+    return [@selector(bootstrapPlatform), @selector(loadThemes), @selector(loadMainCibFile)];
 }
 
 + (void)performActions
@@ -1250,17 +1261,30 @@ var _CPAppBootstrapperActions = nil;
     return [CPPlatform bootstrap];
 }
 
-+ (BOOL)loadDefaultTheme
++ (BOOL)loadThemes
 {
-    var defaultThemeName = [CPApplication defaultThemeName],
-        themeURL = nil;
+    _CPAppThemeURLsToLoad.push([CPApplication defaultThemeName]);
 
-    if (defaultThemeName === @"Aristo")
-        themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:defaultThemeName + @".blend"];
+    var auxiliaryThemes = ([[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPAuxiliaryThemes"] || []);
+
+    _CPAppThemeURLsToLoad = _CPAppThemeURLsToLoad.concat(auxiliaryThemes);
+
+    [self loadNextTheme];
+
+    return YES;
+}
+
++ (BOOL)loadNextTheme
+{
+    var themeName = _CPAppThemeURLsToLoad.shift();
+
+    if (themeName === @"Aristo")
+        themeURL = [[CPBundle bundleForClass:[CPApplication class]] pathForResource:themeName + @".blend"];
     else
-        themeURL = [[CPBundle mainBundle] pathForResource:defaultThemeName + @".blend"];
+        themeURL = [[CPBundle mainBundle] pathForResource:themeName + @".blend"];
 
     var blend = [[CPThemeBlend alloc] initWithContentsOfURL:themeURL];
+
     [blend loadWithDelegate:self];
 
     return YES;
@@ -1271,7 +1295,10 @@ var _CPAppBootstrapperActions = nil;
     [[CPApplication sharedApplication] setThemeBlend:aThemeBlend];
     [CPTheme setDefaultTheme:[CPTheme themeNamed:[CPApplication defaultThemeName]]];
 
-    [self performActions];
+    if (_CPAppThemeURLsToLoad.length === 0)
+        [self performActions];
+    else
+        [self loadNextTheme];
 }
 
 + (BOOL)loadMainCibFile
diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j
index ff37bee69..1383fa831 100644
--- a/AppKit/CPTheme.j
+++ b/AppKit/CPTheme.j
@@ -25,9 +25,10 @@
 @import 
 
 
-var CPThemesByName          = { },
-    CPThemeDefaultTheme     = nil,
-    CPThemeDefaultHudTheme  = nil;
+var CPThemesByName              = { },
+    CPThemeNamesByInheritance   = [],
+    CPThemeDefaultTheme         = nil,
+    CPThemeDefaultHudTheme      = nil;
 
 
 /*!
@@ -76,6 +77,45 @@ var CPThemesByName          = { },
     return CPThemesByName[aName];
 }
 
+/*!
+    Returns an array of names of all loaded themes, in the order they were loaded.
+*/
++ (CPArray)allThemeNames
+{
+    return [CPThemeNamesByInheritance copy];
+}
+
+/*!
+    Returns the closest matching theme for a given view. The matching proceeds as follows:
+
+    - The default theme class name for the class is retrieved.
+    - Starting with the last loaded theme and proceeding towards the default theme,
+      each theme is checked to see if it defines that theme class.
+    - If the theme defines the class name, that theme is returned.
+    - If no themes match, the default theme is returned.
+
+    @param aView The view to match with a theme
+    @return      The first matching theme
+*/
++ (CPTheme)themeForView:(CPView)aView
+{
+    var count = CPThemeNamesByInheritance.length,
+        themeClass = [aView themeClass];
+
+    if (themeClass)
+    {
+        while (count--)
+        {
+            var theme = CPThemesByName[CPThemeNamesByInheritance[count]];
+
+            if ([theme._attributes containsKey:themeClass])
+                return theme;
+        }
+    }
+
+    return CPThemeDefaultTheme;
+}
+
 - (id)initWithName:(CPString)aName
 {
     self = [super init];
@@ -86,6 +126,9 @@ var CPThemesByName          = { },
         _attributes = [CPDictionary dictionary];
 
         CPThemesByName[_name] = self;
+
+        if (![CPThemeNamesByInheritance containsObject:_name])
+            CPThemeNamesByInheritance.push(_name);
     }
 
     return self;
@@ -299,6 +342,9 @@ var CPThemeNameKey          = @"CPThemeNameKey",
         _attributes = [aCoder decodeObjectForKey:CPThemeAttributesKey];
 
         CPThemesByName[_name] = self;
+
+        if (![CPThemeNamesByInheritance containsObject:_name])
+            CPThemeNamesByInheritance.push(_name);
     }
 
     return self;
diff --git a/AppKit/CPView.j b/AppKit/CPView.j
index bd2a5be03..b152ffd9c 100644
--- a/AppKit/CPView.j
+++ b/AppKit/CPView.j
@@ -292,7 +292,7 @@ var CPViewFlags                     = { },
         _DOMImageSizes = [];
 #endif
 
-        _theme = [CPTheme defaultTheme];
+        _theme = [CPTheme themeForView:self];
         _themeState = CPThemeStateNormal;
 
         [self setupViewFlags];