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 (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to