Undo menu strings
Undo menu strings
- Subject: Undo menu strings
- From: Quincey Morris <email@hidden>
- Date: Tue, 26 Feb 2008 17:35:31 -0800
On Feb 26, 2008, at 16:06, Brian Williams wrote:
OK Quincey I'll bite, how did you do it?
I am interested in how to more efficiently integrate the undo menu
stuff into my CD project.
Well, you asked for it ...
Basically I co-opted KVC's keyPath mechanism.
The easiest case is setting a single property (for example, a text
field bound to a NSObjectController or File's Owner with keyPath
"firstName", though it works for in-place edits of table view columns
bound to a NSArrayController too). In IB, I just write the keyPath in
the binding as "ui.firstName" instead.
To detect this, I override [NSObject setValue:forKeyPath:].
(Regrettably, I was forced to subclass NSObject instead of adding a
category, since all that KVC/KVO stuff is already added via
categories.) The override decodes the keyPath component by component,
stripping out any "ui" component, and resolves the target component by
component too, until it can eventually call the standard [NSObject
setValue:forKey:]. After seeing a "ui" component, it also starts
building an "undo string" with an @-sign, the target class name and
the rest of the components ("@Employee.firstName" for example). When
the real setValue is called, that undo string is also shipped off to
an undo controller.
For standard controls and views, there can only be one of these undo
strings per run-loop-bracketed undo group. But you might also way to
use the same mechanism from action methods, and they can be coded to
change multiple "ui.something" properties. (To-many property array
deletions can be multiple changes too, but I'll get to that a bit
later.) So the undo controller combines any strings it receives with
the string it already has. For example, if it gets
"@Employee.firstName" more than once, it changes its string to
"@Employee.firstName.+s" to mark the plural. Or if it gets
"@Employee.firstName" and "@Employee.lastName", it changes its string
to "@Employee.+s" to mark multiple attributes.
The undo controller registers to receive NSUndoManager's
NSUndoManagerWillCloseUndoGroupNotification. When it receives that
notification for the top level group, it uses a .strings table to
"localize" its weird undo string into things like "Employee First
Name" (from "@Employee.firstName") or into "Employee First
Names" (from "@Employee.firstName.+s") or into "Employee
Changes" (from "@Employee.+s"), etc, and sends it to the
NSUndoManager. Then it clears its own string for the next run loop
iteration.
For insertions and deletions in a to-many (array) property (typically
but not necessarily arriving via a NSArrayController), I had to
override [NSObject mutableArrayValueForKeyPath:keyPath]. The override
resolves the keyPath like the setValue:forKeyPath override, but of
course it can't use setValue:forKey. Instead, it creates and returns a
NSArray subclass that holds and monitors the standard proxy object
that mutableArrayValueForKeyPath:keyPath normally returns. When it
sees an insertion, it sends a string like "@Employee.@a" to the undo
controller. Deletions produce strings like "@Employee.@r". These
string are subject combining like I already described, eventually
leading to localized strings like "Delete Employee" (from
"@Employee.@r") or "Delete Employees" (from "@Employee.@r.+s").
To set this up in IB, the model keyPath of the NSArrayController's
binding to File's Owner needs to have a "ui" in it at a suitable place.
The final little piece is to add a -(id) ui {return self;} property to
the NSObject subclass, so that "ui" components in key paths are
ignored in all the other keyPath methods that are not overridden.
All of this is just to get the undo menu string. The actual undo data
itself is handled entirely separately.
That's about it. It isn't trivial, although it's not a lot of code.
I have a theory that this methodology can be adapted to core data
objects, too, implemented as a category on NSManagedObject plus an
override of mutableSetValueForKeyPath:keyPath. But I'm not sure yet if
this is feasible.
_______________________________________________
Cocoa-dev mailing list (email@hidden)
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:
This email sent to email@hidden