Created
January 18, 2012 13:09
-
-
Save nevyn/1632940 to your computer and use it in GitHub Desktop.
half-way generalized KVO undoer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Cocoa/Cocoa.h> | |
#import "SPLangNode.h" | |
@interface SPLangUndoer : NSObject { | |
NSUndoManager *mgr; | |
SPLangNode *root; | |
} | |
-(id)initWatchingTree:(SPLangNode*)tree withUndo:(NSUndoManager*)undoManager; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "SPLangUndoer.h" | |
@interface SPLangUndoer () | |
@property (retain) NSUndoManager *mgr; | |
@property (retain) SPLangNode *root; | |
-(void)watchNodeWithSubtree:(SPLangNode*)node; | |
-(void)unwatchNodeWithSubtree:(SPLangNode*)node; | |
-(void)watchTranslation:(SPTranslation*)translation; | |
-(void)unwatchTranslation:(SPTranslation*)translation; | |
@end | |
static NSString *UndoKVOContext = @"UndoKVOContext"; | |
@implementation SPLangUndoer | |
@synthesize mgr, root; | |
-(id)initWatchingTree:(SPLangNode*)tree withUndo:(NSUndoManager*)undoManager; | |
{ | |
self.mgr = undoManager; | |
self.root = tree; | |
[self watchNodeWithSubtree:tree]; | |
return self; | |
} | |
-(void)dealloc; | |
{ | |
[self unwatchNodeWithSubtree:self.root]; | |
self.mgr = nil; | |
self.root = nil; | |
[super dealloc]; | |
} | |
-(void)watchNodeWithSubtree:(SPLangNode*)node; | |
{ | |
for (NSString *prop in [[node class] undoableKeyPaths]) | |
[node addObserver:self forKeyPath:prop options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:UndoKVOContext]; | |
for (SPTranslation *translation in [node valueForKey:@"translations"]) | |
[self watchTranslation:translation]; | |
for (SPLangNode *child in [node valueForKey:@"children"]) | |
[self watchNodeWithSubtree:child]; | |
} | |
-(void)unwatchNodeWithSubtree:(SPLangNode*)node; | |
{ | |
for (SPLangNode *child in [node valueForKey:@"children"]) | |
[self unwatchNodeWithSubtree:child]; | |
for (SPTranslation *translation in [node valueForKey:@"translations"]) | |
[self unwatchTranslation:translation]; | |
for (NSString *prop in [[node class] undoableKeyPaths]) | |
[node removeObserver:self forKeyPath:prop]; | |
} | |
-(void)watchTranslation:(SPTranslation*)translation; | |
{ | |
for (NSString *prop in [[translation class] undoableKeyPaths]) | |
[translation addObserver:self forKeyPath:prop options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:UndoKVOContext]; | |
} | |
-(void)unwatchTranslation:(SPTranslation*)translation; | |
{ | |
for (NSString *prop in [[translation class] undoableKeyPaths]) | |
[translation removeObserver:self forKeyPath:prop]; | |
} | |
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context | |
{ | |
NSKeyValueChange kind = [[change objectForKey:NSKeyValueChangeKindKey] intValue]; | |
NSIndexSet *changedIndices = [change objectForKey:NSKeyValueChangeIndexesKey]; | |
NSUInteger anIndex = [changedIndices firstIndex]; // wrong but good enough? | |
id old = [change objectForKey:NSKeyValueChangeOldKey]; | |
id new = [change objectForKey:NSKeyValueChangeNewKey]; | |
if([keyPath isEqual:@"children"]) { | |
if(kind == NSKeyValueChangeRemoval || kind == NSKeyValueChangeReplacement || kind == NSKeyValueChangeSetting) | |
for (SPLangNode *node in old) { | |
[self unwatchNodeWithSubtree:node]; | |
[[mgr prepareWithInvocationTarget:object] insertObject:node inChildrenAtIndex:anIndex]; | |
} | |
if(kind == NSKeyValueChangeInsertion || kind == NSKeyValueChangeReplacement || kind == NSKeyValueChangeSetting) | |
for (SPLangNode *node in new) { | |
[self watchNodeWithSubtree:node]; | |
[[mgr prepareWithInvocationTarget:object] removeObjectFromChildrenAtIndex:[[object valueForKey:@"children"] indexOfObject:node]]; | |
} | |
} else if([keyPath isEqual:@"translations"]) { | |
if(kind == NSKeyValueChangeRemoval || kind == NSKeyValueChangeReplacement || kind == NSKeyValueChangeSetting) | |
for (SPTranslation *trans in old) { | |
[self unwatchTranslation:trans]; | |
[[mgr prepareWithInvocationTarget:object] insertObject:trans inTranslationsAtIndex:anIndex]; | |
} | |
if(kind == NSKeyValueChangeInsertion || kind == NSKeyValueChangeReplacement || kind == NSKeyValueChangeSetting) | |
for (SPTranslation *trans in new) { | |
[self watchTranslation:trans]; | |
[[mgr prepareWithInvocationTarget:[object mutableArrayValueForKey:@"translations"]] removeObject:trans]; | |
} | |
} else { | |
[[mgr prepareWithInvocationTarget:object] setValue:old forKeyPath:keyPath]; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment