On 06/12/2009, at 8:12 PM, Uli Kusterer wrote:

> I thought that was what undo groups were for? Open a group at the start of 
> the drag manually, close it at the end, and everything in between gets lumped 
> into one big action at the end. It may get "replayed" internally, but will 
> all be triggered by one Cmd-Z.


Indeed, undo groups are great. Unfortunately, NSUndoManager has a bug where if 
you open a group (on mouse down, say), do nothing (no drag), and close it again 
(on mouse up), an empty Undo task appears in the Undo menu. It's harmless, in 
that it does nothing, but it's also a nuisance, since the user doesn't expect 
this and reports it as a bug with your app. It's working around this bug that 
is horrible and surprisingly complicated (for two reasons - one, you can't peek 
at the top of the undo stack to see what's there and two, even if you could 
there's no way to tell whether the task there is empty, because the 'tasks' are 
all private classes. Therefore you have to come up with another way either to 
detect this case, or to prevent it from happening. Either way, it's a 
complicated and nasty hack). Incidentally I have reported this bug but it came 
back as a dupe. It's been there since I started with Cocoa, on 10.2.

In addition, you don't really want a group to record every intermediate step of 
a drag - theoretically that could run to any number of tasks, which on Undo 
would be 'replayed', so the drag would eventually get undone but who cares 
about all the in-between steps? You just want the object to return to the 
position at the start of the drag. This is what 'task coalescing' achieves, 
along with the potentially huge memory saving of not recording the irrelevant 
in-between steps. Unfortunately NSUndoManager doesn't support task coalescing, 
so you have to subclass it to add this. It's not a huge deal but the bogus task 
problem is.

I've now written my own undo manager from scratch. It's much more 
straightforward than NSUndoManager in that it uses Cocoa collection classes 
internally - I'm guessing that one reason for NSUndoManager's arcane 
implementation with all its weird group end and start markers and so on is so 
that it also works with Core Foundation alone. It turns out my approach is 
coincidentally very near identical to GNUStep's implementation. I was concerned 
that because it subclasses NSObject, not NSUndoManager, it would cause trouble 
when passed to NSDocument's -setUndoManager: method, but so far I can report 
that it works perfectly with no issues, supports coalescing and doesn't exhibit 
the empty group bug. It has an identical public API to NSUndoManager so my main 
concern was whether internal parts of Cocoa were using private API but that 
does not appear to be the case. I followed the documentation with respect to 
when the various notifications are sent, and that didn't quite keep 
NSDocument's dirty state properly in synch, so I did what was necessary to keep 
it happy and so now it's slightly not as documented - but I suspect the issue 
there is that the docs are subtly incorrect. There's also a suspicion among 
users who contacted me off-list that Core Data is doing something with private 
Undo API, so mine may not support a Core Data app, but for now that doesn't 
concern me.

I've still got some testing to do to really prove it's safe to use, but so far 
I'm much happier with it than NSUndoManager (and if things do go wrong I can at 
least debug it directly instead of having to guess what's going on inside the 
black box and relying on the inaccurate documentation). I'll put it out on my 
website when I'm done - not seeing those useless 'undo manager is in an invalid 
state' logs is liberating, I can tell you.

--Graham


_______________________________________________

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