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