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 ([email protected])
>
> 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 [email protected]
_______________________________________________
Cocoa-dev mailing list ([email protected])
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 [email protected]