26 var CPThemesByName = { },
27 CPThemeDefaultTheme = nil,
28 CPThemeDefaultHudTheme = nil;
41 + (void)setDefaultTheme:(
CPTheme)aTheme
43 CPThemeDefaultTheme = aTheme;
48 return CPThemeDefaultTheme;
55 + (void)setDefaultHudTheme:(
CPTheme)aTheme
57 CPThemeDefaultHudTheme = aTheme;
67 if (!CPThemeDefaultHudTheme)
68 CPThemeDefaultHudTheme = [
CPTheme themeNamed:[[
self defaultTheme] name] + "-HUD"];
69 return CPThemeDefaultHudTheme;
74 return CPThemesByName[aName];
86 CPThemesByName[_name] =
self;
105 - (CPArray)classNames
107 return [_attributes allKeys];
127 if ([aClass isKindOfClass:[
CPString class]])
140 if ([aClass isKindOfClass:[
CPView class]])
142 if ([aClass respondsToSelector:
@selector(defaultThemeClass)])
143 className = [aClass defaultThemeClass];
144 else if ([aClass respondsToSelector:
@selector(themeClass)])
146 CPLog.warn(
@"%@ themeClass is deprecated in favor of defaultThemeClass",
CPStringFromClass(aClass));
147 className = [aClass themeClass];
156 return [_attributes objectForKey:className];
177 var attributes = [
self attributesForClass:aClass];
180 return [attributes allKeys];
182 return [CPArray array];
198 - (_CPThemeAttribute)attributeWithName:(CPString)aName forClass:(
id)aClass
200 var attributes = [
self attributesForClass:aClass];
205 return [attributes objectForKey:aName];
221 - (id)valueForAttributeWithName:(CPString)aName forClass:(
id)aClass
223 return [
self valueForAttributeWithName:aName inState:CPThemeStateNormal forClass:aClass];
239 - (id)valueForAttributeWithName:(CPString)aName inState:(CPThemeState)aState forClass:(
id)aClass
241 var attribute = [
self attributeWithName:aName forClass:aClass];
246 return [attribute valueForState:aState];
249 - (void)takeThemeFromObject:(
id)anObject
251 var attributes = [anObject _themeAttributeDictionary],
253 attributeNames = [attributes keyEnumerator],
254 objectThemeClass = [anObject themeClass];
256 while ((attributeName = [attributeNames nextObject]) !== nil)
257 [
self _recordAttribute:[attributes objectForKey:attributeName] forClass:objectThemeClass];
260 - (void)_recordAttribute:(_CPThemeAttribute)anAttribute forClass:(CPString)aClass
262 if (![anAttribute hasValues])
265 var attributes = [_attributes objectForKey:aClass];
271 [_attributes setObject:attributes forKey:aClass];
274 var
name = [anAttribute name],
275 existingAttribute = [attributes objectForKey:name];
277 if (existingAttribute)
278 [attributes setObject:[existingAttribute attributeMergedWithAttribute:anAttribute] forKey:name];
280 [attributes setObject:anAttribute forKey:name];
285 var CPThemeNameKey =
@"CPThemeNameKey",
286 CPThemeAttributesKey =
@"CPThemeAttributesKey";
288 @implementation CPTheme (CPCoding)
290 - (id)initWithCoder:(
CPCoder)aCoder
296 _name = [aCoder decodeObjectForKey:CPThemeNameKey];
297 _attributes = [aCoder decodeObjectForKey:CPThemeAttributesKey];
299 CPThemesByName[_name] =
self;
305 - (void)encodeWithCoder:(
CPCoder)aCoder
307 [aCoder encodeObject:_name forKey:CPThemeNameKey];
308 [aCoder encodeObject:_attributes forKey:CPThemeAttributesKey];
318 - (id)initForReadingWithData:(
CPData)data bundle:(
CPBundle)aBundle
320 self = [
super initForReadingWithData:data];
333 - (BOOL)awakenCustomResources
340 var CPThemeStates = {},
341 CPThemeStateNames = {},
342 CPThemeStateCount = 0;
344 function CPThemeState(aStateName)
346 var state = CPThemeStates[aStateName];
348 if (state === undefined)
350 if (aStateName.indexOf(
'+') === -1)
351 state = 1 << CPThemeStateCount++;
355 states = aStateName.split(
'+'),
356 count = states.length;
360 var stateName = states[count],
361 individualState = CPThemeStates[stateName];
363 if (individualState === undefined)
365 individualState = 1 << CPThemeStateCount++;
366 CPThemeStates[stateName] = individualState;
367 CPThemeStateNames[individualState] = stateName;
370 state |= individualState;
374 CPThemeStates[aStateName] = state;
375 CPThemeStateNames[state] = aStateName;
381 function CPThemeStateName(aState)
383 var
name = CPThemeStateNames[aState];
385 if (name !== undefined)
388 if (!(aState & (aState - 1)))
394 for (; state < aState; state <<= 1)
396 name += (name.length === 0 ? '' : '+') + CPThemeStateNames[state];
398 CPThemeStateNames[aState] =
name;
403 CPThemeStateNames[0] =
"normal";
404 CPThemeStateNormal = CPThemeStates["normal"] = 0;
405 CPThemeStateDisabled = CPThemeState(
"disabled");
406 CPThemeStateHovered = CPThemeState(
"hovered");
407 CPThemeStateHighlighted = CPThemeState(
"highlighted");
408 CPThemeStateSelected = CPThemeState(
"selected");
409 CPThemeStateTableDataView = CPThemeState(
"tableDataView");
410 CPThemeStateSelectedDataView = CPThemeState(
"selectedTableDataView");
411 CPThemeStateGroupRow = CPThemeState(
"CPThemeStateGroupRow");
412 CPThemeStateBezeled = CPThemeState(
"bezeled");
413 CPThemeStateBordered = CPThemeState(
"bordered");
414 CPThemeStateEditable = CPThemeState(
"editable");
415 CPThemeStateEditing = CPThemeState(
"editing");
416 CPThemeStateVertical = CPThemeState(
"vertical");
417 CPThemeStateDefault = CPThemeState(
"default");
418 CPThemeStateCircular = CPThemeState(
"circular");
419 CPThemeStateAutocompleting = CPThemeState(
"autocompleting");
420 CPThemeStateMainWindow = CPThemeState(
"mainWindow");
421 CPThemeStateKeyWindow = CPThemeState(
"keyWindow");
423 @implementation _CPThemeAttribute :
CPObject
430 _CPThemeAttribute _themeDefaultAttribute;
433 - (id)initWithName:(CPString)aName defaultValue:(
id)aDefaultValue
441 _defaultValue = aDefaultValue;
455 return _defaultValue;
460 return [_values count] > 0;
465 return ([_values count] === 1) && (Number([_values allKeys][0]) === CPThemeStateNormal);
468 - (void)setValue:(
id)aValue
472 if (aValue === undefined || aValue === nil)
475 _values = @{ String(CPThemeStateNormal): aValue };
478 - (void)setValue:(
id)aValue forState:(CPThemeState)aState
482 if ((aValue === undefined) || (aValue === nil))
483 [_values removeObjectForKey:String(aState)];
485 [_values setObject:aValue forKey:String(aState)];
490 return [
self valueForState:CPThemeStateNormal];
493 - (id)valueForState:(CPThemeState)aState
495 var value = _cache[aState];
498 if (value !== undefined)
501 value = [_values objectForKey:String(aState)];
504 if ((value === undefined || value === nil) && aState !== CPThemeStateNormal)
507 if (aState & (aState - 1))
509 var highestOneCount = 0,
510 states = [_values allKeys],
511 count = states.length;
516 var state = Number(states[count]);
519 if ((state & aState) === state)
521 var oneCount = cachedNumberOfOnes[state];
523 if (oneCount === undefined)
524 oneCount = numberOfOnes(state);
526 if (oneCount > highestOneCount)
528 highestOneCount = oneCount;
529 value = [_values objectForKey:String(state)];
536 if (value === undefined || value === nil)
537 value = [_values objectForKey:String(CPThemeStateNormal)];
540 if (value === undefined || value === nil)
541 value = [_themeDefaultAttribute valueForState:aState];
543 if (value === undefined || value === nil)
545 value = _defaultValue;
549 if (value === [
CPNull null])
553 _cache[aState] = value;
558 - (void)setParentAttribute:(_CPThemeAttribute)anAttribute
560 if (_themeDefaultAttribute === anAttribute)
564 _themeDefaultAttribute = anAttribute;
567 - (_CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
569 var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue];
571 mergedAttribute._values = [_values copy];
572 [mergedAttribute._values addEntriesFromDictionary:anAttribute._values];
574 return mergedAttribute;
579 @implementation _CPThemeAttribute (CPCoding)
581 - (id)initWithCoder:(
CPCoder)aCoder
589 _name = [aCoder decodeObjectForKey:@"name"];
590 _defaultValue = [aCoder decodeObjectForKey:@"defaultValue"];
593 if ([aCoder containsValueForKey:
@"value"])
595 var state = CPThemeStateNormal;
597 if ([aCoder containsValueForKey:
@"state"])
598 state = CPThemeState([aCoder decodeObjectForKey:
@"state"]);
600 [_values setObject:[aCoder decodeObjectForKey:"value"] forKey:state];
604 var encodedValues = [aCoder decodeObjectForKey:@"values"],
605 keys = [encodedValues allKeys],
610 var key = keys[count];
612 [_values setObject:[encodedValues objectForKey:key] forKey:CPThemeState(key)];
620 - (void)encodeWithCoder:(
CPCoder)aCoder
622 [aCoder encodeObject:_name forKey:@"name"];
623 [aCoder encodeObject:_defaultValue forKey:@"defaultValue"];
625 var keys = [_values allKeys],
630 var onlyKey = keys[0];
632 if (Number(onlyKey) !== CPThemeStateNormal)
633 [aCoder encodeObject:CPThemeStateName(Number(onlyKey)) forKey:@"state"];
635 [aCoder encodeObject:[_values objectForKey:onlyKey] forKey:@"value"];
639 var encodedValues = @{};
643 var key = keys[count];
645 [encodedValues setObject:[_values objectForKey:key] forKey:CPThemeStateName(Number(key))];
648 [aCoder encodeObject:encodedValues forKey:@"values"];
654 var cachedNumberOfOnes = [ 0 , 1 , 1 , 2 , 1 , 2 , 2 ,
655 3 , 1 , 2 , 2 , 3 , 2 , 3 ,
656 3 , 4 , 1 , 2 , 2 , 3 , 2 ,
657 3 , 3 , 4 , 2 , 3 , 3 , 4 ,
658 3 , 4 , 4 , 5 , 1 , 2 , 2 ,
659 3 , 2 , 3 , 3 , 4 , 2 , 3 ,
660 3 , 4 , 3 , 4 , 4 , 5 , 2 ,
661 3 , 3 , 4 , 3 , 4 , 4 , 5 ,
662 3 , 4 , 4 , 5 , 4 , 5 , 5 ,
665 var numberOfOnes =
function(aNumber)
673 aNumber &= (aNumber - 1);
676 cachedNumberOfOnes[slot] = count;
681 numberOfOnes.displayName =
"numberOfOnes";
683 function CPThemeAttributeEncode(aCoder, aThemeAttribute)
685 var values = aThemeAttribute._values,
686 count = [values count],
687 key =
"$a" + [aThemeAttribute name];
691 var state = [values allKeys][0];
693 if (Number(state) === 0)
695 [aCoder encodeObject:[values objectForKey:state] forKey:key];
703 [aCoder encodeObject:aThemeAttribute forKey:key];
711 function CPThemeAttributeDecode(aCoder, anAttributeName, aDefaultValue, aTheme, aClass)
713 var key =
"$a" + anAttributeName;
715 if (![aCoder containsValueForKey:key])
716 var attribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
720 var attribute = [aCoder decodeObjectForKey:key];
722 if (!attribute.isa || ![attribute isKindOfClass:[_CPThemeAttribute
class]])
724 var themeAttribute = [[_CPThemeAttribute alloc] initWithName:anAttributeName defaultValue:aDefaultValue];
726 [themeAttribute setValue:attribute];
728 attribute = themeAttribute;
732 if (aTheme && aClass)
733 [attribute setParentAttribute:[aTheme attributeWithName:anAttributeName forClass:aClass]];