Search code examples
objective-cmacoscocoansundomanager

NSUndoManager action name broken for grouped action after undo-redo cycle


I have the following, rather simple code for adding, removing and joining MP3 audio regions in the MP3 editing app I am currently writing:

- (void)addRegions:(NSArray *)regions
{
    [[self.undoManager prepareWithInvocationTarget:self] removeRegions:regions];
    
    // ... <code to actually add the regions> ...
    
    if (![self.undoManager isUndoing]) {
        [self.undoManager setActionName:@"Add Region(s)"];
    }
}


- (void)removeRegions:(NSArray *)regions
{
    [[self.undoManager prepareWithInvocationTarget:self] addRegions:regions];
    
    // ... <code to actually remove the regions> ...
    
    if (![self.undoManager isUndoing]) {
        [self.undoManager setActionName:@"Remove Region(s)"];
    }
}


- (void)joinRegions:(NSArray *)regions
{
    [self.undoManager beginUndoGrouping];
    
    MP3Region *joinedRegion = [[MP3Region alloc] init];
    
    // ... <code to set the parameters of the joined region accordingly> ...
    
    [self removeRegions:regions];
    [self addRegions:@[joinedRegion]];
    
    [self.undoManager setActionName:@"Join Regions"];
    
    [self.undoManager endUndoGrouping];
}

Undo and redo for adding and removing regions is working just fine. Now what's weird is that undo and redo work fine for the join regions functionality as well, but after undoing and redoing once, the menu item now wrongly reads "Undo Add Region(s)" instead of the expected "Undo Join Regions". Clicking this wrongly labeled menu item actually undoes the join action, and clicking the subsequently appearing "Redo Add Region(s)" menu item repeats the join action again. So the undo/redo functionality actually does the thing it was supposed to. It's just the action name that somehow gets messed up.

I tried guarding [self.undoManager setActionName:@"Join Regions"]; with if (![self.undoManager isUndoing]) {}, but this doesn't change anything at all. (And I didn't see why it should have in the first place, anyway, but that was my only idea as to what might be wrong.)

Any ideas on what's causing this issue and how to fix it?


Solution

  • Change addRegions: to the following:

    - (void)addRegions:(NSArray *)regions
    {
        [[self.undoManager prepareWithInvocationTarget:self] removeRegions:regions];
        
        // ... <code to actually add the regions> ...
        
        if (![self.undoManager isUndoing] && ![self.undoManager isRedoing]) {
            [self.undoManager setActionName:@"Add Region(s)"];
        }
    }
    

    (I.e. add check to avoid setting the action name when the undo manager is redoing as well.)