API  0.9.10
CPUndoManager.j
Go to the documentation of this file.
1 /*
2  * CPUndoManager.j
3  * Foundation
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
27 
28 CPUndoManagerCheckpointNotification = @"CPUndoManagerCheckpointNotification";
29 CPUndoManagerDidOpenUndoGroupNotification = @"CPUndoManagerDidOpenUndoGroupNotification";
30 CPUndoManagerDidCloseUndoGroupNotification = @"CPUndoManagerDidCloseUndoGroupNotification";
31 CPUndoManagerDidRedoChangeNotification = @"CPUndoManagerDidRedoChangeNotification";
32 CPUndoManagerDidUndoChangeNotification = @"CPUndoManagerDidUndoChangeNotification";
33 CPUndoManagerWillCloseUndoGroupNotification = @"CPUndoManagerWillCloseUndoGroupNotification";
34 CPUndoManagerWillRedoChangeNotification = @"CPUndoManagerWillRedoChangeNotification";
35 CPUndoManagerWillUndoChangeNotification = @"CPUndoManagerWillUndoChangeNotification";
36 
38 
39 var _CPUndoGroupingPool = [],
40  _CPUndoGroupingPoolCapacity = 5;
41 
42 /* @ignore */
43 @implementation _CPUndoGrouping : CPObject
44 {
45  id _parent;
46  CPMutableArray _invocations;
47  CPString _actionName;
48 }
49 
50 + (void)_poolUndoGrouping:(_CPUndoGrouping)anUndoGrouping
51 {
52  if (!anUndoGrouping || _CPUndoGroupingPool.length >= _CPUndoGroupingPoolCapacity)
53  return;
54 
55  _CPUndoGroupingPool.push(anUndoGrouping);
56 }
57 
58 + (id)undoGroupingWithParent:(_CPUndoGrouping)anUndoGrouping
59 {
60  if (_CPUndoGroupingPool.length)
61  {
62  var grouping = _CPUndoGroupingPool.pop();
63 
64  grouping._parent = anUndoGrouping;
65 
66  if (grouping._invocations.length)
67  grouping._invocations = [];
68 
69  return grouping;
70  }
71 
72  return [[self alloc] initWithParent:anUndoGrouping];
73 }
74 
75 - (id)initWithParent:(_CPUndoGrouping)anUndoGrouping
76 {
77  self = [super init];
78 
79  if (self)
80  {
81  _parent = anUndoGrouping;
82  _invocations = [];
83  _actionName = @"";
84  }
85 
86  return self;
87 }
88 
89 - (_CPUndoGrouping)parent
90 {
91  return _parent;
92 }
93 
94 - (void)addInvocation:(CPInvocation)anInvocation
95 {
96  _invocations.push(anInvocation);
97 }
98 
99 - (void)addInvocationsFromArray:(CPArray)invocations
100 {
101  [_invocations addObjectsFromArray:invocations];
102 }
103 
104 - (BOOL)removeInvocationsWithTarget:(id)aTarget
105 {
106  var index = _invocations.length;
107 
108  while (index--)
109  if ([_invocations[index] target] == aTarget)
110  _invocations.splice(index, 1);
111 }
112 
113 - (CPArray)invocations
114 {
115  return _invocations;
116 }
117 
118 - (void)invoke
119 {
120  var index = _invocations.length;
121 
122  while (index--)
123  [_invocations[index] invoke];
124 }
125 
126 - (void)setActionName:(CPString)aName
127 {
128  _actionName = aName;
129 }
130 
131 - (CPString)actionName
132 {
133  return _actionName;
134 }
135 
136 @end
137 
138 var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
139  _CPUndoGroupingInvocationsKey = @"_CPUndoGroupingInvocationsKey",
140  _CPUndoGroupingActionNameKey = @"_CPUndoGroupingActionNameKey";
141 
142 @implementation _CPUndoGrouping (CPCoder)
143 
144 - (id)initWithCoder:(CPCoder)aCoder
145 {
146  self = [super init];
147 
148  if (self)
149  {
150  _parent = [aCoder decodeObjectForKey:_CPUndoGroupingParentKey];
151  _invocations = [aCoder decodeObjectForKey:_CPUndoGroupingInvocationsKey];
152  _actionName = [aCoder decodeObjectForKey:_CPUndoGroupingActionNameKey];
153  }
154 
155  return self;
156 }
157 
158 - (void)encodeWithCoder:(CPCoder)aCoder
159 {
160  [aCoder encodeObject:_parent forKey:_CPUndoGroupingParentKey];
161  [aCoder encodeObject:_invocations forKey:_CPUndoGroupingInvocationsKey];
162  [aCoder encodeObject:_actionName forKey:_CPUndoGroupingActionNameKey];
163 }
164 
165 @end
166 
181 @implementation CPUndoManager : CPObject
182 {
183  CPMutableArray _redoStack;
184  CPMutableArray _undoStack;
185 
186  BOOL _groupsByEvent;
187  int _disableCount;
188  int _levelsOfUndo;
189  id _currentGrouping;
190  int _state;
191 
192  id _preparedTarget;
193  id _undoManagerProxy;
194 
195  CPArray _runLoopModes;
196  BOOL _registeredWithRunLoop;
197 }
198 
203 - (id)init
204 {
205  self = [super init];
206 
207  if (self)
208  {
209  _redoStack = [];
210  _undoStack = [];
211 
212  _disableCount = 0;
213  _state = CPUndoManagerNormal;
214 
215  [self setRunLoopModes:[CPDefaultRunLoopMode]];
216  [self setGroupsByEvent:YES];
217 
218  _undoManagerProxy = [_CPUndoManagerProxy alloc];
219  _undoManagerProxy._undoManager = self;
220  }
221 
222  return self;
223 }
224 
225 - (void)_addUndoInvocation:(CPInvocation)anInvocation
226 {
227  if (!_currentGrouping)
228  // Remember that we create these lazily...
229  if ([self groupsByEvent])
230  [self _beginUndoGroupingForEvent];
231  else
232  [CPException raise:CPInternalInconsistencyException reason:"No undo group is currently open"];
233 
234  [_currentGrouping addInvocation:anInvocation];
235 
236  if (_state === CPUndoManagerNormal)
237  [_redoStack removeAllObjects];
238 }
239 
240 // Registering Undo Operations
248 - (void)registerUndoWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anObject
249 {
250  // Don't do anything if we're disabled.
251  if (_disableCount > 0)
252  return;
253 
254  //signature = [target methodSignatureForSelector:selector];
255  // FIXME: we need method signatures.
256  var invocation = [CPInvocation invocationWithMethodSignature:nil];
257 
258  [invocation setTarget:aTarget];
259  [invocation setSelector:aSelector];
260  [invocation setArgument:anObject atIndex:2];
261 
262  [self _addUndoInvocation:invocation];
263 }
269 - (id)prepareWithInvocationTarget:(id)aTarget
270 {
271  _preparedTarget = aTarget;
272 
273  return _undoManagerProxy;
274 }
275 
276 /*
277  FIXME This method doesn't seem to do anything right
278  @ignore
279 */
280 - (CPMethodSignature)_methodSignatureOfPreparedTargetForSelector:(SEL)aSelector
281 {
282  if ([_preparedTarget respondsToSelector:aSelector])
283  return 1;
284 
285  return nil;//[_preparedTarget methodSignatureForSelector:selector];
286 }
287 
293 - (void)_forwardInvocationToPreparedTarget:(CPInvocation)anInvocation
294 {
295  // Don't do anything if we're disabled.
296  if (_disableCount > 0)
297  return;
298 
299 /*
300 if (_currentGroup == nil)
301  [NSException raise:NSInternalInconsistencyException
302  format:@"forwardInvocation called without first opening an undo group"];
303 */
304  [anInvocation setTarget:_preparedTarget];
305 
306  [self _addUndoInvocation:anInvocation];
307 
308  _preparedTarget = nil;
309 }
310 
311 // Checking Undo Ability
315 - (BOOL)canRedo
316 {
318  postNotificationName:CPUndoManagerCheckpointNotification
319  object:self];
320 
321  return [_redoStack count] > 0;
322 }
323 
327 - (BOOL)canUndo
328 {
329  if (_undoStack.length > 0)
330  return YES;
331 
332  return [[_currentGrouping invocations] count] > 0;
333 }
334 
335 // Preform Undo and Redo
339 - (void)undo
340 {
341  if ([self groupingLevel] === 1)
342  [self endUndoGrouping];
343 
344  [self undoNestedGroup];
345 }
346 
350 - (void)undoNestedGroup
351 {
352  if ([_undoStack count] <= 0)
353  return;
354 
355  var defaultCenter = [CPNotificationCenter defaultCenter];
356 
357  [defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
358  object:self];
359 
360  [defaultCenter postNotificationName:CPUndoManagerWillUndoChangeNotification
361  object:self];
362 
363  var undoGrouping = _undoStack.pop(),
364  actionName = [undoGrouping actionName];
365 
366  _state = CPUndoManagerUndoing;
367 
368  [self _beginUndoGrouping];
369  [undoGrouping invoke];
370  [self endUndoGrouping];
371 
372  [_CPUndoGrouping _poolUndoGrouping:undoGrouping];
373 
374  _state = CPUndoManagerNormal;
375 
376  [[_redoStack lastObject] setActionName:actionName];
377 
378  [defaultCenter postNotificationName:CPUndoManagerDidUndoChangeNotification
379  object:self];
380 }
381 
385 - (void)redo
386 {
387  // Don't do anything if we have no redos.
388  if ([_redoStack count] <= 0)
389  return;
390 
391 /* if (_state == NSUndoManagerUndoing)
392  [NSException raise:NSInternalInconsistencyException
393  format:@"redo called while undoing"];
394 */
395 
396  var defaultCenter = [CPNotificationCenter defaultCenter];
397 
398  [defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
399  object:self];
400 
401  [defaultCenter postNotificationName:CPUndoManagerWillRedoChangeNotification
402  object:self];
403 
404  var oldUndoGrouping = _currentGrouping,
405  undoGrouping = _redoStack.pop(),
406  actionName = [undoGrouping actionName];
407 
408  _currentGrouping = nil;
409  _state = CPUndoManagerRedoing;
410 
411  [self _beginUndoGrouping];
412  [undoGrouping invoke];
413  [self endUndoGrouping];
414 
415  [_CPUndoGrouping _poolUndoGrouping:undoGrouping];
416 
417  _currentGrouping = oldUndoGrouping;
418  _state = CPUndoManagerNormal;
419 
420  [[_undoStack lastObject] setActionName:actionName];
421  [defaultCenter postNotificationName:CPUndoManagerDidRedoChangeNotification object:self];
422 }
423 
424 // Creating Undo Groups
428 - (void)beginUndoGrouping
429 {
430  // It doesn't matter that the user is creating a group themselves, we are
431  // pretending to have opened the group at the beginning of the run loop,
432  // so create an implicit one here.
433  if (!_currentGrouping && [self groupsByEvent])
434  [self _beginUndoGroupingForEvent];
435 
437  postNotificationName:CPUndoManagerCheckpointNotification
438  object:self];
439 
440  [self _beginUndoGrouping];
441 }
442 
443 /* @ignore */
444 - (void)_beginUndoGroupingForEvent
445 {
446  [self _beginUndoGrouping];
447  [self _registerWithRunLoop];
448 }
449 
450 /* @ignore */
451 - (void)_beginUndoGrouping
452 {
453  _currentGrouping = [_CPUndoGrouping undoGroupingWithParent:_currentGrouping];
454 }
455 
460 - (void)endUndoGrouping
461 {
462  if (!_currentGrouping)
463  [CPException raise:CPInternalInconsistencyException reason:"endUndoGrouping. No undo group is currently open."];
464 
465  var defaultCenter = [CPNotificationCenter defaultCenter];
466 
467  [defaultCenter postNotificationName:CPUndoManagerCheckpointNotification
468  object:self];
469 
470  var parent = [_currentGrouping parent];
471 
472  if (!parent && [_currentGrouping invocations].length > 0)
473  {
474  [defaultCenter
475  postNotificationName:CPUndoManagerWillCloseUndoGroupNotification
476  object:self];
477 
478  // Put this group on the redo stack if we are currently undoing, otherwise
479  // put it on the undo stack. That way, "undos" become "redos".
480  var stack = _state === CPUndoManagerUndoing ? _redoStack : _undoStack;
481 
482  stack.push(_currentGrouping);
483 
484  if (_levelsOfUndo > 0 && stack.length > _levelsOfUndo)
485  stack.splice(0, 1);
486 
487  [defaultCenter
488  postNotificationName:CPUndoManagerDidCloseUndoGroupNotification
489  object:self];
490  }
491 
492  // Nested Undo Grouping
493  else
494  {
495  [parent addInvocationsFromArray:[_currentGrouping invocations]];
496 
497  [_CPUndoGrouping _poolUndoGrouping:_currentGrouping];
498  }
499 
500  _currentGrouping = parent;
501 }
502 
509 - (void)enableUndoRegistration
510 {
511  if (_disableCount <= 0)
512  [CPException raise:CPInternalInconsistencyException
513  reason:"enableUndoRegistration. There are no disable messages in effect right now."];
514 
515  _disableCount--;
516 }
517 
521 - (BOOL)groupsByEvent
522 {
523  return _groupsByEvent;
524 }
525 
530 - (void)setGroupsByEvent:(BOOL)aFlag
531 {
532  aFlag = !!aFlag;
533 
534  if (_groupsByEvent === aFlag)
535  return;
536 
537  _groupsByEvent = aFlag;
538 
539  if (![self groupsByEvent])
540  [self _unregisterWithRunLoop];
541 }
542 
546 - (unsigned)groupingLevel
547 {
548  var grouping = _currentGrouping,
549  level = _currentGrouping ? 1 : 0;
550 
551  while (grouping = [grouping parent])
552  ++level;
553 
554  return level;
555 }
556 
557 // Disabling Undo
561 - (void)disableUndoRegistration
562 {
563  ++_disableCount;
564 }
565 
569 - (BOOL)isUndoRegistrationEnabled
570 {
571  return _disableCount == 0;
572 }
573 
574 // Checking Whether Undo or Redo Is Being Performed
578 - (BOOL)isUndoing
579 {
580  return _state === CPUndoManagerUndoing;
581 }
582 
586 - (BOOL)isRedoing
587 {
588  return _state === CPUndoManagerRedoing;
589 }
590 
591 // Clearing Undo Operations
595 - (void)removeAllActions
596 {
597  // Close off any groupings.
598  while (_currentGrouping)
599  [self endUndoGrouping];
600 
601  // Won't need this anymore
602  [self _unregisterWithRunLoop];
603 
604  _state = CPUndoManagerNormal;
605  _redoStack = [];
606  _undoStack = [];
607  _disableCount = 0;
608 }
609 
614 - (void)removeAllActionsWithTarget:(id)aTarget
615 {
616  [_currentGrouping removeInvocationsWithTarget:aTarget];
617 
618  var index = _redoStack.length;
619 
620  while (index--)
621  {
622  var grouping = _redoStack[index];
623 
624  [grouping removeInvocationsWithTarget:aTarget];
625 
626  if (![grouping invocations].length)
627  _redoStack.splice(index, 1);
628  }
629 
630  index = _undoStack.length;
631 
632  while (index--)
633  {
634  var grouping = _undoStack[index];
635 
636  [grouping removeInvocationsWithTarget:aTarget];
637 
638  if (![grouping invocations].length)
639  _undoStack.splice(index, 1);
640  }
641 }
642 
643 // Managing the Action Name
649 - (void)setActionName:(CPString)anActionName
650 {
651  if (anActionName !== nil && _currentGrouping)
652  [_currentGrouping setActionName:anActionName];
653 }
654 
661 - (CPString)redoActionName
662 {
663  if (![self canRedo])
664  return nil;
665 
666  return [[_redoStack lastObject] actionName];
667 }
668 
674 - (CPString)redoMenuItemTitle
675 {
676  return [self redoMenuTitleForUndoActionName:[self redoActionName]];
677 }
678 
684 - (CPString)redoMenuTitleForUndoActionName:(CPString)anActionName
685 {
686  // This handles the empty string ("") case as well.
687  if (anActionName || anActionName === 0)
688 
689  // FIXME: The terms @"Redo" and @"Redo %@" should be localized.
690  // KEYWORDS: Localization
691  return @"Redo " + anActionName;
692 
693  return @"Redo";
694 }
695 
702 - (CPString)undoActionName
703 {
704  if (![self canUndo])
705  return nil;
706 
707  return [[_undoStack lastObject] actionName];
708 }
709 
715 - (CPString)undoMenuItemTitle
716 {
717  return [self undoMenuTitleForUndoActionName:[self undoActionName]];
718 }
719 
725 - (CPString)undoMenuTitleForUndoActionName:(CPString)anActionName
726 {
727  // This handles the empty string ("") case as well.
728  if (anActionName || anActionName === 0)
729 
730  // FIXME: The terms @"Undo" and @"Undo %@" should be localized.
731  // KEYWORDS: Localization
732  return @"Undo " + anActionName;
733 
734  return @"Undo";
735 }
736 
737 // Working With Run Loops
742 - (CPArray)runLoopModes
743 {
744  return _runLoopModes;
745 }
746 
755 - (void)setRunLoopModes:(CPArray)modes
756 {
757  _runLoopModes = [modes copy];
758 
759  if (_registeredWithRunLoop)
760  {
761  [self _unregisterWithRunLoop];
762  [self _registerWithRunLoop];
763  }
764 }
765 
766 - (void)_runLoopEndUndoGrouping
767 {
768  [self endUndoGrouping];
769  _registeredWithRunLoop = NO;
770 }
771 
772 /* @ignore */
773 - (void)_registerWithRunLoop
774 {
775  if (_registeredWithRunLoop)
776  return;
777 
778  _registeredWithRunLoop = YES;
780  performSelector:@selector(_runLoopEndUndoGrouping)
781  target:self
782  argument:nil
783  order:CPUndoCloseGroupingRunLoopOrdering
784  modes:_runLoopModes];
785 }
786 
787 /* @ignore */
788 - (void)_unregisterWithRunLoop
789 {
790  if (!_registeredWithRunLoop)
791  return;
792 
793  _registeredWithRunLoop = NO;
795  cancelPerformSelector:@selector(_runLoopEndUndoGrouping)
796  target:self
797  argument:nil];
798 }
799 
800 - (void)observeChangesForKeyPath:(CPString)aKeyPath ofObject:(id)anObject
801 {
802  [anObject addObserver:self
803  forKeyPath:aKeyPath
804  options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew
805  context:NULL];
806 }
807 
808 - (void)stopObservingChangesForKeyPath:(CPString)aKeyPath ofObject:(id)anObject
809 {
810  [anObject removeObserver:self forKeyPath:aKeyPath];
811 }
812 
813 - (void)observeValueForKeyPath:(CPString)aKeyPath
814  ofObject:(id)anObject
815  change:(CPDictionary)aChange
816  context:(id)aContext
817 {
818  // Don't add no-ops to the undo stack.
819  var before = [aChange valueForKey:CPKeyValueChangeOldKey],
820  after = [aChange valueForKey:CPKeyValueChangeNewKey];
821  if (before === after || (before !== nil && before.isa && (after === nil || after.isa) && [before isEqual:after]))
822  return;
823 
824  [[self prepareWithInvocationTarget:anObject]
825  applyChange:[aChange inverseChangeDictionary]
826  toKeyPath:aKeyPath];
827 }
828 
829 @end
830 
831 var CPUndoManagerRedoStackKey = @"CPUndoManagerRedoStackKey",
832  CPUndoManagerUndoStackKey = @"CPUndoManagerUndoStackKey",
833 
834  CPUndoManagerLevelsOfUndoKey = @"CPUndoManagerLevelsOfUndoKey",
835  CPUndoManagerActionNameKey = @"CPUndoManagerActionNameKey",
836  CPUndoManagerCurrentGroupingKey = @"CPUndoManagerCurrentGroupingKey",
837 
838  CPUndoManagerRunLoopModesKey = @"CPUndoManagerRunLoopModesKey",
839  CPUndoManagerGroupsByEventKey = @"CPUndoManagerGroupsByEventKey";
840 
842 
843 - (id)initWithCoder:(CPCoder)aCoder
844 {
845  self = [super init];
846 
847  if (self)
848  {
849  _redoStack = [aCoder decodeObjectForKey:CPUndoManagerRedoStackKey];
850  _undoStack = [aCoder decodeObjectForKey:CPUndoManagerUndoStackKey];
851 
852  _levelsOfUndo = [aCoder decodeObjectForKey:CPUndoManagerLevelsOfUndoKey];
853 // _actionName = [aCoder decodeObjectForKey:CPUndoManagerActionNameKey];
854  _currentGrouping = [aCoder decodeObjectForKey:CPUndoManagerCurrentGroupingKey];
855 
856  _state = CPUndoManagerNormal;
857 
858  [self setRunLoopModes:[aCoder decodeObjectForKey:CPUndoManagerRunLoopModesKey]];
859  [self setGroupsByEvent:[aCoder decodeBoolForKey:CPUndoManagerGroupsByEventKey]];
860  }
861 
862  return self;
863 }
864 
865 - (void)encodeWithCoder:(CPCoder)aCoder
866 {
867  [aCoder encodeObject:_redoStack forKey:CPUndoManagerRedoStackKey];
868  [aCoder encodeObject:_undoStack forKey:CPUndoManagerUndoStackKey];
869 
870  [aCoder encodeInt:_levelsOfUndo forKey:CPUndoManagerLevelsOfUndoKey];
871 // [aCoder encodeObject:_actionName forKey:CPUndoManagerActionNameKey];
872 
873  [aCoder encodeObject:_currentGrouping forKey:CPUndoManagerCurrentGroupingKey];
874 
875  [aCoder encodeObject:_runLoopModes forKey:CPUndoManagerRunLoopModesKey];
876  [aCoder encodeBool:_groupsByEvent forKey:CPUndoManagerGroupsByEventKey];
877 }
878 
879 @end
880 
881 @implementation _CPUndoManagerProxy : CPProxy
882 {
883  CPUndoManager _undoManager;
884 }
885 
886 - (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
887 {
888  return [_undoManager _methodSignatureOfPreparedTargetForSelector:aSelector];
889 }
890 
891 - (void)forwardInvocation:(CPInvocation)anInvocation
892 {
893  [_undoManager _forwardInvocationToPreparedTarget:anInvocation];
894 }
895 
896 @end
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
CPUndoManagerWillRedoChangeNotification
Definition: CPUndoManager.j:34
CPString redoActionName()
var CPUndoManagerNormal
Definition: CPUndoManager.j:24
id init()
Definition: CALayer.j:126
id invocationWithMethodSignature:(CPMethodSignature aMethodSignature)
Definition: CPInvocation.j:43
var CPUndoManagerUndoStackKey
var CPUndoManagerGroupsByEventKey
The main run loop for the application.
Definition: CPRunLoop.h:2
var CPUndoManagerRunLoopModesKey
void performSelector:target:argument:order:modes:(SEL aSelector, [target] id aTarget, [argument] id anArgument, [order] int anOrder, [modes] CPArray modes)
Definition: CPRunLoop.j:253
CPDictionary inverseChangeDictionary()
CPUndoManagerDidCloseUndoGroupNotification
Definition: CPUndoManager.j:30
Definition: CPProxy.h:2
void postNotificationName:object:(CPString aNotificationName, [object] id anObject)
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
void applyChange:toKeyPath:(CPDictionary aChange, [toKeyPath] CPString aKeyPath)
CPNotificationCenter defaultCenter()
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPUndoManagerWillCloseUndoGroupNotification
Definition: CPUndoManager.j:33
CPRunLoop currentRunLoop()
Definition: CPRunLoop.j:232
void setRunLoopModes:(CPArray modes)
An object representation of a message.
Definition: CPInvocation.h:2
void cancelPerformSelector:target:argument:(SEL aSelector, [target] id aTarget, [argument] id anArgument)
Definition: CPRunLoop.j:289
var CPUndoManagerLevelsOfUndoKey
An immutable string (collection of characters).
Definition: CPString.h:2
var CPUndoManagerRedoStackKey
var CPUndoManagerRedoing
Definition: CPUndoManager.j:26
var CPUndoManagerUndoing
Definition: CPUndoManager.j:25
CPString undoActionName()
CPUndoManagerDidOpenUndoGroupNotification
Definition: CPUndoManager.j:29
void endUndoGrouping()
CPUndoManagerWillUndoChangeNotification
Definition: CPUndoManager.j:35
void setGroupsByEvent:(BOOL aFlag)
var CPUndoManagerActionNameKey
CPUndoManagerCheckpointNotification
Definition: CPUndoManager.j:28
A general mechanism for user action "undo".
Definition: CPUndoManager.h:2
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
id valueForKey:(CPString aKey)
CPUndoCloseGroupingRunLoopOrdering
Definition: CPUndoManager.j:37
void undoNestedGroup()
CPString redoMenuTitleForUndoActionName:(CPString anActionName)
id init()
Definition: CPObject.j:145
Sends messages (CPNotification) between objects.
id prepareWithInvocationTarget:(id aTarget)
CPUndoManagerDidRedoChangeNotification
Definition: CPUndoManager.j:31
var CPUndoManagerCurrentGroupingKey
CPUndoManagerDidUndoChangeNotification
Definition: CPUndoManager.j:32
CPString undoMenuTitleForUndoActionName:(CPString anActionName)