39 var _CPUndoGroupingPool = [],
40 _CPUndoGroupingPoolCapacity = 5;
43 @implementation _CPUndoGrouping :
CPObject
45 _CPUndoGrouping _parent;
50 + (void)_poolUndoGrouping:(_CPUndoGrouping)anUndoGrouping
52 if (!anUndoGrouping || _CPUndoGroupingPool.length >= _CPUndoGroupingPoolCapacity)
55 _CPUndoGroupingPool.push(anUndoGrouping);
58 + (id)undoGroupingWithParent:(_CPUndoGrouping)anUndoGrouping
60 if (_CPUndoGroupingPool.length)
62 var grouping = _CPUndoGroupingPool.pop();
64 grouping._parent = anUndoGrouping;
66 if (grouping._invocations.length)
67 grouping._invocations = [];
72 return [[
self alloc] initWithParent:anUndoGrouping];
75 - (id)initWithParent:(_CPUndoGrouping)anUndoGrouping
81 _parent = anUndoGrouping;
89 - (_CPUndoGrouping)parent
96 _invocations.push(anInvocation);
99 - (void)addInvocationsFromArray:(CPArray)invocations
101 [_invocations addObjectsFromArray:invocations];
104 - (BOOL)removeInvocationsWithTarget:(
id)aTarget
106 var index = _invocations.length;
109 if ([_invocations[index] target] == aTarget)
110 _invocations.splice(index, 1);
113 - (CPArray)invocations
120 var index = _invocations.length;
123 [_invocations[index] invoke];
126 - (void)setActionName:(
CPString)aName
138 var _CPUndoGroupingParentKey =
@"_CPUndoGroupingParentKey",
139 _CPUndoGroupingInvocationsKey =
@"_CPUndoGroupingInvocationsKey",
140 _CPUndoGroupingActionNameKey =
@"_CPUndoGroupingActionNameKey";
142 @implementation _CPUndoGrouping (CPCoder)
144 - (id)initWithCoder:(
CPCoder)aCoder
150 _parent = [aCoder decodeObjectForKey:_CPUndoGroupingParentKey];
151 _invocations = [aCoder decodeObjectForKey:_CPUndoGroupingInvocationsKey];
152 _actionName = [aCoder decodeObjectForKey:_CPUndoGroupingActionNameKey];
158 - (void)encodeWithCoder:(
CPCoder)aCoder
160 [aCoder encodeObject:_parent forKey:_CPUndoGroupingParentKey];
161 [aCoder encodeObject:_invocations forKey:_CPUndoGroupingInvocationsKey];
162 [aCoder encodeObject:_actionName forKey:_CPUndoGroupingActionNameKey];
194 id _undoManagerProxy;
196 CPArray _runLoopModes;
197 BOOL _registeredWithRunLoop;
219 _undoManagerProxy = [_CPUndoManagerProxy alloc];
220 _undoManagerProxy._undoManager =
self;
228 if (!_currentGrouping)
230 if ([
self groupsByEvent])
231 [
self _beginUndoGroupingForEvent];
235 [_currentGrouping addInvocation:anInvocation];
238 [_redoStack removeAllObjects];
249 - (void)registerUndoWithTarget:(
id)aTarget selector:(
SEL)aSelector object:(
id)anObject
252 if (_disableCount > 0)
259 [invocation setTarget:aTarget];
260 [invocation setSelector:aSelector];
261 [invocation setArgument:anObject atIndex:2];
263 [
self _addUndoInvocation:invocation];
270 - (id)prepareWithInvocationTarget:(
id)aTarget
272 _preparedTarget = aTarget;
274 return _undoManagerProxy;
281 - (CPMethodSignature)_methodSignatureOfPreparedTargetForSelector:(
SEL)aSelector
283 if ([_preparedTarget respondsToSelector:aSelector])
294 - (void)_forwardInvocationToPreparedTarget:(
CPInvocation)anInvocation
297 if (_disableCount > 0)
305 [anInvocation
setTarget:_preparedTarget];
307 [
self _addUndoInvocation:anInvocation];
309 _preparedTarget = nil;
322 return [_redoStack count] > 0;
330 if (_undoStack.length > 0)
333 return [[_currentGrouping invocations] count] > 0;
342 if ([
self groupingLevel] === 1)
351 - (void)undoNestedGroup
353 if ([_undoStack count] <= 0)
358 [defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
361 [defaultCenter postNotificationName:CPUndoManagerWillUndoChangeNotification
364 var undoGrouping = _undoStack.pop(),
365 actionName = [undoGrouping actionName];
369 [
self _beginUndoGrouping];
370 [undoGrouping invoke];
373 [_CPUndoGrouping _poolUndoGrouping:undoGrouping];
377 [[_redoStack lastObject] setActionName:actionName];
379 [defaultCenter postNotificationName:CPUndoManagerDidUndoChangeNotification
389 if ([_redoStack count] <= 0)
399 [defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
402 [defaultCenter postNotificationName:CPUndoManagerWillRedoChangeNotification
405 var oldUndoGrouping = _currentGrouping,
406 undoGrouping = _redoStack.pop(),
407 actionName = [undoGrouping actionName];
409 _currentGrouping = nil;
412 [
self _beginUndoGrouping];
413 [undoGrouping invoke];
416 [_CPUndoGrouping _poolUndoGrouping:undoGrouping];
418 _currentGrouping = oldUndoGrouping;
421 [[_undoStack lastObject] setActionName:actionName];
422 [defaultCenter postNotificationName:CPUndoManagerDidRedoChangeNotification object:self];
429 - (void)beginUndoGrouping
434 if (!_currentGrouping && [
self groupsByEvent])
435 [
self _beginUndoGroupingForEvent];
441 [
self _beginUndoGrouping];
445 - (void)_beginUndoGroupingForEvent
447 [
self _beginUndoGrouping];
448 [
self _registerWithRunLoop];
452 - (void)_beginUndoGrouping
454 _currentGrouping = [_CPUndoGrouping undoGroupingWithParent:_currentGrouping];
461 - (void)endUndoGrouping
463 if (!_currentGrouping)
464 [
CPException raise:CPInternalInconsistencyException
reason:"endUndoGrouping. No undo group is currently open."];
468 [defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
471 var parent = [_currentGrouping parent];
473 if (!parent && [_currentGrouping invocations].length > 0)
476 postNotificationName:CPUndoManagerWillCloseUndoGroupNotification
483 stack.push(_currentGrouping);
485 if (_levelsOfUndo > 0 && stack.length > _levelsOfUndo)
489 postNotificationName:CPUndoManagerDidCloseUndoGroupNotification
496 [parent addInvocationsFromArray:[_currentGrouping invocations]];
498 [_CPUndoGrouping _poolUndoGrouping:_currentGrouping];
501 _currentGrouping = parent;
510 - (void)enableUndoRegistration
512 if (_disableCount <= 0)
514 reason:"enableUndoRegistration. There are no disable messages in effect right now."];
522 - (BOOL)groupsByEvent
524 return _groupsByEvent;
531 - (void)setGroupsByEvent:(BOOL)aFlag
535 if (_groupsByEvent === aFlag)
538 _groupsByEvent = aFlag;
540 if (![
self groupsByEvent])
541 [
self _unregisterWithRunLoop];
547 - (unsigned)groupingLevel
549 var grouping = _currentGrouping,
550 level = _currentGrouping ? 1 : 0;
552 while (grouping = [grouping parent])
562 - (void)disableUndoRegistration
570 - (BOOL)isUndoRegistrationEnabled
572 return _disableCount == 0;
596 - (void)removeAllActions
599 while (_currentGrouping)
603 [
self _unregisterWithRunLoop];
615 - (void)removeAllActionsWithTarget:(
id)aTarget
617 [_currentGrouping removeInvocationsWithTarget:aTarget];
619 var index = _redoStack.length;
623 var grouping = _redoStack[index];
625 [grouping removeInvocationsWithTarget:aTarget];
627 if (![grouping invocations].length)
628 _redoStack.splice(index, 1);
631 index = _undoStack.length;
635 var grouping = _undoStack[index];
637 [grouping removeInvocationsWithTarget:aTarget];
639 if (![grouping invocations].length)
640 _undoStack.splice(index, 1);
652 if (anActionName !== nil && _currentGrouping)
653 [_currentGrouping setActionName:anActionName];
667 return [[_redoStack lastObject] actionName];
688 if (anActionName || anActionName === 0)
692 return @"Redo " + anActionName;
708 return [[_undoStack lastObject] actionName];
729 if (anActionName || anActionName === 0)
733 return @"Undo " + anActionName;
743 - (CPArray)runLoopModes
745 return _runLoopModes;
756 - (void)setRunLoopModes:(CPArray)modes
758 _runLoopModes = [modes copy];
760 if (_registeredWithRunLoop)
762 [
self _unregisterWithRunLoop];
763 [
self _registerWithRunLoop];
767 - (void)_runLoopEndUndoGrouping
769 [
self endUndoGrouping];
770 _registeredWithRunLoop = NO;
774 - (void)_registerWithRunLoop
776 if (_registeredWithRunLoop)
779 _registeredWithRunLoop = YES;
784 order:CPUndoCloseGroupingRunLoopOrdering
785 modes:_runLoopModes];
789 - (void)_unregisterWithRunLoop
791 if (!_registeredWithRunLoop)
794 _registeredWithRunLoop = NO;
801 - (void)observeChangesForKeyPath:(
CPString)aKeyPath ofObject:(
id)anObject
803 [anObject addObserver:self
805 options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew
809 - (void)stopObservingChangesForKeyPath:(
CPString)aKeyPath ofObject:(
id)anObject
811 [anObject removeObserver:self forKeyPath:aKeyPath];
815 ofObject:(
id)anObject
820 var before = [aChange
valueForKey:CPKeyValueChangeOldKey],
821 after = [aChange
valueForKey:CPKeyValueChangeNewKey];
822 if (before === after || (before !== nil && before.isa && (after === nil || after.isa) && [before
isEqual:after]))
850 _redoStack = [aCoder decodeObjectForKey:CPUndoManagerRedoStackKey];
851 _undoStack = [aCoder decodeObjectForKey:CPUndoManagerUndoStackKey];
853 _levelsOfUndo = [aCoder decodeObjectForKey:CPUndoManagerLevelsOfUndoKey];
855 _currentGrouping = [aCoder decodeObjectForKey:CPUndoManagerCurrentGroupingKey];
859 [
self setRunLoopModes:[aCoder decodeObjectForKey:CPUndoManagerRunLoopModesKey]];
860 [
self setGroupsByEvent:[aCoder decodeBoolForKey:CPUndoManagerGroupsByEventKey]];
868 [aCoder encodeObject:_redoStack forKey:CPUndoManagerRedoStackKey];
869 [aCoder encodeObject:_undoStack forKey:CPUndoManagerUndoStackKey];
871 [aCoder encodeInt:_levelsOfUndo forKey:CPUndoManagerLevelsOfUndoKey];
874 [aCoder encodeObject:_currentGrouping forKey:CPUndoManagerCurrentGroupingKey];
876 [aCoder encodeObject:_runLoopModes forKey:CPUndoManagerRunLoopModesKey];
877 [aCoder encodeBool:_groupsByEvent forKey:CPUndoManagerGroupsByEventKey];
882 @implementation _CPUndoManagerProxy :
CPProxy
887 - (CPMethodSignature)methodSignatureForSelector:(
SEL)aSelector
889 return [_undoManager _methodSignatureOfPreparedTargetForSelector:aSelector];
894 [_undoManager _forwardInvocationToPreparedTarget:anInvocation];