Making undo work with complicated NSTextView subclass

  • I have been trying to make this work for weeks now without avail, and I now
    turn to the world at large hoping someone has dealt with the same issues.

    I'm working on a core data document-based application. For it I have created
    a very complicated NSTextView subclass (about 700 lines of code), which
    allows for proper outlining (i.e., nested lists). The most complicated part
    of this subclass is the keyDown method, which changes the NSTextStore
    primarily by changing the selection and calling [super keyDown], setting
    attributes using setAttributes:range: on the text store, using several
    string changing methods on the NSTextStore's mutableString method and in a
    few cases setting a new NSAttributedString using the  text store's
    setAttributedString method.

    All of these modifications muck with the text view's undo system, and I've
    been unable to make it act nicely with them, with the result that some undos
    throw errors (generally -[NSBigMutableString characterAtIndex:]: Range or
    index out of bounds) while others proceed but restore the text completely
    wrong. The apple documentation on subclassing
    NSTextView<http://developer.apple.com/documentation/Cocoa/Conceptual/TextEditing/Tasks
    /Subclassing.html
    >says:

    > In actually making changes to the text, you must ensure that the changes
    > are properly performed and recorded by different parts of the text system.
    > You do this by bracketing each batch of potential changes with
    > shouldChangeTextInRange:replacementString: and didChangeText messages.
    > These methods ensure that the appropriate delegate messages are sent and
    > notifications posted. The first method asks the delegate for permission to
    > begin editing with a textShouldBeginEditing: message. If the delegate
    > returns NO<http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Ref
    erence/reference.html#//apple_ref/doc/c_ref/NO
    >
    > , shouldChangeTextInRange:replacementString: in turn returns NO<http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Ref
    erence/reference.html#//apple_ref/doc/c_ref/NO
    >,
    > in which case your subclass should disallow the change. If the delegate
    > returns YES<http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Ref
    erence/reference.html#//apple_ref/doc/c_ref/YES
    >,
    > the text view posts an NSTextDidBeginEditingNotification, and
    > shouldChangeTextInRange:replacementString: in turn returns YES<http://developer.apple.com/documentation/Cocoa/Reference/ObjCRuntimeRef/Ref
    erence/reference.html#//apple_ref/doc/c_ref/YES
    >.
    > In this case you can make your changes to the text, and follow up by
    > invoking didChangeText. This method concludes the changes by posting an
    > NSTextDidChangeNotification, which results in the delegate receiving a
    > textDidChange: message.

    however calling these methods doesn't seem to fix my issues.

    My basic question is this: is there a way I can make NSTextView's built-in
    undo manager work for me, or am I doomed to the unpleasant task of handling
    it manually?

    Thanks for any help.

    --
    Micah Wylde
  • >
    > I'm working on a core data document-based application. For it I
    > have created
    > a very complicated NSTextView subclass (about 700 lines of code),
    > which
    > allows for proper outlining (i.e., nested lists). The most
    > complicated part
    > of this subclass is the keyDown method, which changes the NSTextStore
    > primarily by changing the selection and calling [super keyDown],
    > setting
    > attributes using setAttributes:range: on the text store, using several
    > string changing methods on the NSTextStore's mutableString method
    > and in a
    > few cases setting a new NSAttributedString using the  text store's
    > setAttributedString method.
    >
    > All of these modifications muck with the text view's undo system,
    > and I've
    > been unable to make it act nicely with them, with the result that
    > some undos
    > throw errors (generally -[NSBigMutableString characterAtIndex:]:
    > Range or
    > index out of bounds) while others proceed but restore the text
    > completely
    > wrong. The apple documentation on subclassing
    > NSTextView<http://developer.apple.com/documentation/Cocoa/
    > Conceptual/TextEditing/Tasks/Subclassing.html>says:
    > however calling these methods doesn't seem to fix my issues.
    >
    > My basic question is this: is there a way I can make NSTextView's
    > built-in
    > undo manager work for me, or am I doomed to the unpleasant task of
    > handling
    > it manually?
    >
    > Thanks for any help.
    >
    > --
    > Micah Wylde
    You need to take care  of undo and redoing of the additional behavior
    that you induce
    into the text view.  You may have to consider grouping of undo
    operations as well. For instance
    as you  mentioned you are doing lots of things in the subclass and
    later also happen to call on super class.
    Now that the Undo manger asssociated with the view will have only the
    invocations of the super class and not at all your
    subclass and thats the exact reason why it cant restore the states
    properly upon undoing.

    Now what I think would solve this issue is by grouping together your
    custom invocations with that of the super class. Thus in the
    case of say  'keydown' I would do something like this. Please note
    that this is just a pseudo  code and I am just passing some ideas.

    ===
    -(void)keyDown:(NSEvent*)event
    {
    [undoMgr beginUndoGrouping];

    //register here my undo invocation which would undo the operations I
    do in this method.

    //Do whatever is appropriate for this subclass here -any custom
    behavior

    //call on the super if needed.

    [undoMgr endUndoGrouping]

    }
    ===

    Regards
    Shripada
previous month january 2008 next month
MTWTFSS
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
Go to today