Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make NSUndoManager's undo/redo action names work properly?

I'm learning Cocoa, and I've gotten undo to work without much trouble. But the setActionName: method is puzzling me. Here's a simple example: a toy app whose windows contain a single text label and two buttons. Press the On button and the label reads 'On'. Press the Off button and the label changes to read 'Off'. Here are the two relevant methods (the only code I wrote for the app):

-(IBAction) turnOnLabel:(id)sender
{
    [[self undoManager] registerUndoWithTarget:self selector:@selector(turnOffLabel:) object:self];
    [[self undoManager] setActionName:@"Turn On Label"];
    [theLabel setStringValue:@"On"];
}

-(IBAction) turnOffLabel:(id)sender
{
    [[self undoManager] registerUndoWithTarget:self selector:@selector(turnOnLabel:) object:self];
    [[self undoManager] setActionName:@"Turn Off Label"];
    [theLabel setStringValue:@"Off"];
}

Here's what I expect:

  • I click the On button
  • The label changes to say 'On'
  • In the Edit menu is the item 'Undo Turn On Label'
  • I click that menu item
  • The label changes to say 'Off'
  • In the Edit menu is the item 'Redo Turn On Label'

In fact, all these things work as I expect apart from the last one. The item in the Edit menu reads 'Redo Turn Off Label', not 'Redo Turn On Label'. (When I click that menu item, the label does turn to On, as I'd expect, but this makes the menu item's name even more of a mystery.)

What am i misunderstanding, and how can I get these menu items to display the way I want them to?

like image 787
Gabriel Roth Avatar asked Feb 23 '11 03:02

Gabriel Roth


1 Answers

Remember: when you are redoing, your code must set an actionName for the Undo menu item.

When you are undoing or redoing, the actionName in the Redo menu item is set automatically.

setActionName: changes the Undo menu item only. The Redo menu item actionName is automated.

When you initially setActionName: when ![[self undoManager] isUndoing], this actionName goes to the Undo menu item. When then you choose to Undo ([[self undoManager] isUndoing] == YES, no actionNames are set by you) the undoManager automatically sets this actionName to the Redo menu item and the previous undo actionName to the Undo menu item. When you then choose to Redo, you still have to pass an actionName to go to the Undo menu item.

In other words: you have to set actionNames only when your code is not Undoing (but you must set when initially invoked or is redoing).

like image 168
Vassilis Avatar answered Oct 20 '22 01:10

Vassilis