• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Undo menu strings
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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


References: 
 >Observing with GC (From: Quincey Morris <email@hidden>)
 >Re: Observing with GC (From: Rob Keniger <email@hidden>)
 >Re: Observing with GC (From: glenn andreas <email@hidden>)
 >Re: Observing with GC (From: Rob Keniger <email@hidden>)
 >Re: Observing with GC (From: Bill Bumgarner <email@hidden>)
 >Re: Observing with GC (From: Quincey Morris <email@hidden>)
 >Re: Observing with GC (From: "Sean McBride" <email@hidden>)
 >Re: Observing with GC (From: Graham <email@hidden>)
 >Re: Observing with GC (From: Quincey Morris <email@hidden>)

  • Prev by Date: Re: Observing with GC
  • Next by Date: Re: Event Logging
  • Previous by thread: Re: Observing with GC
  • Next by thread: Re: Observing with GC
  • Index(es):
    • Date
    • Thread