Thanks, Jerry. I will have to try this out (if I can get up the nerve to fiddle 
with my currently working undo groups.)

Dave


On 2011-01-12, at 12:47 AM, Jerry Krinock wrote:

> 
> On 2011 Jan 11, at 17:54, Dave Fernandes wrote:
> 
>> And I'd love to know a trick to bring the correct tab to the front when 
>> undoing/redoing changes in a tabbed interface.
> 
> Well, the way I've seen it done in Apple Sample Code is to avoid a tabbed 
> interface, do editing of objects in sheets, and the sheets have their own 
> managed object contexts and own undo managers.  But I don't like that because 
> the undo chain often appears to be broken from the user's viewpoint, objects 
> must be copied between mocs when editing ends, and as far as multiple undo 
> managers, well, I say "Thank you", but one undo manager is more than enough 
> for me to handle on a good day.
> 
> So, I've done the switching of tabs as you suggest, and am fairly pleased 
> with the results, except that I'm sure I spent much more time on Undo than is 
> worth it to my users.
> 
> First of all, you make the simplifying assumption that the "correct" tab view 
> item is the one that is being displayed at the time that the Undo Group was 
> ended.  I find this to be correct about 90% of of the time.  Then, you 
> register an undo action to select this tab view item in your current undo 
> group.
> 
> It sounds simple, but when you try it, all kinds of wacky things happen.  You 
> also need to consider Redo.  The plot thickens.
> 
> I do it via a notification with two observers:
> 
> [[NSNotificationCenter defaultCenter] addObserver:self
>                  selector:@selector(registerTabViewForUndo:)
>                      name:SSYUndoManagerWillEndUndoGroupNotification
>                    object:[self undoManager]] ;
> [[NSNotificationCenter defaultCenter] addObserver:self
>                  selector:@selector(registerTabViewForUndo:)
>                      name:NSUndoManagerCheckpointNotification
>                    object:[self undoManager]] ;
> 
> SSYUndoManagerWillEndUndoGroupNotification is, as the name implies, a 
> notification that I post when I end an undo grouping, usually a super-group 
> that includes a half dozen or so Core Data undo groupings.  (But that's 
> another topic.)
> 
> Now here's the method that it triggers.  The comments are the most 
> interesting part.
> 
> - (void)registerTabViewForUndo:(NSNotification*)note {
>    // As always, I use [[self managedObjectContext] hasChanges]
>    // instead of [self isDocumentEdited] because the former
>    // often returns YES when the latter is NO, and this
>    // seems to be the true answer.
>    if (![[self managedObjectContext] hasChanges]) {
>        return ;
>    }
> 
>    // Some tab view items do not have any controls which
>    // affect the data model.
>    if (![[self bkmslfWinCon] activeTabViewIsUndoable]) {
>        return ;
>    }
> 
>    // Determine whether or not to register a change to the current tab view 
> in the current
>    // undo group.  The code with follows looks weird and unsymmetrical, but 
> it implements
>    // (efficiently) what has been determined by careful experimentation to be 
> the
>    // following Magic Algorithm:
>    // doRegister if any one of:
>    //   1.  SSYUndoManagerWillEndUndoGroupNotification && !isUndoing
>    //       (Register during normal "do" changes by the user, to be used 
> during later Undo.)
>    //   2.  NSUndoManagerCheckpointNotification && isUndoing
>    //       (Register during undo operations, to be used during later Redo.)
>    //   3.  NSUndoManagerCheckpointNotification && isRedoing
>    //       (Register during redo operations, to be used during later Undo.)
>    // I tried combining cases 1 and 2 above, by observing 
> NSUndoManagerCheckpointNotification
>    // without regard to isUndoing, but this caused a proliferation of undo 
> groups, needing to
>    // click Undo several times before the desired action was undone.
>    // I also tried to use NSUndoManagerWillCloseUndoGroupNotification instead 
> of
>    // SSYUndoManagerWillEndUndoGroupNotification, but this caused a "bad 
> group state -
>    // attempt to close a nested group with no group open" to be logged from 
> GCUndoManager
>    // immediately upon doing an action.
>    NSString* name = [note name] ;
>    BOOL doRegister = NO ;    
>    if ([name isEqualToString:NSUndoManagerCheckpointNotification]) {
>        doRegister = [[self undoManager] isUndoing] || [[self undoManager] 
> isRedoing] ;
>    }
>    else if ([name 
> isEqualToString:SSYUndoManagerWillEndUndoGroupNotification]) {
>        doRegister = ![[self undoManager] isUndoing] ;
>    }
> 
>    if (doRegister) {
>        // Register revealing the tab view item in the current undo group
>        NSString* currentTabViewIdentifier = [[self bkmslfWinCon] 
> activeTabViewItemIdentifier] ;
>        [[self undoManager] registerUndoWithTarget:self
>                                          
> selector:@selector(revealTabViewIdentifier:)
>                                            object:currentTabViewIdentifier] ; 
>        
>    }
> }
> 
> Of course, bkmslfWinCon is the document's window controller, and the 
> -activeTabViewItemIdentifier and -revealTabViewIdentifier: methods are as 
> their names imply.  I use these wrappers since I actually have multiple 
> levels of tabs.
> 
> _______________________________________________
> 
> 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:
> http://lists.apple.com/mailman/options/cocoa-dev/dave.fernandes%40utoronto.ca
> 
> This email sent to dave.fernan...@utoronto.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:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

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

Reply via email to