Re: Implementing undo in custom model object for lots of properties
Re: Implementing undo in custom model object for lots of properties
- Subject: Re: Implementing undo in custom model object for lots of properties
- From: Ben Kennedy <email@hidden>
- Date: Mon, 07 May 2012 20:17:32 -0700
On 07 May 2012, at 4:45 pm, Graham Cox wrote:
> Another way to 'centralise' undo is to have an object listen for the KVO notifications of changes for properties it's interested in, and use the observation method to record to the undo manager. You'd still use setValue:forKey: at undo time, so it amounts to a very similar idea. I've done it that way and it works.
Well, that would require my controller to -addObserver:... on each of the collected objects, which I had earlier decided to avoid for various reasons. But that's a good technique for me to keep in mind, and is beginning to sound like possibly a better approach.
> So your problem must be something else. Are you sure undoManager returns something?
You're right, I deked myself out; I was trying to examine changes to a property that was changed via the setter of a different property, but setValue:forKey: was not being called for the former. I wasn't looking closely enough at the call stack or the forKey: parameter when I saw the seemingly do-nothing undoManager line dutifully execute.
But, that's sort of moot because as you point out...:
> Also, more obvious now I come to think about it, is that setValue:forKey: is not called for every property when it is set! That method can be used to call down to the setter for a named property, but if the setter is invoked directly (the more usual case), then setValue:forKey: isn't invoked. You might want to check whether that is what's happening. In which case making your undo handler a KVO observer will get around that problem, or you could override -willChangeValueForKey: instead, which is the method that causes some of the KVO "magic" to happen.
Much better idea, thanks. Moving my undo registration shim into -willChangeValueForKey: solves the problem nicely:
- (void)willChangeValueForKey:(NSString *)key
{
// Register the inverse action if we are about to change an undoable property.
if ([[FAEditorNote undoableKeys] containsObject:key])
[[undoManager prepareWithInvocationTarget:self] setValue:[self valueForKey:key] forKey:key];
[super willChangeValueForKey:key];
return;
}
Which allows me to turf this gnarly macro I briefly purpose-built in the interim:
#define DEFINE_UNDOABLE_COPYING_SETTER(SETTER,PROPERTY) \
- (void)SETTER(id)newValue \
{ \
if (newValue != PROPERTY) \
{ \
[undoManager registerUndoWithTarget:self selector:@selector(SETTER) object:PROPERTY]; \
[PROPERTY release]; \
PROPERTY = [newValue copy]; \
} \
}
DEFINE_UNDOABLE_COPYING_SETTER(setPgmInTC:, pgmInTC);
DEFINE_UNDOABLE_COPYING_SETTER(setPgmOutTC:, pgmOutTC);
//etc.
thanks!
b
--
Ben Kennedy, chief magician
Zygoat Creative Technical Services
http://www.zygoat.ca
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden