// // NGAValuePropagator.m // // Created by Michael Bishop on 11/3/11. // Copyright (c) 2011 Numerical Garden LLC. All rights reserved. // #import "NGAValuePropagator.h" #import "NGAUtilities.h" @interface NGAValuePropagator : NSObject { NSMutableDictionary * bindings; } +(NGAValuePropagator*)sharedBinder; -(BOOL)addPropagationOfValueForKeyPath:(NSString*)keyPath ofObject:(id)object toKeyPath:(NSString*)secondKeyPath ofObject:(id)secondObject; @end @implementation NGAValuePropagator SINGLETON_IMPLEMENTATION(NGAValuePropagator, sharedBinder); -(id)init { if (!(self = [super init])) return nil; bindings = [NSMutableDictionary new]; return self; } -(void)dealloc { [bindings dealloc]; [super dealloc]; } -(BOOL)addPropagationOfValueForKeyPath:(NSString*)keyPath ofObject:(id)object toKeyPath:(NSString*)secondKeyPath ofObject:(id)secondObject { NSArray * source = [NSArray arrayFromFirstObject:[NSValue valueWithNonretainedObject:object] secondObject:keyPath]; NSArray * target = [NSArray arrayFromFirstObject:[NSValue valueWithNonretainedObject:secondObject] secondObject:secondKeyPath]; NSSet * bindingTargets = [bindings objectForKey:source]; if ( bindingTargets ) { if ( [bindingTargets containsObject:target] ) return NO; bindingTargets = [bindingTargets setByAddingObject:target]; } else { bindingTargets = [NSSet setWithObject:target]; } [bindings setObject:bindingTargets forKey:source]; [object addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL]; [secondObject setValue:[object valueForKeyPath:keyPath] forKeyPath:secondKeyPath]; return YES; } -(BOOL)removePropagationOfValueForKeyPath:(NSString*)keyPath ofObject:(id)object toKeyPath:(NSString*)secondKeyPath ofObject:(id)secondObject { NSArray * source = [NSArray arrayFromFirstObject:[NSValue valueWithNonretainedObject:object] secondObject:keyPath]; NSArray * target = [NSArray arrayFromFirstObject:[NSValue valueWithNonretainedObject:secondObject] secondObject:secondKeyPath]; NSSet * bindingTargets = [bindings objectForKey:source]; if (!bindingTargets || ![bindingTargets containsObject:target]) return NO; bindingTargets = [bindingTargets setByRemovingObject:target]; [bindings setObject:bindingTargets forKey:source]; [object removeObserver:self forKeyPath:keyPath]; return YES; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSArray * source = [NSArray arrayFromFirstObject:[NSValue valueWithNonretainedObject:object] secondObject:keyPath]; NSSet * bindingTargets = [bindings objectForKey:source]; if ( !bindingTargets ) { [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; return; } id newValue = [change objectForKey:NSKeyValueChangeNewKey]; if ( [newValue isEqual:[NSNull null]] ) newValue = nil; for (NSArray * target in bindingTargets ) { [[[target firstObject] nonretainedObjectValue] setValue:newValue forKeyPath:[target secondObject]]; } } @end @implementation NSObject (NGASimpleBinding) -(BOOL)addPropagationOfValueForKeyPath:(NSString*)keyPath intoTargetObject:(id)target keyPath:(NSString*)targetKeyPath { return [[NGAValuePropagator sharedBinder] addPropagationOfValueForKeyPath:keyPath ofObject:self toKeyPath:targetKeyPath ofObject:target]; } -(BOOL)removePropagationOfValueForKeyPath:(NSString*)keyPath intoTargetObject:(id)target keyPath:(NSString*)targetKeyPath { return [[NGAValuePropagator sharedBinder] removePropagationOfValueForKeyPath:keyPath ofObject:self toKeyPath:targetKeyPath ofObject:target]; } @end