I believe NSUndoManager arguably works a bit incorrectly in this regard - I’ve 
just run into the same issue on one project.

When you register an Undo action, you set the action name. On an Undo or a 
Redo, because you’re invoking the ‘inverse’ operation which may also be a 
normal operation, it could set the action name to something else. I believe 
NSUndoManager should ignore that setting of the action name if it is currently 
in the middle of undoing or redoing tasks - instead it should copy the action 
name to the opposite stack so that the name remains as it was when it was first 
registered. However, it doesn’t - it accepts the new name so you get the weird 
naming that you are seeing.

It’s possible this has changed at some point because I don’t recall it behaving 
this way in the distant past.

On my main app project I use my own rewrite of NSUndoManager (GCUndoManager) 
which copies over the name on an undo or redo, which is why I only just noticed 
this behaviour in another project where I’m using NSUndoManager. In my case 
I’ll probably just adopt my own class, since I’m comfortable with its behaviour 
even when it deviates from the standard NSUndoManager.

In your case you have a couple of options. One is to test whether the method is 
being invoked by Undo or Redo by querying the undo manager at that point. If 
it’s undoing or redoing, you can avoid setting the action name, or can set it 
by copying the current one. If it’s not undoing or redoing, set the name as 
usual.

A better way to deal with it is to be more strict with your division of classes 
according to MVC. The registering of undo tasks themselves logically belong in 
the model - that’s the state that will need to be undone or redone. But setting 
the action name really belongs in the View (or Controller) - it’s something 
visible to the user that labels the undo or redo stack. So setting the action 
name in the model is not strictly correct. To reconcile this, usually there’s a 
high-level method (like an action method) that responds to the user’s input 
(e.g. a menu or button) which in turn calls methods on the model. The action 
name should be set in the action method only, and the model which implements 
the state shouldn’t do this. This will fix your problem. It’s possible that 
NSUndoManager’s behaviour has been implemented assuming this strict separation 
of View/Controller and Model.

As a bonus, one way to avoid hard-coding the Undo action names is to take them 
from the title of the sender of the intial action method. Unfortunately not all 
senders have a -title property, but buttons and menus do.

So putting this altogether into a contrived pseudo-example:

- (IBAction)    myAction:(id) sender
{
        [myModel toggleState];
        [self.undoManager setActionName:sender.title];
}

…

- (void)                toggleState
{
        self.state = !self.state;
}


- (void)                setState:(BOOL) state
{
        [[self.undoManager prepareWithInvocationTarget:self] setState:_state];
        _state = state;
}


—Graham





> On 21 Dec 2016, at 7:06 PM, John Brownie <john_brow...@sil.org> wrote:
> 
> I have undo working correctly in my macOS app, but I have a question about 
> action names.
> 
> The documentation tells you to set the name when registering the action on 
> the undo stack. The correct name appears in the Undo menu item. The trouble 
> is when you have symmetric actions, such as add and delete an item. You do an 
> add, which puts the name "add" for the action. Undoing it is a remove, which 
> sets the name "remove". Then you have "Redo remove" as the menu item, which 
> isn't correct.
> 
> It seems that the way to get around this is to set the action name only when 
> you are actually doing the original action, and not when doing the undo 
> version of it. Is that a correct interpretation? If so, how do you implement 
> it? Do you test the NSUndoManager's undoing property and only set the name if 
> it is NO?
> 
> Any insights welcomed!
> John
> -- 
> John Brownie
> In Finland on furlough from SIL Papua New Guinea


_______________________________________________

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