API 0.9.5
Foundation/CPUndoManager.j
Go to the documentation of this file.
00001 /*
00002  * CPUndoManager.j
00003  * Foundation
00004  *
00005  * Created by Francisco Tolmasky.
00006  * Copyright 2008, 280 North, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 
00024 var CPUndoManagerNormal     = 0,
00025     CPUndoManagerUndoing    = 1,
00026     CPUndoManagerRedoing    = 2;
00027 
00028 CPUndoManagerCheckpointNotification         = @"CPUndoManagerCheckpointNotification";
00029 CPUndoManagerDidOpenUndoGroupNotification   = @"CPUndoManagerDidOpenUndoGroupNotification";
00030 CPUndoManagerDidRedoChangeNotification      = @"CPUndoManagerDidRedoChangeNotification";
00031 CPUndoManagerDidUndoChangeNotification      = @"CPUndoManagerDidUndoChangeNotification";
00032 CPUndoManagerWillCloseUndoGroupNotification = @"CPUndoManagerWillCloseUndoGroupNotification";
00033 CPUndoManagerWillRedoChangeNotification     = @"CPUndoManagerWillRedoChangeNotification";
00034 CPUndoManagerWillUndoChangeNotification     = @"CPUndoManagerWillUndoChangeNotification";
00035 
00036 CPUndoCloseGroupingRunLoopOrdering          = 350000;
00037 
00038 var _CPUndoGroupingPool         = [],
00039     _CPUndoGroupingPoolCapacity = 5;
00040 
00041 /* @ignore */
00042 @implementation _CPUndoGrouping : CPObject
00043 {
00044     _CPUndoGrouping _parent;
00045     CPMutableArray  _invocations;
00046     CPString        _actionName;
00047 }
00048 
00049 + (void)_poolUndoGrouping:(_CPUndoGrouping)anUndoGrouping
00050 {
00051     if (!anUndoGrouping || _CPUndoGroupingPool.length >= _CPUndoGroupingPoolCapacity)
00052         return;
00053 
00054     _CPUndoGroupingPool.push(anUndoGrouping);
00055 }
00056 
00057 + (id)undoGroupingWithParent:(_CPUndoGrouping)anUndoGrouping
00058 {
00059     if (_CPUndoGroupingPool.length)
00060     {
00061         var grouping = _CPUndoGroupingPool.pop();
00062 
00063         grouping._parent = anUndoGrouping;
00064 
00065         if (grouping._invocations.length)
00066             grouping._invocations = [];
00067 
00068         return grouping;
00069     }
00070 
00071     return [[self alloc] initWithParent:anUndoGrouping];
00072 }
00073 
00074 - (id)initWithParent:(_CPUndoGrouping)anUndoGrouping
00075 {
00076     self = [super init];
00077 
00078     if (self)
00079     {
00080         _parent = anUndoGrouping;
00081         _invocations = [];
00082         _actionName = @"";
00083     }
00084 
00085     return self;
00086 }
00087 
00088 - (_CPUndoGrouping)parent
00089 {
00090     return _parent;
00091 }
00092 
00093 - (void)addInvocation:(CPInvocation)anInvocation
00094 {
00095     _invocations.push(anInvocation);
00096 }
00097 
00098 - (void)addInvocationsFromArray:(CPArray)invocations
00099 {
00100     [_invocations addObjectsFromArray:invocations];
00101 }
00102 
00103 - (BOOL)removeInvocationsWithTarget:(id)aTarget
00104 {
00105     var index = _invocations.length;
00106 
00107     while (index--)
00108         if ([_invocations[index] target] == aTarget)
00109             _invocations.splice(index, 1);
00110 }
00111 
00112 - (CPArray)invocations
00113 {
00114     return _invocations;
00115 }
00116 
00117 - (void)invoke
00118 {
00119     var index = _invocations.length;
00120 
00121     while (index--)
00122         [_invocations[index] invoke];
00123 }
00124 
00125 - (void)setActionName:(CPString)aName
00126 {
00127     _actionName = aName;
00128 }
00129 
00130 - (CPString)actionName
00131 {
00132     return _actionName;
00133 }
00134 
00135 @end
00136 
00137 var _CPUndoGroupingParentKey        = @"_CPUndoGroupingParentKey",
00138     _CPUndoGroupingInvocationsKey   = @"_CPUndoGroupingInvocationsKey",
00139     _CPUndoGroupingActionNameKey    = @"_CPUndoGroupingActionNameKey";
00140 
00141 @implementation _CPUndoGrouping (CPCoder)
00142 
00143 - (id)initWithCoder:(CPCoder)aCoder
00144 {
00145     self = [super init];
00146 
00147     if (self)
00148     {
00149         _parent = [aCoder decodeObjectForKey:_CPUndoGroupingParentKey];
00150         _invocations = [aCoder decodeObjectForKey:_CPUndoGroupingInvocationsKey];
00151         _actionName = [aCoder decodeObjectForKey:_CPUndoGroupingActionNameKey];
00152     }
00153 
00154     return self;
00155 }
00156 
00157 - (void)encodeWithCoder:(CPCoder)aCoder
00158 {
00159     [aCoder encodeObject:_parent forKey:_CPUndoGroupingParentKey];
00160     [aCoder encodeObject:_invocations forKey:_CPUndoGroupingInvocationsKey];
00161     [aCoder encodeObject:_actionName forKey:_CPUndoGroupingActionNameKey];
00162 }
00163 
00164 @end
00165 
00181 @implementation CPUndoManager : CPObject
00182 {
00183     CPMutableArray  _redoStack;
00184     CPMutableArray  _undoStack;
00185 
00186     BOOL            _groupsByEvent;
00187     int             _disableCount;
00188     int             _levelsOfUndo;
00189     id              _currentGrouping;
00190     int             _state;
00191 
00192     id              _preparedTarget;
00193     id              _undoManagerProxy;
00194 
00195     CPArray         _runLoopModes;
00196     BOOL            _registeredWithRunLoop;
00197 }
00198 
00203 - (id)init
00204 {
00205     self = [super init];
00206 
00207     if (self)
00208     {
00209         _redoStack = [];
00210         _undoStack = [];
00211 
00212         _state = CPUndoManagerNormal;
00213 
00214         [self setRunLoopModes:[CPDefaultRunLoopMode]];
00215         [self setGroupsByEvent:YES];
00216 
00217         _undoManagerProxy = [_CPUndoManagerProxy alloc];
00218         _undoManagerProxy._undoManager = self;
00219     }
00220 
00221     return self;
00222 }
00223 
00224 - (void)_addUndoInvocation:(CPInvocation)anInvocation
00225 {
00226     if (!_currentGrouping)
00227         // Remember that we create these lazily...
00228         if ([self groupsByEvent])
00229             [self _beginUndoGroupingForEvent];
00230         else
00231             [CPException raise:CPInternalInconsistencyException reason:"No undo group is currently open"];
00232 
00233     [_currentGrouping addInvocation:anInvocation];
00234 
00235     if (_state === CPUndoManagerNormal)
00236         [_redoStack removeAllObjects];
00237 }
00238 
00239 // Registering Undo Operations
00247 - (void)registerUndoWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anObject
00248 {
00249     // Don't do anything if we're disabled.
00250     if (_disableCount > 0)
00251         return;
00252 
00253     //signature = [target methodSignatureForSelector:selector];
00254     // FIXME: we need method signatures.
00255     var invocation = [CPInvocation invocationWithMethodSignature:nil];
00256 
00257     [invocation setTarget:aTarget];
00258     [invocation setSelector:aSelector];
00259     [invocation setArgument:anObject atIndex:2];
00260 
00261     [self _addUndoInvocation:invocation];
00262 }
00268 - (id)prepareWithInvocationTarget:(id)aTarget
00269 {
00270     _preparedTarget = aTarget;
00271 
00272     return _undoManagerProxy;
00273 }
00274 
00275 /*
00276     FIXME This method doesn't seem to do anything right
00277     @ignore
00278 */
00279 - (CPMethodSignature)_methodSignatureOfPreparedTargetForSelector:(SEL)aSelector
00280 {
00281     if ([_preparedTarget respondsToSelector:aSelector])
00282         return 1;
00283 
00284     return nil;//[_preparedTarget methodSignatureForSelector:selector];
00285 }
00286 
00292 - (void)_forwardInvocationToPreparedTarget:(CPInvocation)anInvocation
00293 {
00294     // Don't do anything if we're disabled.
00295     if (_disableCount > 0)
00296         return;
00297 
00298 /*
00299 if (_currentGroup == nil)
00300         [NSException raise:NSInternalInconsistencyException
00301                     format:@"forwardInvocation called without first opening an undo group"];
00302 */
00303     [anInvocation setTarget:_preparedTarget];
00304 
00305     [self _addUndoInvocation:anInvocation];
00306 
00307     _preparedTarget = nil;
00308 }
00309 
00310 // Checking Undo Ability
00314 - (BOOL)canRedo
00315 {
00316     [[CPNotificationCenter defaultCenter]
00317         postNotificationName:CPUndoManagerCheckpointNotification
00318                       object:self];
00319 
00320     return [_redoStack count] > 0;
00321 }
00322 
00326 - (BOOL)canUndo
00327 {
00328     if (_undoStack.length > 0)
00329         return YES;
00330 
00331     return [[_currentGrouping invocations] count] > 0;
00332 }
00333 
00334 // Preform Undo and Redo
00338 - (void)undo
00339 {
00340     if ([self groupingLevel] === 1)
00341         [self endUndoGrouping];
00342 
00343     [self undoNestedGroup];
00344 }
00345 
00349 - (void)undoNestedGroup
00350 {
00351     if ([_undoStack count] <= 0)
00352         return;
00353 
00354     var defaultCenter = [CPNotificationCenter defaultCenter];
00355 
00356     [defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
00357                                  object:self];
00358 
00359     [defaultCenter postNotificationName:CPUndoManagerWillUndoChangeNotification
00360                                  object:self];
00361 
00362     var undoGrouping = _undoStack.pop(),
00363         actionName = [undoGrouping actionName];
00364 
00365     _state = CPUndoManagerUndoing;
00366 
00367     [self _beginUndoGrouping];
00368     [undoGrouping invoke];
00369     [self endUndoGrouping];
00370 
00371     [_CPUndoGrouping _poolUndoGrouping:undoGrouping];
00372 
00373     _state = CPUndoManagerNormal;
00374 
00375     [[_redoStack lastObject] setActionName:actionName];
00376 
00377     [defaultCenter postNotificationName:CPUndoManagerDidUndoChangeNotification
00378                                  object:self];
00379 }
00380 
00384 - (void)redo
00385 {
00386     // Don't do anything if we have no redos.
00387     if ([_redoStack count] <= 0)
00388         return;
00389 
00390 /*    if (_state == NSUndoManagerUndoing)
00391         [NSException raise:NSInternalInconsistencyException
00392                     format:@"redo called while undoing"];
00393 */
00394 
00395     var defaultCenter = [CPNotificationCenter defaultCenter];
00396 
00397     [defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
00398                                  object:self];
00399 
00400     [defaultCenter postNotificationName:CPUndoManagerWillRedoChangeNotification
00401                                  object:self];
00402 
00403     var oldUndoGrouping = _currentGrouping,
00404         undoGrouping = _redoStack.pop(),
00405         actionName = [undoGrouping actionName];
00406 
00407     _currentGrouping = nil;
00408     _state = CPUndoManagerRedoing;
00409 
00410     [self _beginUndoGrouping];
00411     [undoGrouping invoke];
00412     [self endUndoGrouping];
00413 
00414     [_CPUndoGrouping _poolUndoGrouping:undoGrouping];
00415 
00416     _currentGrouping = oldUndoGrouping;
00417     _state = CPUndoManagerNormal;
00418 
00419     [[_undoStack lastObject] setActionName:actionName];
00420     [defaultCenter postNotificationName:CPUndoManagerDidRedoChangeNotification object:self];
00421 }
00422 
00423 // Creating Undo Groups
00427 - (void)beginUndoGrouping
00428 {
00429     // It doesn't matter that the user is creating a group themselves, we are
00430     // pretending to have opened the group at the beginning of the run loop,
00431     // so create an implicit one here.
00432     if (!_currentGrouping && [self groupsByEvent])
00433         [self _beginUndoGroupingForEvent];
00434 
00435     [[CPNotificationCenter defaultCenter]
00436         postNotificationName:CPUndoManagerCheckpointNotification
00437                       object:self];
00438 
00439     [self _beginUndoGrouping];
00440 }
00441 
00442 /* @ignore */
00443 - (void)_beginUndoGroupingForEvent
00444 {
00445     [self _beginUndoGrouping];
00446     [self _registerWithRunLoop];
00447 }
00448 
00449 /* @ignore */
00450 - (void)_beginUndoGrouping
00451 {
00452     _currentGrouping = [_CPUndoGrouping undoGroupingWithParent:_currentGrouping];
00453 }
00454 
00459 - (void)endUndoGrouping
00460 {
00461     if (!_currentGrouping)
00462         [CPException raise:CPInternalInconsistencyException reason:"endUndoGrouping. No undo group is currently open."];
00463 
00464     var defaultCenter = [CPNotificationCenter defaultCenter];
00465 
00466     [defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
00467                                  object:self];
00468 
00469     var parent = [_currentGrouping parent];
00470 
00471     if (!parent && [_currentGrouping invocations].length > 0)
00472     {
00473         [defaultCenter
00474             postNotificationName:CPUndoManagerWillCloseUndoGroupNotification
00475                           object:self];
00476 
00477         // Put this group on the redo stack if we are currently undoing, otherwise
00478         // put it on the undo stack.  That way, "undos" become "redos".
00479         var stack = _state === CPUndoManagerUndoing ? _redoStack : _undoStack;
00480 
00481         stack.push(_currentGrouping);
00482 
00483         if (_levelsOfUndo > 0 && stack.length > _levelsOfUndo)
00484             stack.splice(0, 1);
00485     }
00486 
00487     // Nested Undo Grouping
00488     else
00489     {
00490         [parent addInvocationsFromArray:[_currentGrouping invocations]];
00491 
00492         [_CPUndoGrouping _poolUndoGrouping:_currentGrouping];
00493     }
00494 
00495     _currentGrouping = parent;
00496 }
00497 
00504 - (void)enableUndoRegistration
00505 {
00506     if (_disableCount <= 0)
00507         [CPException raise:CPInternalInconsistencyException
00508                     reason:"enableUndoRegistration. There are no disable messages in effect right now."];
00509 
00510     _disableCount--;
00511 }
00512 
00516 - (BOOL)groupsByEvent
00517 {
00518     return _groupsByEvent;
00519 }
00520 
00525 - (void)setGroupsByEvent:(BOOL)aFlag
00526 {
00527     aFlag = !!aFlag;
00528 
00529     if (_groupsByEvent === aFlag)
00530         return;
00531 
00532     _groupsByEvent = aFlag;
00533 
00534     if (![self groupsByEvent])
00535         [self _unregisterWithRunLoop];
00536 }
00537 
00541 - (unsigned)groupingLevel
00542 {
00543     var grouping = _currentGrouping,
00544         level = _currentGrouping ? 1 : 0;
00545 
00546     while (grouping = [grouping parent])
00547         ++level;
00548 
00549     return level;
00550 }
00551 
00552 // Disabling Undo
00556 - (void)disableUndoRegistration
00557 {
00558     ++_disableCount;
00559 }
00560 
00564 - (BOOL)isUndoRegistrationEnabled
00565 {
00566     return _disableCount == 0;
00567 }
00568 
00569 // Checking Whether Undo or Redo Is Being Performed
00573 - (BOOL)isUndoing
00574 {
00575     return _state === CPUndoManagerUndoing;
00576 }
00577 
00581 - (BOOL)isRedoing
00582 {
00583     return _state === CPUndoManagerRedoing;
00584 }
00585 
00586 // Clearing Undo Operations
00590 - (void)removeAllActions
00591 {
00592     // Close off any groupings.
00593     while (_currentGrouping)
00594         [self endUndoGrouping];
00595 
00596     // Won't need this anymore
00597     [self _unregisterWithRunLoop];
00598 
00599     _state = CPUndoManagerNormal;
00600     _redoStack = [];
00601     _undoStack = [];
00602     _disableCount = 0;
00603 }
00604 
00609 - (void)removeAllActionsWithTarget:(id)aTarget
00610 {
00611     [_currentGrouping removeInvocationsWithTarget:aTarget];
00612 
00613     var index = _redoStack.length;
00614 
00615     while (index--)
00616     {
00617         var grouping = _redoStack[index];
00618 
00619         [grouping removeInvocationsWithTarget:aTarget];
00620 
00621         if (![grouping invocations].length)
00622             _redoStack.splice(index, 1);
00623     }
00624 
00625     index = _undoStack.length;
00626 
00627     while (index--)
00628     {
00629         var grouping = _undoStack[index];
00630 
00631         [grouping removeInvocationsWithTarget:aTarget];
00632 
00633         if (![grouping invocations].length)
00634             _undoStack.splice(index, 1);
00635     }
00636 }
00637 
00638 // Managing the Action Name
00644 - (void)setActionName:(CPString)anActionName
00645 {
00646     if (anActionName !== nil && _currentGrouping)
00647         [_currentGrouping setActionName:anActionName];
00648 }
00649 
00656 - (CPString)redoActionName
00657 {
00658     if (![self canRedo])
00659         return nil;
00660 
00661     return [[_redoStack lastObject] actionName];
00662 }
00663 
00669 - (CPString)redoMenuItemTitle
00670 {
00671     return [self redoMenuTitleForUndoActionName:[self redoActionName]];
00672 }
00673 
00679 - (CPString)redoMenuTitleForUndoActionName:(CPString)anActionName
00680 {
00681     // This handles the empty string ("") case as well.
00682     if (anActionName || anActionName === 0)
00683 
00684         // FIXME: The terms @"Redo" and @"Redo %@" should be localized.
00685         // KEYWORDS: Localization
00686         return @"Redo " + anActionName;
00687 
00688     return @"Redo";
00689 }
00690 
00697 - (CPString)undoActionName
00698 {
00699     if (![self canUndo])
00700         return nil;
00701 
00702     return [[_undoStack lastObject] actionName];
00703 }
00704 
00710 - (CPString)undoMenuItemTitle
00711 {
00712     return [self undoMenuTitleForUndoActionName:[self undoActionName]];
00713 }
00714 
00720 - (CPString)undoMenuTitleForUndoActionName:(CPString)anActionName
00721 {
00722     // This handles the empty string ("") case as well.
00723     if (anActionName || anActionName === 0)
00724 
00725         // FIXME: The terms @"Undo" and @"Undo %@" should be localized.
00726         // KEYWORDS: Localization
00727         return @"Undo " + anActionName;
00728 
00729     return @"Undo";
00730 }
00731 
00732 // Working With Run Loops
00737 - (CPArray)runLoopModes
00738 {
00739     return _runLoopModes;
00740 }
00741 
00750 - (void)setRunLoopModes:(CPArray)modes
00751 {
00752     _runLoopModes = [modes copy];
00753 
00754     if (_registeredWithRunLoop)
00755     {
00756         [self _unregisterWithRunLoop];
00757         [self _registerWithRunLoop];
00758     }
00759 }
00760 
00761 - (void)_runLoopEndUndoGrouping
00762 {
00763     [self endUndoGrouping];
00764     _registeredWithRunLoop = NO;
00765 }
00766 
00767 /* @ignore */
00768 - (void)_registerWithRunLoop
00769 {
00770     if (_registeredWithRunLoop)
00771         return;
00772 
00773     _registeredWithRunLoop = YES;
00774     [[CPRunLoop currentRunLoop]
00775         performSelector:@selector(_runLoopEndUndoGrouping)
00776                  target:self
00777                argument:nil
00778                   order:CPUndoCloseGroupingRunLoopOrdering
00779                   modes:_runLoopModes];
00780 }
00781 
00782 /* @ignore */
00783 - (void)_unregisterWithRunLoop
00784 {
00785     if (!_registeredWithRunLoop)
00786         return;
00787 
00788     _registeredWithRunLoop = NO;
00789     [[CPRunLoop currentRunLoop]
00790         cancelPerformSelector:@selector(_runLoopEndUndoGrouping)
00791                        target:self
00792                      argument:nil];
00793 }
00794 
00795 - (void)observeChangesForKeyPath:(CPString)aKeyPath ofObject:(id)anObject
00796 {
00797     [anObject addObserver:self
00798               forKeyPath:aKeyPath
00799                  options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew
00800                  context:NULL];
00801 }
00802 
00803 - (void)stopObservingChangesForKeyPath:(CPString)aKeyPath ofObject:(id)anObject
00804 {
00805     [anObject removeObserver:self forKeyPath:aKeyPath];
00806 }
00807 
00808 - (void)observeValueForKeyPath:(CPString)aKeyPath
00809                       ofObject:(id)anObject
00810                         change:(CPDictionary)aChange
00811                        context:(id)aContext
00812 {
00813     // Don't add no-ops to the undo stack.
00814     var before = [aChange valueForKey:CPKeyValueChangeOldKey],
00815         after = [aChange valueForKey:CPKeyValueChangeNewKey];
00816     if (before === after || (before !== nil && before.isa && (after === nil || after.isa) && [before isEqual:after]))
00817         return;
00818 
00819     [[self prepareWithInvocationTarget:anObject]
00820         applyChange:[aChange inverseChangeDictionary]
00821           toKeyPath:aKeyPath];
00822 }
00823 
00824 @end
00825 
00826 var CPUndoManagerRedoStackKey       = @"CPUndoManagerRedoStackKey",
00827     CPUndoManagerUndoStackKey       = @"CPUndoManagerUndoStackKey",
00828 
00829     CPUndoManagerLevelsOfUndoKey    = @"CPUndoManagerLevelsOfUndoKey",
00830     CPUndoManagerActionNameKey      = @"CPUndoManagerActionNameKey",
00831     CPUndoManagerCurrentGroupingKey = @"CPUndoManagerCurrentGroupingKey",
00832 
00833     CPUndoManagerRunLoopModesKey    = @"CPUndoManagerRunLoopModesKey",
00834     CPUndoManagerGroupsByEventKey   = @"CPUndoManagerGroupsByEventKey";
00835 
00836 @implementation CPUndoManager (CPCoding)
00837 
00838 - (id)initWithCoder:(CPCoder)aCoder
00839 {
00840     self = [super init];
00841 
00842     if (self)
00843     {
00844         _redoStack = [aCoder decodeObjectForKey:CPUndoManagerRedoStackKey];
00845         _undoStack = [aCoder decodeObjectForKey:CPUndoManagerUndoStackKey];
00846 
00847         _levelsOfUndo = [aCoder decodeObjectForKey:CPUndoManagerLevelsOfUndoKey];
00848         _actionName = [aCoder decodeObjectForKey:CPUndoManagerActionNameKey];
00849         _currentGrouping = [aCoder decodeObjectForKey:CPUndoManagerCurrentGroupingKey];
00850 
00851         _state = CPUndoManagerNormal;
00852 
00853         [self setRunLoopModes:[aCoder decodeObjectForKey:CPUndoManagerRunLoopModesKey]];
00854         [self setGroupsByEvent:[aCoder decodeBoolForKey:CPUndoManagerGroupsByEventKey]];
00855     }
00856 
00857     return self;
00858 }
00859 
00860 - (void)encodeWithCoder:(CPCoder)aCoder
00861 {
00862     [aCoder encodeObject:_redoStack forKey:CPUndoManagerRedoStackKey];
00863     [aCoder encodeObject:_undoStack forKey:CPUndoManagerUndoStackKey];
00864 
00865     [aCoder encodeInt:_levelsOfUndo forKey:CPUndoManagerLevelsOfUndoKey];
00866     [aCoder encodeObject:_actionName forKey:CPUndoManagerActionNameKey];
00867 
00868     [aCoder encodeObject:_currentGrouping forKey:CPUndoManagerCurrentGroupingKey];
00869 
00870     [aCoder encodeObject:_runLoopModes forKey:CPUndoManagerRunLoopModesKey];
00871     [aCoder encodeBool:_groupsByEvent forKey:CPUndoManagerGroupsByEventKey];
00872 }
00873 
00874 @end
00875 
00876 @implementation _CPUndoManagerProxy : CPProxy
00877 {
00878     CPUndoManager   _undoManager;
00879 }
00880 
00881 - (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
00882 {
00883     return [_undoManager _methodSignatureOfPreparedTargetForSelector:aSelector];
00884 }
00885 
00886 - (void)forwardInvocation:(CPInvocation)anInvocation
00887 {
00888     [_undoManager _forwardInvocationToPreparedTarget:anInvocation];
00889 }
00890 
00891 @end
 All Classes Files Functions Variables Defines