43 BOOL _automaticallyPreparesContent;
57 + (CPSet)keyPathsForValuesAffectingContentObject
59 return [CPSet setWithObjects:"content"];
62 + (BOOL)automaticallyNotifiesObserversForKey:(
CPString)aKey
64 if (aKey ===
@"contentObject")
70 + (CPSet)keyPathsForValuesAffectingCanAdd
72 return [CPSet setWithObject:"editable"];
75 + (CPSet)keyPathsForValuesAffectingCanInsert
77 return [CPSet setWithObject:"editable"];
80 + (CPSet)keyPathsForValuesAffectingCanRemove
82 return [CPSet setWithObjects:"editable", "selection"];
99 - (id)initWithContent:(
id)aContent
101 if (
self = [super
init])
121 return _contentObject;
128 - (void)setContent:(
id)aContent
131 [
self _selectionWillChange];
133 _contentObject = aContent;
135 [
self _selectionDidChange];
142 - (void)_setContentObject:(
id)aContent
144 [
self setContent:aContent];
152 return [
self content];
162 - (void)setAutomaticallyPreparesContent:(BOOL)shouldAutomaticallyPrepareContent
164 _automaticallyPreparesContent = shouldAutomaticallyPrepareContent;
171 - (BOOL)automaticallyPreparesContent
173 return _automaticallyPreparesContent;
179 - (void)prepareContent
188 - (void)setObjectClass:(Class)aClass
190 _objectClass = aClass;
206 - (id)_defaultNewObject
208 return [[[
self objectClass] alloc] init];
217 return [
self _defaultNewObject];
224 - (void)addObject:(
id)anObject
228 var binderClass = [[
self class] _binderClassForBinding:@"contentObject"];
229 [[binderClass getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
236 - (void)removeObject:(
id)anObject
238 if ([
self content] === anObject)
241 var binderClass = [[
self class] _binderClassForBinding:@"contentObject"];
242 [[binderClass getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
249 - (void)add:(
id)aSender
267 - (void)remove:(
id)aSender
285 - (void)setEditable:(BOOL)shouldBeEditable
287 _isEditable = shouldBeEditable;
301 - (CPArray)selectedObjects
303 return [[_CPObservableArray alloc] initWithArray:[_contentObject]];
317 - (void)_selectionWillChange
319 [_selection controllerWillChange];
320 [
self willChangeValueForKey:@"selection"];
326 - (void)_selectionDidChange
328 if (_selection === undefined || _selection === nil)
331 [_selection controllerDidChange];
332 [
self didChangeValueForKey:@"selection"];
340 return _observedKeys;
343 - (void)addObserver:(
id)anObserver forKeyPath:(
CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(
id)context
345 [_observedKeys addObject:aKeyPath];
349 - (void)removeObserver:(
id)anObserver forKeyPath:(
CPString)aKeyPath
351 [_observedKeys removeObject:aKeyPath];
370 var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
374 [
self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
376 [
self setContent:[aCoder decodeObjectForKey:CPObjectControllerContentKey]];
386 [aCoder encodeObject:[
self content]
forKey:CPObjectControllerContentKey];
389 [aCoder encodeObject:CPStringFromClass(_objectClass) forKey:CPObjectControllerObjectClassNameKey];
390 else if (_objectClassName)
391 [aCoder encodeObject:_objectClassName forKey:CPObjectControllerObjectClassNameKey];
393 [aCoder encodeBool:[
self isEditable] forKey:CPObjectControllerIsEditableKey];
399 if (![
self content] && [
self automaticallyPreparesContent])
405 @implementation _CPObservationProxy :
CPObject
417 - (id)initWithKeyPath:(
id)aKeyPath observer:(
id)anObserver object:(
id)anObject
419 if (
self = [super
init])
422 _observer = anObserver;
449 - (void)setNotifyObject:(BOOL)notify
451 _notifyObject = notify;
456 if (
self === anObject)
459 if (!anObject || [anObject
class] !== [
self class] || anObject._observer !== _observer || anObject._keyPath !== _keyPath || anObject._object !== _object)
465 - (void)observeValueForKeyPath:(
CPString)aKeyPath ofObject:(
id)anObject change:(
CPDictionary)change context:(
id)context
468 [_object observeValueForKeyPath:aKeyPath ofObject:_object change:change context:context];
470 [_observer observeValueForKeyPath:aKeyPath ofObject:_object change:change context:context];
475 return [
super description] + [
CPString stringWithFormat:
@"observation proxy for %@ on key path %@", _observer, _keyPath];
481 @implementation _CPObservableArray : _CPJavaScriptArray
483 CPArray _observationProxies;
491 var ivars = class_copyIvarList(
self),
492 count = ivars.length;
495 a[ivar_getName(ivars[count])] = nil;
502 return "<_CPObservableArray: " + [super description] + " >";
505 - (id)initWithArray:(CPArray)anArray
507 self = [
super initWithArray:anArray];
509 self.isa = [_CPObservableArray class];
510 self._observationProxies = [];
515 - (void)addObserver:(
id)anObserver forKeyPath:(
CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(
id)context
517 if (aKeyPath.charAt(0) ===
"@")
520 if ([_CPCollectionKVCOperator isSimpleCollectionOperator:aKeyPath])
523 var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self];
525 proxy._options = options;
526 proxy._context = context;
528 [_observationProxies addObject:proxy];
530 var dotIndex = aKeyPath.indexOf(
"."),
531 remaining = aKeyPath.substring(dotIndex + 1),
534 [
self addObserver:proxy toObjectsAtIndexes:indexes forKeyPath:remaining options:options context:context];
539 [
self addObserver:anObserver toObjectsAtIndexes:indexes forKeyPath:aKeyPath options:options context:context];
543 - (void)removeObserver:(
id)anObserver forKeyPath:(
CPString)aKeyPath
545 if (aKeyPath.charAt(0) ===
"@")
548 if ([_CPCollectionKVCOperator isSimpleCollectionOperator:aKeyPath])
551 var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self],
552 index = [_observationProxies indexOfObject:proxy];
554 proxy = [_observationProxies objectAtIndex:index];
556 var dotIndex = aKeyPath.indexOf(
"."),
557 remaining = aKeyPath.substring(dotIndex + 1),
560 [
self removeObserver:proxy fromObjectsAtIndexes:indexes forKeyPath:remaining];
565 [
self removeObserver:anObserver fromObjectsAtIndexes:indexes forKeyPath:aKeyPath];
569 - (void)insertObject:(
id)anObject atIndex:(CPUInteger)anIndex
571 for (var i = 0, count = [_observationProxies count]; i < count; i++)
573 var proxy = [_observationProxies objectAtIndex:i],
574 keyPath = [proxy keyPath],
575 operator = keyPath.charAt(0) ===
".";
578 [
self willChangeValueForKey:keyPath];
580 [anObject addObserver:proxy forKeyPath:keyPath options:[proxy options] context:[proxy context]];
583 [
self didChangeValueForKey:keyPath];
586 [
super insertObject:anObject atIndex:anIndex];
589 - (void)removeObjectAtIndex:(CPUInteger)anIndex
591 var currentObject = [
self objectAtIndex:anIndex];
593 for (var i = 0, count = [_observationProxies count]; i < count; i++)
595 var proxy = [_observationProxies objectAtIndex:i],
596 keyPath = [proxy keyPath],
597 operator = keyPath.charAt(0) ===
".";
600 [
self willChangeValueForKey:keyPath];
602 [currentObject removeObserver:proxy forKeyPath:keyPath];
605 [
self didChangeValueForKey:keyPath];
608 [
super removeObjectAtIndex:anIndex];
611 - (CPArray)objectsAtIndexes:(
CPIndexSet)theIndexes
613 return [_CPObservableArray arrayWithArray:[
super objectsAtIndexes:theIndexes]];
616 - (void)addObject:(
id)anObject
618 [
self insertObject:anObject atIndex:[
self count]];
621 - (void)removeLastObject
623 [
self removeObjectAtIndex:[
self count]];
626 - (void)replaceObjectAtIndex:(CPUInteger)anIndex withObject:(
id)anObject
628 var currentObject = [
self objectAtIndex:anIndex];
630 for (var i = 0, count = [_observationProxies count]; i < count; i++)
632 var proxy = [_observationProxies objectAtIndex:i],
633 keyPath = [proxy keyPath],
634 operator = keyPath.charAt(0) ===
".";
637 [
self willChangeValueForKey:keyPath];
639 [currentObject removeObserver:proxy forKeyPath:keyPath];
640 [anObject addObserver:proxy forKeyPath:keyPath options:[proxy options] context:[proxy context]];
643 [
self didChangeValueForKey:keyPath];
646 [
super replaceObjectAtIndex:anIndex withObject:anObject];
657 CPArray _observationProxies;
659 Object _observedObjectsByKeyPath;
662 - (id)initWithController:(
id)aController
664 if (
self = [super
init])
667 _observationProxies = [CPArray array];
668 _controller = aController;
669 _observedObjectsByKeyPath = {};
675 - (id)_controllerMarkerForValues:(CPArray)theValues
677 var count = [theValues count],
682 else if (count === 1)
683 value = [theValues objectAtIndex:0];
686 if ([_controller alwaysUsesMultipleValuesMarker])
690 value = [theValues objectAtIndex:0];
694 if (![value
isEqual:[theValues objectAtIndex:i]])
700 if (value === nil || value.isa && [value
isEqual:[
CPNull null]])
708 var values = [[_controller selectedObjects] valueForKeyPath:theKeyPath];
711 if ([values isKindOfClass:CPArray] || [values isKindOfClass:CPSet])
713 var value = [
self _controllerMarkerForValues:values];
714 [_cachedValues setObject:value forKey:theKeyPath];
727 - (void)setValue:(
id)theValue forKeyPath:(
CPString)theKeyPath
729 [[_controller selectedObjects] setValue:theValue forKeyPath:theKeyPath];
730 [_cachedValues removeObjectForKey:theKeyPath];
744 - (void)setValue:(
id)theValue forKey:(
CPString)theKeyPath
751 return [_cachedValues count];
756 return [_cachedValues keyEnumerator];
759 - (void)controllerWillChange
761 _keys = [_cachedValues allKeys];
766 for (var i = 0, count = _keys.length; i < count; i++)
767 [
self willChangeValueForKey:_keys[i]];
769 [_cachedValues removeAllObjects];
772 - (void)controllerDidChange
774 [_cachedValues removeAllObjects];
779 for (var i = 0, count = _keys.length; i < count; i++)
780 [
self didChangeValueForKey:_keys[i]];
785 - (void)observeValueForKeyPath:(
CPString)aKeyPath ofObject:(
id)anObject change:(
CPDictionary)change context:(
id)context
787 [_cachedValues removeObjectForKey:aKeyPath];
790 - (void)addObserver:(
id)anObject forKeyPath:(
CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(
id)context
792 var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObject object:self];
794 [proxy setNotifyObject:YES];
795 [_observationProxies addObject:proxy];
798 var observedObjects = [_controller selectedObjects];
799 _observedObjectsByKeyPath[aKeyPath] = observedObjects;
800 [observedObjects addObserver:proxy forKeyPath:aKeyPath options:options context:context];
803 - (void)removeObserver:(
id)anObject forKeyPath:(
CPString)aKeyPath
805 [_observationProxies enumerateObjectsUsingBlock:function(aProxy, idx, stop)
807 if (aProxy._object === self && aProxy._keyPath == aKeyPath && aProxy._observer === anObject)
809 var observedObjects = _observedObjectsByKeyPath[aKeyPath];
811 [observedObjects removeObserver:aProxy forKeyPath:aKeyPath];
812 [_observationProxies removeObjectAtIndex:idx];
814 _observedObjectsByKeyPath[aKeyPath] = nil;