API  1.0.0
CPTheme.j
Go to the documentation of this file.
1 /*
2  * CPTheme.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2009, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 var CPThemesByName = { },
28 
29 
33 @implementation CPTheme : CPObject
34 {
35  CPString _name;
36  CPDictionary _attributes;
37 }
38 
39 + (void)setDefaultTheme:(CPTheme)aTheme
40 {
41  CPThemeDefaultTheme = aTheme;
42 }
43 
44 + (CPTheme)defaultTheme
45 {
46  return CPThemeDefaultTheme;
47 }
48 
53 + (void)setDefaultHudTheme:(CPTheme)aTheme
54 {
55  CPThemeDefaultHudTheme = aTheme;
56 }
57 
63 + (CPTheme)defaultHudTheme
64 {
68 }
69 
70 + (CPTheme)themeNamed:(CPString)aName
71 {
72  return CPThemesByName[aName];
73 }
74 
75 - (id)initWithName:(CPString)aName
76 {
77  self = [super init];
78 
79  if (self)
80  {
81  _name = aName;
82  _attributes = @{};
83 
84  CPThemesByName[_name] = self;
85  }
86 
87  return self;
88 }
89 
91 {
92  return _name;
93 }
94 
103 - (CPArray)classNames
104 {
105  return [_attributes allKeys];
106 }
107 
118 - (CPDictionary)attributesForClass:(id)aClass
119 {
120  if (!aClass)
121  return nil;
122 
123  var className = nil;
124 
125  if ([aClass isKindOfClass:[CPString class]])
126  {
127  // See if it is a class name
128  var theClass = CPClassFromString(aClass);
129 
130  if (theClass)
131  aClass = theClass;
132  else
133  className = aClass;
134  }
135 
136  if (!className)
137  {
138  if ([aClass respondsToSelector:@selector(defaultThemeClass)])
139  {
140  className = [aClass defaultThemeClass];
141  }
142  else if ([aClass respondsToSelector:@selector(themeClass)])
143  {
144  CPLog.warn(@"%@ themeClass is deprecated in favor of defaultThemeClass", CPStringFromClass(aClass));
145  className = [aClass themeClass];
146  }
147  else
148  {
149  return nil;
150  }
151  }
152 
153  return [_attributes objectForKey:className];
154 }
155 
172 - (CPDictionary)attributeNamesForClass:(id)aClass
173 {
174  var attributes = [self attributesForClass:aClass];
175 
176  if (attributes)
177  return [attributes allKeys];
178  else
179  return [CPArray array];
180 }
181 
195 - (_CPThemeAttribute)attributeWithName:(CPString)aName forClass:(id)aClass
196 {
197  var attributes = [self attributesForClass:aClass];
198 
199  if (!attributes)
200  return nil;
201 
202  return [attributes objectForKey:aName];
203 }
204 
218 - (id)valueForAttributeWithName:(CPString)aName forClass:(id)aClass
219 {
220  return [self valueForAttributeWithName:aName inState:CPThemeStateNormal forClass:aClass];
221 }
222 
236 - (id)valueForAttributeWithName:(CPString)aName inState:(ThemeState)aState forClass:(id)aClass
237 {
238  var attribute = [self attributeWithName:aName forClass:aClass];
239 
240  if (!attribute)
241  return nil;
242 
243  return [attribute valueForState:aState];
244 }
245 
246 - (void)takeThemeFromObject:(id)anObject
247 {
248  var attributes = [anObject _themeAttributeDictionary],
249  attributeName = nil,
250  attributeNames = [attributes keyEnumerator],
251  objectThemeClass = [anObject themeClass];
252 
253  while ((attributeName = [attributeNames nextObject]) !== nil)
254  [self _recordAttribute:[attributes objectForKey:attributeName] forClass:objectThemeClass];
255 }
256 
257 - (void)_recordAttribute:(_CPThemeAttribute)anAttribute forClass:(CPString)aClass
258 {
259  if (![anAttribute hasValues])
260  return;
261 
262  var attributes = [_attributes objectForKey:aClass];
263 
264  if (!attributes)
265  {
266  attributes = @{};
267 
268  [_attributes setObject:attributes forKey:aClass];
269  }
270 
271  var name = [anAttribute name],
272  existingAttribute = [attributes objectForKey:name];
273 
274  if (existingAttribute)
275  [attributes setObject:[existingAttribute attributeMergedWithAttribute:anAttribute] forKey:name];
276  else
277  [attributes setObject:anAttribute forKey:name];
278 }
279 
280 @end
281 
282 var CPThemeNameKey = @"CPThemeNameKey",
283  CPThemeAttributesKey = @"CPThemeAttributesKey";
284 
285 @implementation CPTheme (CPCoding)
286 
287 - (id)initWithCoder:(CPCoder)aCoder
288 {
289  self = [super init];
290 
291  if (self)
292  {
293  _name = [aCoder decodeObjectForKey:CPThemeNameKey];
294  _attributes = [aCoder decodeObjectForKey:CPThemeAttributesKey];
295 
296  CPThemesByName[_name] = self;
297  }
298 
299  return self;
300 }
301 
302 - (void)encodeWithCoder:(CPCoder)aCoder
303 {
304  [aCoder encodeObject:_name forKey:CPThemeNameKey];
305  [aCoder encodeObject:_attributes forKey:CPThemeAttributesKey];
306 }
307 
308 @end
309 
310 #pragma mark -
311 #pragma mark CSS Theming
312 
313 // The code below adds support for CSS theming with 100% compatibility with current theming system.
314 // The idea is to extend CPColor and CPImage with CSS components and adapt low level UI components to
315 // support this new kind of CPColor/CPImage. See CPImageView, CPView and _CPImageAndTextView.
316 //
317 // To be considered and treated as CSS based, a theme must set to YES the attribute "css-based" for CPView
318 // in the theDescriptor (default value is NO), like in the example below :
319 //
320 // + (CPView)themedView
321 // {
322 // var view = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
323 //
324 // [self registerThemeValues:[[@"css-based", YES]] forView:view];
325 //
326 // return view;
327 // }
328 //
329 // You can use the method -(BOOL)isCSSBased on a theme to determine how to cope with it in your own code.
330 //
331 // The method -(void)setCSSResourcesPath is meant to be used only by CPThemeBlend during theme loading in order to
332 // replace the special path "%%" in CSS components (like in url(%%packed.png) ) with the path to the theme blend resources folder.
333 
334 @implementation CPTheme (CSSTheming)
335 
336 - (void)setCSSResourcesPath:(CPString)pathToResources
337 {
338  [_attributes enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
339  {
340  [anObject enumerateKeysAndObjectsUsingBlock:function(aKey2, anObject2, stop2)
341  {
342  // anObject2 is now a _CPThemeAttribute
343 
344  [[anObject2 values] enumerateKeysAndObjectsUsingBlock:function(aKey3, anObject3, stop3)
345  {
346  if (anObject3.isa && ([anObject3 isKindOfClass:CPImage] || [anObject3 isKindOfClass:CPColor]) && [anObject3 cssDictionary])
347  {
348  // We have a CSS defined image or color
349  [self _fixPathInCSSDictionary:[anObject3 cssDictionary] withPathToResources:pathToResources];
350  [self _fixPathInCSSDictionary:[anObject3 cssBeforeDictionary] withPathToResources:pathToResources];
351  [self _fixPathInCSSDictionary:[anObject3 cssAfterDictionary] withPathToResources:pathToResources];
352  }
353  }];
354  }];
355  }];
356 }
357 
358 - (void)_fixPathInCSSDictionary:(CPDictionary)aDictionary withPathToResources:(CPString)pathToResources
359 {
360  [aDictionary enumerateKeysAndObjectsUsingBlock:function(aKey, anObject, stop)
361  {
362  [aDictionary setObject:[anObject stringByReplacingOccurrencesOfString:@"%%" withString:pathToResources] forKey:aKey];
363  }];
364 }
365 
366 - (BOOL)isCSSBased
367 {
368  return !![self valueForAttributeWithName:@"css-based" forClass:[CPView class]];
369 }
370 
371 @end
372 
373 #pragma mark -
374 
379 function ThemeState(stateNames)
380 {
381  var stateNameKeys = [];
382  this._stateNames = {};
383 
384  for (var key in stateNames)
385  {
386  if (!stateNames.hasOwnProperty(key))
387  continue;
388 
389  if (key !== 'normal')
390  {
391  this._stateNames[key] = true;
392  stateNameKeys.push(key);
393  }
394  }
395 
396  if (stateNameKeys.length === 0)
397  {
398  stateNameKeys.push('normal');
399  this._stateNames['normal'] = true;
400  }
401 
402  stateNameKeys.sort();
403  this._stateNameString = stateNameKeys[0];
404 
405  var stateNameLength = stateNameKeys.length;
406 
407  for (var stateIndex = 1; stateIndex < stateNameLength; stateIndex++)
408  this._stateNameString = this._stateNameString + "+" + stateNameKeys[stateIndex];
409 
410  this._stateNameCount = stateNameLength;
411 }
412 
413 ThemeState.prototype.toString = function()
414 {
415  return this._stateNameString;
416 }
417 
418 ThemeState.prototype.hasThemeState = function(aState)
419 {
420  if (!aState || !aState._stateNames)
421  return false;
422 
423  // We can do this in O(n) because both states have their stateNames already sorted.
424  for (var stateName in aState._stateNames)
425  {
426  if (!aState._stateNames.hasOwnProperty(stateName))
427  continue;
428 
429  if (!this._stateNames[stateName])
430  return false;
431  }
432  return true;
433 }
434 
435 ThemeState.prototype.isSubsetOf = function(aState)
436 {
437  if (aState._stateNameCount < this._stateNameCount)
438  return false;
439 
440  for (var key in this._stateNames)
441  {
442  if (!this._stateNames.hasOwnProperty(key))
443  continue;
444 
445  if (!aState._stateNames[key])
446  return false;
447  }
448  return true;
449 }
450 
451 ThemeState.prototype.without = function(aState)
452 {
453  if (!aState || aState === [CPNull null])
454  return this;
455 
456  var firstTransform = CPThemeWithoutTransform[this._stateNameString],
457  result;
458 
459  if (firstTransform)
460  {
461  result = firstTransform[aState._stateNameString];
462 
463  if (result)
464  return result;
465  }
466 
467  var newStates = {};
468 
469  for (var stateName in this._stateNames)
470  {
471  if (!this._stateNames.hasOwnProperty(stateName))
472  continue;
473 
474  if (!aState._stateNames[stateName])
475  newStates[stateName] = true;
476  }
477 
478  result = ThemeState._cacheThemeState(new ThemeState(newStates));
479 
480  if (!firstTransform)
481  firstTransform = CPThemeWithoutTransform[this._stateNameString] = {};
482 
483  firstTransform[aState._stateNameString] = result;
484 
485  return result;
486 }
487 
488 ThemeState.prototype.and = function(aState)
489 {
490  var firstTransform = CPThemeAndTransform[this._stateNameString],
491  result;
492 
493  if (firstTransform)
494  {
495  result = firstTransform[aState._stateNameString];
496 
497  if (result)
498  return result;
499  }
500 
501  result = CPThemeState(this, aState);
502 
503  if (!firstTransform)
504  firstTransform = CPThemeAndTransform[this._stateNameString] = {};
505 
506  firstTransform[aState._stateNameString] = result;
507 
508  return result;
509 }
510 
511 var CPThemeStates = {},
514 
515 ThemeState._cacheThemeState = function(aState)
516 {
517  // We do this caching so themeState equality works. Basically, doing CPThemeState('foo+bar') === CPThemeState('bar', 'foo') will return true.
518  var themeState = CPThemeStates[String(aState)];
519 
520  if (themeState === undefined)
521  {
522  themeState = aState;
523  CPThemeStates[String(themeState)] = themeState;
524  }
525 
526  return themeState;
527 }
528 
537 function CPThemeState()
538 {
539  if (arguments.length < 1)
540  throw "CPThemeState() must be called with at least one string argument";
541 
542  var themeState;
543 
544  if (arguments.length === 1 && typeof arguments[0] === 'string')
545  {
546  themeState = CPThemeStates[arguments[0]];
547 
548  if (themeState !== undefined)
549  return themeState;
550  }
551 
552  var stateNames = {};
553 
554  for (var argIndex = 0; argIndex < arguments.length; argIndex++)
555  {
556  if (arguments[argIndex] === [CPNull null] || !arguments[argIndex])
557  continue;
558 
559  if (typeof arguments[argIndex] === 'object')
560  {
561  for (var stateName in arguments[argIndex]._stateNames)
562  {
563  if (!arguments[argIndex]._stateNames.hasOwnProperty(stateName))
564  continue;
565 
566  stateNames[stateName] = true;
567  }
568  }
569  else
570  {
571  var allNames = arguments[argIndex].split('+');
572 
573  for (var nameIndex = 0; nameIndex < allNames.length; nameIndex++)
574  stateNames[allNames[nameIndex]] = true;
575  }
576  }
577 
578  themeState = ThemeState._cacheThemeState(new ThemeState(stateNames));
579  return themeState;
580 }
581 
582 @implementation _CPThemeKeyedUnarchiver : CPKeyedUnarchiver
583 {
584  CPBundle _bundle;
585 }
586 
587 - (id)initForReadingWithData:(CPData)data bundle:(CPBundle)aBundle
588 {
589  self = [super initForReadingWithData:data];
590 
591  if (self)
592  _bundle = aBundle;
593 
594  return self;
595 }
596 
597 - (CPBundle)bundle
598 {
599  return _bundle;
600 }
601 
602 - (BOOL)awakenCustomResources
603 {
604  return YES;
605 }
606 
607 @end
608 
609 CPThemeStateNormal = CPThemeState("normal");
610 CPThemeStateDisabled = CPThemeState("disabled");
611 CPThemeStateHovered = CPThemeState("hovered");
612 CPThemeStateHighlighted = CPThemeState("highlighted");
613 CPThemeStateSelected = CPThemeState("selected");
614 CPThemeStateTableDataView = CPThemeState("tableDataView");
615 CPThemeStateSelectedDataView = CPThemeState("selectedTableDataView");
616 CPThemeStateGroupRow = CPThemeState("CPThemeStateGroupRow");
617 CPThemeStateBezeled = CPThemeState("bezeled");
618 CPThemeStateBordered = CPThemeState("bordered");
619 CPThemeStateEditable = CPThemeState("editable");
620 CPThemeStateEditing = CPThemeState("editing");
621 CPThemeStateVertical = CPThemeState("vertical");
622 CPThemeStateDefault = CPThemeState("default");
623 CPThemeStateCircular = CPThemeState("circular");
624 CPThemeStateAutocompleting = CPThemeState("autocompleting");
625 CPThemeStateFirstResponder = CPThemeState("firstResponder");
626 CPThemeStateMainWindow = CPThemeState("mainWindow");
627 CPThemeStateKeyWindow = CPThemeState("keyWindow");
628 CPThemeStateControlSizeRegular = CPThemeState("controlSizeRegular");
629 CPThemeStateControlSizeSmall = CPThemeState("controlSizeSmall");
630 CPThemeStateControlSizeMini = CPThemeState("controlSizeMini");
631 
632 CPThemeStateNormalString = String(CPThemeStateNormal);
633 
634 
635 @implementation _CPThemeAttribute : CPObject
636 {
637  CPString _name;
638  id _defaultValue;
639  CPDictionary _values;
640 
641  JSObject _cache;
642  _CPThemeAttribute _themeDefaultAttribute;
643 }
644 
645 - (id)initWithName:(CPString)aName defaultValue:(id)aDefaultValue defaultAttribute:(_CPThemeAttribute)aDefaultAttribute
646 {
647  self = [super init];
648 
649  if (self)
650  {
651  _cache = { };
652  _name = aName;
653  _defaultValue = aDefaultValue;
654 
655  if (aDefaultAttribute)
656  _themeDefaultAttribute = aDefaultAttribute;
657  }
658 
659  return self;
660 }
661 
662 - (CPString)name
663 {
664  return _name;
665 }
666 
667 - (id)defaultValue
668 {
669  return _defaultValue;
670 }
671 
672 - (BOOL)hasValues
673 {
674  return [_values count] > 0;
675 }
676 
677 - (_CPThemeAttribute)attributeBySettingValue:(id)aValue
678 {
679  var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute];
680 
681  if (aValue !== undefined && aValue !== nil)
682  attribute._values = @{ CPThemeStateNormalString: aValue };
683 
684  return attribute;
685 }
686 
687 - (_CPThemeAttribute)attributeBySettingValue:(id)aValue forState:(ThemeState)aState
688 {
689  var shouldRemoveValue = aValue === undefined || aValue === nil,
690  attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute],
691  values = _values;
692 
693  if (values != null)
694  {
695  values = [values copy];
696 
697  if (shouldRemoveValue)
698  [values removeObjectForKey:String(aState)];
699  else
700  [values setObject:aValue forKey:String(aState)];
701 
702  attribute._values = values;
703  }
704  else if (!shouldRemoveValue)
705  {
706  values = [[CPDictionary alloc] init];
707  [values setObject:aValue forKey:String(aState)];
708  attribute._values = values;
709  }
710 
711  return attribute;
712 }
713 
714 - (id)value
715 {
716  return [self valueForState:CPThemeStateNormal];
717 }
718 
719 - (id)valueForState:(ThemeState)aState
720 {
721  // First, search in cache.
722  var stateName = String(aState),
723  value = _cache[stateName];
724 
725  // This can be nil.
726  if (value !== undefined)
727  return value;
728 
729  // Not in cache. OK, search in values.
730  value = [_values objectForKey:stateName];
731 
732  if ((value !== undefined) && (value !== nil))
733  return _cache[stateName] = value;
734 
735  // No direct match in values.
736  // If this is a composite state, find the closest partial subset match.
737  if (aState._stateNameCount > 1)
738  {
739  var largestThemeState = [self largestThemeStateMatchForState:aState returnedValue:@ref(value)];
740 
741  if ((value !== undefined) && (value !== nil))
742  return _cache[stateName] = value;
743  }
744 
745  // Still don't have a value? OK, let's use the normal value.
746  value = [_values objectForKey:String(CPThemeStateNormal)];
747 
748  if ((value !== undefined) && (value !== nil))
749  return _cache[stateName] = value;
750 
751  // No normal value, try asking _themeDefaultAttribute
752  value = [_themeDefaultAttribute valueForState:aState];
753 
754  if ((value !== undefined) && (value !== nil))
755  return _cache[stateName] = value;
756 
757  // Well, last option, use default value
758  value = _defaultValue;
759 
760  // Class theme attributes cannot use nil because it's a dictionary.
761  // So transform CPNull into nil.
762  if (value === [CPNull null])
763  value = nil;
764 
765  return _cache[stateName] = value;
766 }
767 
768 - (CPInteger)largestThemeStateMatchForState:(ThemeState)aState returnedValue:(id)valueRef
769 {
770  var stateName = String(aState),
771  value,
772  states = [_values allKeys],
773  count = states ? states.length : 0,
774  largestThemeState = 0;
775 
776  while (count--)
777  {
778  var stateObject = CPThemeState(states[count]);
779 
780  if (stateObject.isSubsetOf(aState) && stateObject._stateNameCount > largestThemeState)
781  {
782  value = [_values objectForKey:states[count]];
783  largestThemeState = stateObject._stateNameCount;
784  }
785  }
786 
787  // _themeDefaultAttribute may have a larger theme state match. If so, we have to take it. If not, we take our closest match.
788  var defaultAttributeFoundValue,
789  defaultAttributeMatchLength = [_themeDefaultAttribute largestThemeStateMatchForState:aState returnedValue:@ref(defaultAttributeFoundValue)];
790 
791  if (defaultAttributeMatchLength > largestThemeState)
792  {
793  value = defaultAttributeFoundValue;
794  largestThemeState = defaultAttributeMatchLength;
795  }
796 
797  @deref(valueRef) = value;
798  return largestThemeState;
799 }
800 
801 - (_CPThemeAttribute)attributeBySettingParentAttribute:(_CPThemeAttribute)anAttribute
802 {
803  if (_themeDefaultAttribute === anAttribute)
804  return self;
805 
806  var attribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:anAttribute];
807 
808  attribute._values = [_values copy];
809 
810  return attribute;
811 }
812 
813 - (_CPThemeAttribute)attributeMergedWithAttribute:(_CPThemeAttribute)anAttribute
814 {
815  var mergedAttribute = [[_CPThemeAttribute alloc] initWithName:_name defaultValue:_defaultValue defaultAttribute:_themeDefaultAttribute];
816 
817  mergedAttribute._values = [_values copy];
818 
819  if (anAttribute._values)
820  mergedAttribute._values ? [mergedAttribute._values addEntriesFromDictionary:anAttribute._values] : [anAttribute._values copy];
821 
822  return mergedAttribute;
823 }
824 
826 {
827  return [super description] + @" Name: " + _name + @", defaultAttribute: " + _themeDefaultAttribute + @", defaultValue: " + _defaultValue + @", values: " + _values;
828 }
829 
830 @end
831 
832 
833 // This is used to pass 'parrentAttribute' to the coder
835 
836 @implementation _CPThemeAttribute (CPCoding)
837 
838 - (id)initWithCoder:(CPCoder)aCoder
839 {
840  self = [super init];
841 
842  if (self)
843  {
844  _cache = {};
845 
846  _name = [aCoder decodeObjectForKey:@"name"];
847  _defaultValue = [aCoder decodeObjectForKey:@"defaultValue"];
848  _values = @{};
849  _themeDefaultAttribute = ParentAttributeForCoder;
850 
851  if ([aCoder containsValueForKey:@"value"])
852  {
853  var state;
854 
855  if ([aCoder containsValueForKey:@"state"])
856  state = [aCoder decodeObjectForKey:@"state"];
857  else
858  state = CPThemeStateNormalString;
859 
860  [_values setObject:[aCoder decodeObjectForKey:"value"] forKey:state];
861  }
862  else
863  {
864  var encodedValues = [aCoder decodeObjectForKey:@"values"],
865  keys = [encodedValues allKeys],
866  count = keys.length;
867 
868  while (count--)
869  {
870  var key = keys[count];
871 
872  [_values setObject:[encodedValues objectForKey:key] forKey:key];
873  }
874  }
875  }
876 
877  return self;
878 }
879 
880 - (void)encodeWithCoder:(CPCoder)aCoder
881 {
882  [aCoder encodeObject:_name forKey:@"name"];
883  [aCoder encodeObject:_defaultValue forKey:@"defaultValue"];
884 
885  var keys = [_values allKeys],
886  count = keys ? keys.length : 0;
887 
888  if (count === 1)
889  {
890  var onlyKey = keys[0];
891 
892  if (onlyKey !== String(CPThemeStateNormal))
893  [aCoder encodeObject:onlyKey forKey:@"state"];
894 
895  [aCoder encodeObject:[_values objectForKey:onlyKey] forKey:@"value"];
896  }
897  else
898  {
899  var encodedValues = @{};
900 
901  while (count--)
902  {
903  var key = keys[count];
904 
905  [encodedValues setObject:[_values objectForKey:key] forKey:key];
906  }
907 
908  [aCoder encodeObject:encodedValues forKey:@"values"];
909  }
910 }
911 
912 @end
913 
914 function CPThemeAttributeEncode(aCoder, aThemeAttribute)
915 {
916  var values = aThemeAttribute._values,
917  count = [values count],
918  key = "$a" + [aThemeAttribute name];
919 
920  if (count === 1)
921  {
922  var state = [values allKeys][0];
923 
924  if (state === CPThemeStateNormalString)
925  {
926  [aCoder encodeObject:[values objectForKey:state] forKey:key];
927 
928  return YES;
929  }
930  }
931 
932  if (count >= 1)
933  {
934  [aCoder encodeObject:aThemeAttribute forKey:key];
935 
936  return YES;
937  }
938 
939  return NO;
940 }
941 
942 function CPThemeAttributeDecode(aCoder, attribute)
943 {
944  var key = "$a" + attribute._name;
945 
946  if ([aCoder containsValueForKey:key])
947  {
948  ParentAttributeForCoder = attribute._themeDefaultAttribute;
949 
950  var decodedAttribute = [aCoder decodeObjectForKey:key];
951 
953 
954  if (!decodedAttribute || !decodedAttribute.isa || ![decodedAttribute isKindOfClass:[_CPThemeAttribute class]])
955  attribute = [attribute attributeBySettingValue:decodedAttribute];
956  else
957  attribute = decodedAttribute;
958  }
959 
960  return attribute;
961 }
962 
963 /* TO AUTO CREATE THESE:
964 function bit_count(bits)
965  {
966  var count = 0;
967 
968  while (bits)
969  {
970  ++count;
971  bits &= (bits - 1);
972  }
973 
974  return count ;
975  }
976 
977 zeros = "000000000";
978 
979 function pad(string, digits)
980 {
981  return zeros.substr(0, digits - string.length) + string;
982 }
983 
984 var str = ""
985 str += '[';
986 for (i = 0;i < Math.pow(2,6);++i)
987 {
988  str += bit_count(i) + " /*" + pad(i.toString(2),6) + "*" + "/, ";
989 }
990 print(str+']');
991 
992 */
CPThemeStateEditing
Definition: CPTheme.j:620
CPThemeStateHovered
Definition: CPTheme.j:611
CPThemeStateSelected
Definition: CPTheme.j:613
function CPStringFromClass(aClass)
Definition: CPObjJRuntime.j:38
CPThemeStateControlSizeMini
Definition: CPTheme.j:630
An object representation of nil.
Definition: CPNull.h:2
ThemeState prototype CPThemeWithoutTransform
Definition: CPTheme.j:512
id valueForAttributeWithName:inState:forClass:(CPString aName, [inState] ThemeState aState, [forClass] id aClass)
Definition: CPTheme.j:236
CPThemeStateDefault
Definition: CPTheme.j:622
CPThemeStateNormalString
Definition: CPTheme.j:632
CPThemeStateAutocompleting
Definition: CPTheme.j:624
CPThemeStateEditable
Definition: CPTheme.j:619
A Cappuccino wrapper for any data type.
Definition: CPData.h:2
Unarchives objects created using CPKeyedArchiver.
CPString name()
Definition: CPTheme.j:90
A mutable key-value pair collection.
Definition: CPDictionary.h:2
function ThemeState(stateNames)
Definition: CPTheme.j:379
var CPThemeDefaultHudTheme
Definition: CPTheme.j:27
var CPThemeNameKey
Definition: CPTheme.j:282
CPThemeStateMainWindow
Definition: CPTheme.j:626
CPThemeStateTableDataView
Definition: CPTheme.j:614
CPThemeStateDisabled
Definition: CPTheme.j:610
CPThemeStateBezeled
Definition: CPTheme.j:617
An immutable string (collection of characters).
Definition: CPString.h:2
CPThemeStateCircular
Definition: CPTheme.j:623
function CPThemeAttributeDecode(aCoder, attribute)
Definition: CPTheme.j:942
_CPThemeAttribute attributeWithName:forClass:(CPString aName, [forClass] id aClass)
Definition: CPTheme.j:195
CPThemeStateBordered
Definition: CPTheme.j:618
CPThemeStateSelectedDataView
Definition: CPTheme.j:615
CPTheme defaultTheme()
Definition: CPTheme.j:44
function CPThemeAttributeEncode(aCoder, aThemeAttribute)
Definition: CPTheme.j:914
CPString name
Definition: CPException.j:47
CPDictionary attributesForClass:(id aClass)
Definition: CPTheme.j:118
CPThemeStateVertical
Definition: CPTheme.j:621
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPThemeStateHighlighted
Definition: CPTheme.j:612
CPThemeStateFirstResponder
Definition: CPTheme.j:625
id init()
Definition: CPObject.j:145
Definition: CPTheme.h:2
void enumerateKeysAndObjectsUsingBlock:(Function/*(id aKey, id anObject, @ref BOOL stop) */aFunction)
Definition: CPDictionary.j:651
CPTheme themeNamed:(CPString aName)
Definition: CPTheme.j:70
var ParentAttributeForCoder
Definition: CPTheme.j:834
CPThemeStateControlSizeSmall
Definition: CPTheme.j:629
CPThemeStateControlSizeRegular
Definition: CPTheme.j:628
ThemeState prototype CPThemeAndTransform
Definition: CPTheme.j:513
id valueForAttributeWithName:forClass:(CPString aName, [forClass] id aClass)
Definition: CPTheme.j:218
var CPThemeAttributesKey
Definition: CPTheme.j:283
var CPThemesByName
Definition: CPTheme.j:25
void setObject:forKey:(id anObject, [forKey] id aKey)
Definition: CPDictionary.j:589
CPThemeStateGroupRow
Definition: CPTheme.j:616
function CPClassFromString(aClassName)
Definition: CPObjJRuntime.j:33
var CPThemeDefaultTheme
Definition: CPTheme.j:26
Definition: CPView.j:137
CPThemeStateKeyWindow
Definition: CPTheme.j:627
FrameUpdater prototype description