Added CPProxy class to support better undo proxying in CPUndoManager.

Closes #370.
Closes #371.

Reviewed by me.
This commit is contained in:
Francisco Ryan Tolmasky I
2009-12-11 11:38:26 -08:00
parent e6244f71bd
commit eb43e416ae
7 changed files with 221 additions and 15 deletions
+1 -1
View File
@@ -312,7 +312,7 @@
_contentView = aView;
[_contentView addObserver:self forKeyPath:@"frame" options:0 context:NULL];
[_contentView addObserver:self forKeyPath:@"frame" options:CPKeyValueObservingOptionOld | CPKeyValueObservingOptionNew context:NULL];
[self addSubview:_contentView];
+1 -1
View File
@@ -701,7 +701,7 @@ CPRunContinuesResponse = -1002;
- (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)aSender
{
var target = [self targetForAction:anAction to:aTarget from:aSender];
if (!target)
return NO;
+13 -1
View File
@@ -2369,9 +2369,21 @@ var keyViewComparator = function(a, b, context)
*/
- (CPUndoManager)undoManager
{
// If we've ever created an undo manager, return it.
if (_undoManager)
return _undoManager;
// If not, check to see if the document has one.
var documentUndoManager = [[_windowController document] undoManager];
if (documentUndoManager)
return documentUndoManager;
// If not, check to see if the delegate has one.
if (_delegateRespondsToWindowWillReturnUndoManagerSelector)
return [_delegate windowWillReturnUndoManager:self];
// If not, create one.
if (!_undoManager)
_undoManager = [[CPUndoManager alloc] init];
+15 -6
View File
@@ -373,19 +373,28 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld,
else if (!isBefore)
[observerInfo.observer observeValueForKeyPath:aKey ofObject:_targetObject change:changes context:observerInfo.context];
}
var dependentKeysMap = _nativeClass[DependentKeysKey];
if (!dependentKeysMap)
return;
var keysComposedOfKey = [dependentKeysMap[aKey] allObjects];
if (!keysComposedOfKey)
var dependentKeyPaths = [dependentKeysMap[aKey] allObjects];
if (!dependentKeyPaths)
return;
for (var i=0, count=keysComposedOfKey.length; i<count; i++)
[self _sendNotificationsForKey:keysComposedOfKey[i] changeOptions:changeOptions isBefore:isBefore];
var index = 0,
count = [dependentKeyPaths count];
for (; index < count; ++index)
{
var key = dependentKeyPaths[index];
[self _sendNotificationsForKey:keyPath
changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath]
isBefore:isBefore];
}
}
@end
+3 -1
View File
@@ -227,6 +227,7 @@ CPLog(@"Got some class: %@", inst);
*/
- (BOOL)respondsToSelector:(SEL)aSelector
{
// isa is isa.isa in class case.
return !!class_getInstanceMethod(isa, aSelector);
}
@@ -269,7 +270,7 @@ CPLog(@"Got some class: %@", inst);
*/
- (CPString)description
{
return "<" + isa.name + " 0x" + [CPString stringWithHash:[self UID]] + ">";
return "<" + class_getName(isa) + " 0x" + [CPString stringWithHash:[self UID]] + ">";
}
// Sending Messages
@@ -322,6 +323,7 @@ CPLog(@"Got some class: %@", inst);
Used for forwarding of messages to other objects.
@ignore
*/
// FIXME: This should be moved to the runtime?
- (void)forward:(SEL)aSelector :(marg_list)args
{
var signature = [self methodSignatureForSelector:aSelector];
+161
View File
@@ -0,0 +1,161 @@
/*
* CPProxy.j
* Foundation
*
* Created by Francisco Tolmasky.
* Copyright 2009, 280 North, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@import "CPException.j"
@import "CPInvocation.j"
@import "CPString.j"
@implementation CPProxy
{
}
+ (void)load
{
}
+ (void)initialize
{
}
+ (Class)class
{
return self;
}
+ (id)alloc
{
return class_createInstance(self);
}
+ (BOOL)respondsToSelector:(SEL)selector
{
return !!class_getInstanceMethod(isa, aSelector);
}
- (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
{
[CPException raise:CPInvalidArgumentException
reason:@"-methodSignatureForSelector: called on abstract CPProxy class."];
}
- (void)forwardInvocation:(CPInvocation)anInvocation
{
[CPException raise:CPInvalidArgumentException
reason:@"-methodSignatureForSelector: called on abstract CPProxy class."];
}
// FIXME: This should be moved to the runtime?
- (void)forward:(SEL)aSelector :(marg_list)args
{
[CPObject methodForSelector:_cmd](self, _cmd, aSelector, args);
}
- (unsigned)hash
{
return [self UID];
}
- (unsigned)UID
{
if (typeof self.__address === "undefined")
self.__address = _objj_generateObjectHash();
return __address;
}
- (BOOL)isEqual:(id)anObject
{
return self === object;
}
- (id)self
{
return self;
}
- (Class)class
{
return isa;
}
- (Class)superclass
{
return class_getSuperclass(isa);
}
- (id)performSelector:(SEL)aSelector
{
return objj_msgSend(self, aSelector);
}
- (id)performSelector:(SEL)aSelector withObject:(id)anObject
{
return objj_msgSend(self, aSelector, anObject);
}
- (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject
{
return objj_msgSend(self, aSelector, anObject, anotherObject);
}
- (BOOL)isProxy
{
return YES;
}
- (BOOL)isKindOfClass:(Class)aClass
{
var signature = [self methodSignatureForSelector:_cmd],
invocation = [CPInvocation invocationWithMethodSignature:signature];
[self forwardInvocation:invocation];
return [invocation returnValue];
}
-(BOOL)isMemberOfClass:(Class)aClass
{
var signature = [self methodSignatureForSelector:_cmd],
invocation = [CPInvocation invocationWithMethodSignature:signature];
[self forwardInvocation:invocation];
return [invocation returnValue];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
var signature = [self methodSignatureForSelector:_cmd],
invocation = [CPInvocation invocationWithMethodSignature:signature];
[self forwardInvocation:invocation];
return [invocation returnValue];
}
- (CPString)description
{
return "<" + class_getName(isa) + " 0x" + [CPString stringWithHash:[self UID]] + ">";
}
@end
+27 -5
View File
@@ -22,6 +22,7 @@
@import "CPObject.j"
@import "CPInvocation.j"
@import "CPProxy.j"
var CPUndoManagerNormal = 0,
@@ -177,8 +178,10 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
id _currentGrouping;
int _state;
CPString _actionName;
id _preparedTarget;
id _undoManagerProxy;
CPArray _runLoopModes;
BOOL _registeredWithRunLoop;
}
@@ -200,7 +203,9 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
[self setRunLoopModes:[CPDefaultRunLoopMode]];
[self setGroupsByEvent:YES];
_performRegistered = NO;
_undoManagerProxy = [_CPUndoManagerProxy alloc];
_undoManagerProxy._undoManager = self;
}
return self;
@@ -244,14 +249,14 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
{
_preparedTarget = aTarget;
return self;
return _undoManagerProxy;
}
/*
FIXME This method doesn't seem to do anything right
@ignore
*/
-(CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
- (CPMethodSignature)_methodSignatureOfPreparedTargetForSelector:(SEL)aSelector
{
if ([_preparedTarget respondsToSelector:aSelector])
return 1;
@@ -264,7 +269,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey",
target on the invocation, and adds it to the current grouping.
@param anInvocation the message to record
*/
- (void)forwardInvocation:(CPInvocation)anInvocation
- (void)_forwardInvocationToPreparedTarget:(CPInvocation)anInvocation
{
if (_disableCount > 0)
return;
@@ -718,3 +723,20 @@ var CPUndoManagerRedoStackKey = @"CPUndoManagerRedoStackKey",
}
@end
@implementation _CPUndoManagerProxy : CPProxy
{
CPUndoManager _undoManager;
}
- (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
{
return [_undoManager _methodSignatureOfPreparedTargetForSelector:aSelector];
}
- (void)forwardInvocation:(CPInvocation)anInvocation
{
[_undoManager _forwardInvocationToPreparedTarget:anInvocation];
}
@end