mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-26 05:27:03 +00:00
331 lines
8.0 KiB
Plaintext
331 lines
8.0 KiB
Plaintext
|
|
@import <Foundation/CPObject.j>
|
|
|
|
|
|
CPControlStateNormal = 0,
|
|
CPControlStateHighlighted = 1 << 0,
|
|
CPControlStateDisabled = 1 << 1,
|
|
CPControlStateSelected = 1 << 2,
|
|
CPControlStateDefault = 1 << 3;
|
|
|
|
var BIT_COUNT = [ 0 /*0000*/, 1 /*0001*/, 1 /*0010*/, 2 /*0011*/, 1 /*0100*/, 2 /*0101*/, 2 /*0110*/, 3 /*0111*/,
|
|
1 /*1000*/, 2 /*1001*/, 2 /*1010*/, 3 /*1011*/, 2 /*1100*/, 3 /*1101*/, 3 /*1110*/, 4 /*1111*/],
|
|
bit_count = function(bits)
|
|
{
|
|
var count = 0;
|
|
|
|
while (bits)
|
|
{
|
|
++count;
|
|
bits &= (bits - 1);
|
|
}
|
|
|
|
return count ;
|
|
}
|
|
|
|
@implementation CPThemedValue : CPObject
|
|
{
|
|
BOOL _isSingularObject;
|
|
|
|
CPTheme _theme;
|
|
CPString _identifier;
|
|
Class _themedClass;
|
|
|
|
id _defaultValue;
|
|
|
|
id _values;
|
|
id _valueFromTheme;
|
|
}
|
|
|
|
- (id)initWithDefaultValue:(id)aDefaultValue identifier:(CPString)anIdentifier theme:(CPTheme)aTheme class:(Class)aClass
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
_theme = aTheme;
|
|
_themedClass = aClass;
|
|
_identifier = anIdentifier;
|
|
_defaultValue = aDefaultValue;
|
|
|
|
_isSingularObject = YES;
|
|
_values = nil;
|
|
_valueFromTheme = [_theme valueForIdentifier:_identifier inClass:_themedClass];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)setDefaultValue:(id)aValue
|
|
{
|
|
_defaultValue = aValue;
|
|
}
|
|
|
|
- (void)setIdentifier:(CPString)anIdentifier
|
|
{
|
|
if (_identifier === anIdentifier)
|
|
return;
|
|
|
|
_identifier = anIdentifier;
|
|
_valueFromTheme = [_theme valueForIdentifier:_identifier inClass:_themedClass];
|
|
}
|
|
|
|
- (CPString)identifier
|
|
{
|
|
return _identifier;
|
|
}
|
|
|
|
- (void)setTheme:(CPTheme)aTheme
|
|
{
|
|
if (_theme === aTheme)
|
|
return;
|
|
|
|
_theme = aTheme;
|
|
_valueFromTheme = [_theme valueForIdentifier:_identifier inClass:_themedClass];
|
|
}
|
|
|
|
- (void)setThemedClass:(Class)aClass
|
|
{
|
|
if (_themedClass === aClass)
|
|
return;
|
|
|
|
_themedClass = aClass;
|
|
_valueFromTheme = [_theme valueForIdentifier:_identifier inClass:_themedClass];
|
|
}
|
|
|
|
- (void)setValue:(id)aValue
|
|
{
|
|
_isSingularObject = YES;
|
|
_values = aValue;
|
|
}
|
|
|
|
- (id)value
|
|
{
|
|
return [self valueForControlState:CPControlStateNormal];
|
|
}
|
|
|
|
- (void)setValue:(id)aValue forControlState:(CPControlState)aState
|
|
{
|
|
if (aState !== CPControlStateNormal)
|
|
{
|
|
if (_isSingularObject)
|
|
{
|
|
var normalValue = _values;
|
|
|
|
_isSingularObject = NO;
|
|
|
|
_values = {};
|
|
|
|
if (normalValue)
|
|
_values[CPControlStateNormal] = normalValue;
|
|
}
|
|
|
|
_values[aState] = aValue;
|
|
}
|
|
|
|
else if (_isSingularObject)
|
|
_values = aValue;
|
|
|
|
else
|
|
_values[CPControlStateNormal] = aValue;
|
|
}
|
|
|
|
- (id)valueForControlState:(CPControlState)aState
|
|
{
|
|
if (_isSingularObject)
|
|
{
|
|
var value = _values;
|
|
|
|
if (value === undefined || value === nil)
|
|
value = [_valueFromTheme valueForControlState:aState];
|
|
|
|
if (value === undefined || value === nil)
|
|
value = _defaultValue;
|
|
|
|
return value;
|
|
}
|
|
|
|
var value = _values[aState];
|
|
|
|
// If we don't have a value, and we have a non-normal state...
|
|
if ((value === undefined || value === nil) && aState > 0)
|
|
{
|
|
// If this is a composite state, find the closest partial subset match.
|
|
if (!(aState & (aState - 1)))
|
|
{
|
|
var highestBitCount = 0;
|
|
|
|
for (state in _values)
|
|
{
|
|
if (!_values.hasOwnProperty(state))
|
|
continue;
|
|
|
|
// A & B = A iff A < B
|
|
if ((state & aState) === state)
|
|
{
|
|
var bitCount = (state < BIT_COUNT.length) ? BIT_COUNT[state] : bit_count(state);
|
|
|
|
if (bitCount > highestBitCount)
|
|
{
|
|
highestBitCount = bitCount;
|
|
value = _values[state];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Still don't have a value? OK, let's use the normal value.
|
|
if (value === undefined || value === nil)
|
|
value = _values[CPControlStateNormal];
|
|
}
|
|
|
|
if (value === undefined || value === nil)
|
|
value = [_valueFromTheme valueForControlState:aState];
|
|
|
|
if (value === undefined || value === nil)
|
|
value = _defaultValue;
|
|
|
|
return value;
|
|
}
|
|
|
|
- (id)initWithCoder:(CPCoder)aCoder
|
|
{
|
|
self = [super init];
|
|
|
|
if (self)
|
|
{
|
|
_isSingularObject = [aCoder containsValueForKey:@"value"];
|
|
|
|
if (_isSingularObject)
|
|
_values = [aCoder decodeObjectForKey:"value"];
|
|
|
|
else
|
|
{
|
|
_values = {};
|
|
|
|
var statesAndValues = [aCoder decodeObjectForKey:"statesAndValues"];
|
|
count = [statesAndValues count];
|
|
|
|
while (count--)
|
|
{
|
|
var value = statesAndValues[count--],
|
|
state = statesAndValues[count];
|
|
|
|
_values[state] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)encodeWithCoder:(CPCoder)aCoder
|
|
{
|
|
if (_isSingularObject)
|
|
{
|
|
[aCoder encodeObject:_values forKey:@"value"];
|
|
|
|
return;
|
|
}
|
|
|
|
var statesAndValues = [];
|
|
|
|
for (state in _values)
|
|
{
|
|
if (!_values.hasOwnProperty(state))
|
|
continue;
|
|
|
|
statesAndValues.push(state);
|
|
statesAndValues.push(_values[state]);
|
|
}
|
|
|
|
[aCoder encodeObject:statesAndValues forKey:@"statesAndValues"];
|
|
}
|
|
|
|
@end
|
|
|
|
function CPThemedValueMake(aDefaultValue, anIdentifier, aTheme, aClass)
|
|
{
|
|
return [[CPThemedValue alloc] initWithDefaultValue:aDefaultValue identifier:anIdentifier theme:aTheme class:aClass];
|
|
}
|
|
|
|
function CPThemedValueEncode(aCoder, aKey, aThemedValue)
|
|
{
|
|
if (aThemedValue._isSingularObject)
|
|
{
|
|
var actualValue = aThemedValue._values;
|
|
|
|
if (aThemedValue._values)
|
|
[aCoder encodeObject:actualValue forKey:aKey];
|
|
}
|
|
else
|
|
[aCoder encodeObject:aThemedValue forKey:aKey];
|
|
}
|
|
|
|
function CPThemedValueDecode(aCoder, aKey, aDefaultValue, anIdentifier, aTheme, aClass)
|
|
{
|
|
if (![aCoder containsValueForKey:aKey])
|
|
return CPThemedValueMake(aDefaultValue, anIdentifier, aTheme, aClass);
|
|
|
|
var value = [aCoder decodeObjectForKey:aKey];
|
|
|
|
if (![value isKindOfClass:[CPThemedValue class]])
|
|
{
|
|
var themedValue = CPThemedValueMake(aDefaultValue, anIdentifier, aTheme, aClass);
|
|
|
|
[themedValue setValue:value];
|
|
|
|
value = themedValue;
|
|
}
|
|
else
|
|
{
|
|
[value setDefaultValue:aDefaultValue];
|
|
[value setTheme:aTheme];
|
|
[value setIdentifier:anIdentifier];
|
|
[value setThemedClass:aClass];
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function CPThemedValueEncode2(aCoder, aThemedValue)
|
|
{
|
|
if (aThemedValue._isSingularObject)
|
|
{
|
|
var actualValue = aThemedValue._values;
|
|
|
|
if (aThemedValue._values)
|
|
[aCoder encodeObject:actualValue forKey:"$a" + [aThemedValue identifier]];
|
|
}
|
|
else
|
|
[aCoder encodeObject:aThemedValue forKey:"$a" + [aThemedValue identifier]];
|
|
}
|
|
|
|
function CPThemedValueDecode2(aCoder, anAttributeName, aDefaultValue, aTheme, aClass)
|
|
{
|
|
var key = "$a" + anAttributeName;
|
|
|
|
if (![aCoder containsValueForKey:key])
|
|
return CPThemedValueMake(aDefaultValue, anAttributeName, aTheme, aClass);
|
|
|
|
var value = [aCoder decodeObjectForKey:key];
|
|
|
|
if (![value isKindOfClass:[CPThemedValue class]])
|
|
{
|
|
var themedValue = CPThemedValueMake(aDefaultValue, anAttributeName, aTheme, aClass);
|
|
|
|
[themedValue setValue:value];
|
|
|
|
value = themedValue;
|
|
}
|
|
else
|
|
{
|
|
[value setDefaultValue:aDefaultValue];
|
|
[value setTheme:aTheme];
|
|
[value setIdentifier:anAttributeName];
|
|
[value setThemedClass:aClass];
|
|
}
|
|
|
|
return value;
|
|
}
|