mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-23 20:01:11 +00:00
Rewrote KVB to use separate binding class.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@25728 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
311bead94e
commit
295b663a75
5 changed files with 488 additions and 428 deletions
|
@ -1,3 +1,10 @@
|
|||
2007-12-13 Fred Kiefer <FredKiefer@gmx.de>
|
||||
|
||||
* Source/GSBindingHelpers.h,
|
||||
* Source/NSKeyValueBinding.m: Rewrote KVB to use separate binding class.
|
||||
* Source/NSTextField.m,
|
||||
* Source/NSView.m: Use it here.
|
||||
|
||||
2007-12-12 Fred Kiefer <FredKiefer@gmx.de>
|
||||
|
||||
* Source/NSNibBindingConnector.m (-establishConnection): Swap
|
||||
|
|
|
@ -27,51 +27,44 @@
|
|||
#ifndef _GS_BINDING_HELPER_H
|
||||
#define _GS_BINDING_HELPER_H
|
||||
|
||||
#include <Foundation/NSObject.h>
|
||||
|
||||
@class NSString;
|
||||
@class NSDictionary;
|
||||
@class NSMutableDictionary;
|
||||
@class NSArray;
|
||||
|
||||
typedef enum {
|
||||
GSBindingOperationAnd = 0,
|
||||
GSBindingOperationOr
|
||||
} GSBindingOperationKind;
|
||||
@interface GSKeyValueBinding : NSObject
|
||||
{
|
||||
@public
|
||||
NSDictionary *info;
|
||||
id src;
|
||||
}
|
||||
|
||||
//Obtain a lock
|
||||
void GSBindingLock();
|
||||
|
||||
//Releases the lock
|
||||
void GSBindingReleaseLock();
|
||||
|
||||
|
||||
//Get the mutable list of bindings for an object. You must obtain a lock
|
||||
//with GSBindingLock() before calling this function and release the lock with
|
||||
//GSBindingReleaseLock() when done with the dictionary.
|
||||
NSMutableDictionary *GSBindingListForObject(id object);
|
||||
|
||||
//TODO: document
|
||||
BOOL GSBindingResolveMultipleValueBool(NSString *key, NSDictionary *bindings,
|
||||
GSBindingOperationKind operationKind);
|
||||
|
||||
//TODO: document
|
||||
void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey,
|
||||
NSDictionary *bindings);
|
||||
|
||||
|
||||
NSArray *GSBindingExposeMultipleValueBindings(
|
||||
NSArray *bindingNames,
|
||||
NSMutableDictionary *bindingList);
|
||||
|
||||
NSArray *GSBindingExposePatternBindings(
|
||||
NSArray *bindingNames,
|
||||
NSMutableDictionary *bindingList);
|
||||
|
||||
void GSBindingUnbindAll(id object);
|
||||
+ (void) exposeBinding: (NSString *)binding forClass: (Class)clazz;
|
||||
+ (NSArray *) exposedBindingsForClass: (Class)clazz;
|
||||
+ (NSDictionary *) infoForBinding: (NSString *)binding forObject: (id)anObject;
|
||||
+ (void) unbind: (NSString *)binding forObject: (id)anObject;
|
||||
+ (void) unbindAllForObject: (id)anObject;
|
||||
|
||||
- (id) initWithBinding: (NSString *)binding
|
||||
withName: (NSString *)name
|
||||
toObject: (id)dest
|
||||
withKeyPath: (NSString *)keyPath
|
||||
options: (NSDictionary *)options
|
||||
fromObject: (id)source;
|
||||
- (void) setValueFor: (NSString *)binding;
|
||||
/* Transforms the value with a value transformer, if specified and available,
|
||||
* and takes care of any placeholders
|
||||
*/
|
||||
id GSBindingTransformedValue(id value, NSDictionary *options);
|
||||
id GSBindingReverseTransformedValue(id value, NSDictionary *options);
|
||||
- (id) transformValue: (id)value withOptions: (NSDictionary *)options;
|
||||
|
||||
@end
|
||||
|
||||
@interface GSKeyValueOrBinding : GSKeyValueBinding
|
||||
@end
|
||||
|
||||
@interface GSKeyValueAndBinding : GSKeyValueBinding
|
||||
@end
|
||||
|
||||
#endif //_GS_BINDING_HELPER_H
|
||||
|
|
|
@ -29,50 +29,24 @@
|
|||
#include <Foundation/NSArray.h>
|
||||
#include <Foundation/NSDictionary.h>
|
||||
#include <Foundation/NSEnumerator.h>
|
||||
#include <Foundation/NSMapTable.h>
|
||||
#include <Foundation/NSLock.h>
|
||||
#include <Foundation/NSException.h>
|
||||
#include <Foundation/NSInvocation.h>
|
||||
#include <Foundation/NSKeyValueObserving.h>
|
||||
#include <Foundation/NSKeyValueCoding.h>
|
||||
#include <Foundation/NSLock.h>
|
||||
#include <Foundation/NSMapTable.h>
|
||||
#include <Foundation/NSValue.h>
|
||||
#include <Foundation/NSValueTransformer.h>
|
||||
#include <Foundation/NSInvocation.h>
|
||||
#include <Foundation/NSException.h>
|
||||
#include <GNUstepBase/GSLock.h>
|
||||
|
||||
#include "AppKit/NSKeyValueBinding.h"
|
||||
#include "GSBindingHelpers.h"
|
||||
|
||||
static NSRecursiveLock *bindingLock = nil;
|
||||
static NSMapTable *classTable = NULL; //available bindings
|
||||
static NSMapTable *objectTable = NULL; //bound bindings
|
||||
|
||||
static inline void setup()
|
||||
{
|
||||
if (bindingLock == nil)
|
||||
{
|
||||
bindingLock = [GSLazyRecursiveLock new];
|
||||
classTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||||
NSOwnedPointerMapValueCallBacks, 128);
|
||||
objectTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||||
NSOwnedPointerMapValueCallBacks, 128);
|
||||
}
|
||||
}
|
||||
|
||||
@implementation NSObject (NSKeyValueBindingCreation)
|
||||
|
||||
+ (void) exposeBinding: (NSString *)binding
|
||||
{
|
||||
NSMutableArray *bindings;
|
||||
|
||||
setup();
|
||||
[bindingLock lock];
|
||||
bindings = (NSMutableArray *)NSMapGet(classTable, (void*)self);
|
||||
if (bindings == nil)
|
||||
{
|
||||
bindings = [NSMutableArray arrayWithCapacity: 15];
|
||||
NSMapInsert(classTable, (void*)self, (void*)bindings);
|
||||
}
|
||||
[bindings addObject: binding];
|
||||
[bindingLock unlock];
|
||||
[GSKeyValueBinding exposeBinding: binding forClass: [self class]];
|
||||
}
|
||||
|
||||
- (NSArray *) exposedBindings
|
||||
|
@ -81,18 +55,16 @@ static inline void setup()
|
|||
NSArray *tmp;
|
||||
Class class = [self class];
|
||||
|
||||
setup();
|
||||
[bindingLock lock];
|
||||
while (class && class != [NSObject class])
|
||||
{
|
||||
tmp = NSMapGet(classTable, (void*)class);
|
||||
tmp = [GSKeyValueBinding exposedBindingsForClass: class];
|
||||
if (tmp != nil)
|
||||
{
|
||||
[exposedBindings addObjectsFromArray: tmp];
|
||||
}
|
||||
|
||||
class = [class superclass];
|
||||
}
|
||||
[bindingLock unlock];
|
||||
|
||||
return exposedBindings;
|
||||
}
|
||||
|
@ -107,36 +79,22 @@ static inline void setup()
|
|||
withKeyPath: (NSString *)keyPath
|
||||
options: (NSDictionary *)options
|
||||
{
|
||||
NSMutableDictionary *bindings;
|
||||
NSDictionary *info;
|
||||
id newValue;
|
||||
|
||||
if ((anObject == nil)
|
||||
|| (keyPath == nil))
|
||||
{
|
||||
NSLog(@"No object or path for binding on %@ for %@", self, binding);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([[self exposedBindings] containsObject: binding])
|
||||
{
|
||||
[self unbind: binding];
|
||||
|
||||
info = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
anObject, NSObservedObjectKey,
|
||||
keyPath, NSObservedKeyPathKey,
|
||||
options, NSOptionsKey,
|
||||
nil];
|
||||
[anObject addObserver: self
|
||||
forKeyPath: keyPath
|
||||
options: NSKeyValueObservingOptionNew
|
||||
context: binding];
|
||||
[bindingLock lock];
|
||||
bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)self);
|
||||
if (bindings == nil)
|
||||
{
|
||||
bindings = [NSMutableDictionary dictionary];
|
||||
NSMapInsert(objectTable, (void*)self, (void*)bindings);
|
||||
}
|
||||
[bindings setValue: info forKey: binding];
|
||||
[bindingLock unlock];
|
||||
|
||||
newValue = [anObject valueForKeyPath: keyPath];
|
||||
newValue = GSBindingTransformedValue(newValue, options);
|
||||
[self setValue: newValue forKey: binding];
|
||||
[[GSKeyValueBinding alloc] initWithBinding: binding
|
||||
withName: binding
|
||||
toObject: anObject
|
||||
withKeyPath: keyPath
|
||||
options: options
|
||||
fromObject: self];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -146,181 +104,133 @@ static inline void setup()
|
|||
|
||||
- (NSDictionary *) infoForBinding: (NSString *)binding
|
||||
{
|
||||
NSMutableDictionary *bindings;
|
||||
NSDictionary *info;
|
||||
|
||||
setup();
|
||||
[bindingLock lock];
|
||||
bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)self);
|
||||
if (bindings != nil)
|
||||
{
|
||||
info = [bindings objectForKey: binding];
|
||||
}
|
||||
[bindingLock unlock];
|
||||
return [[info copy] autorelease];
|
||||
return [GSKeyValueBinding infoForBinding: binding forObject: self];
|
||||
}
|
||||
|
||||
- (void) unbind: (NSString *)binding
|
||||
{
|
||||
[GSKeyValueBinding unbind: binding forObject: self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static NSRecursiveLock *bindingLock = nil;
|
||||
static NSMapTable *classTable = NULL; //available bindings
|
||||
static NSMapTable *objectTable = NULL; //bound bindings
|
||||
|
||||
typedef enum {
|
||||
GSBindingOperationAnd = 0,
|
||||
GSBindingOperationOr
|
||||
} GSBindingOperationKind;
|
||||
|
||||
//TODO: document
|
||||
BOOL GSBindingResolveMultipleValueBool(NSString *key, NSDictionary *bindings,
|
||||
GSBindingOperationKind operationKind);
|
||||
|
||||
//TODO: document
|
||||
void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey,
|
||||
NSDictionary *bindings);
|
||||
|
||||
NSArray *GSBindingExposeMultipleValueBindings(
|
||||
NSArray *bindingNames,
|
||||
NSMutableDictionary *bindingList);
|
||||
|
||||
NSArray *GSBindingExposePatternBindings(
|
||||
NSArray *bindingNames,
|
||||
NSMutableDictionary *bindingList);
|
||||
|
||||
id GSBindingReverseTransformedValue(id value, NSDictionary *options);
|
||||
|
||||
@implementation GSKeyValueBinding
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
if (self == [GSKeyValueBinding class])
|
||||
{
|
||||
bindingLock = [GSLazyRecursiveLock new];
|
||||
classTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||||
NSOwnedPointerMapValueCallBacks, 128);
|
||||
objectTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||||
NSOwnedPointerMapValueCallBacks, 128);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void) exposeBinding: (NSString *)binding forClass: (Class)clazz
|
||||
{
|
||||
NSMutableArray *bindings;
|
||||
|
||||
[bindingLock lock];
|
||||
bindings = (NSMutableArray *)NSMapGet(classTable, (void*)clazz);
|
||||
if (bindings == nil)
|
||||
{
|
||||
// Need to retain it ourselves
|
||||
bindings = [[NSMutableArray alloc] initWithCapacity: 5];
|
||||
NSMapInsert(classTable, (void*)clazz, (void*)bindings);
|
||||
}
|
||||
[bindings addObject: binding];
|
||||
[bindingLock unlock];
|
||||
}
|
||||
|
||||
+ (NSArray *) exposedBindingsForClass: (Class)clazz
|
||||
{
|
||||
NSArray *tmp;
|
||||
|
||||
if (!classTable)
|
||||
return nil;
|
||||
|
||||
[bindingLock lock];
|
||||
tmp = NSMapGet(classTable, (void*)clazz);
|
||||
[bindingLock unlock];
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
+ (NSDictionary *) infoForBinding: (NSString *)binding forObject: (id)anObject
|
||||
{
|
||||
NSMutableDictionary *bindings;
|
||||
GSKeyValueBinding *theBinding;
|
||||
|
||||
if (!objectTable)
|
||||
return nil;
|
||||
|
||||
[bindingLock lock];
|
||||
bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)anObject);
|
||||
if (bindings != nil)
|
||||
{
|
||||
theBinding = (GSKeyValueBinding*)[bindings objectForKey: binding];
|
||||
}
|
||||
[bindingLock unlock];
|
||||
|
||||
return theBinding->info;
|
||||
}
|
||||
|
||||
+ (void) unbind: (NSString *)binding forObject: (id)anObject
|
||||
{
|
||||
NSMutableDictionary *bindings;
|
||||
NSDictionary *info;
|
||||
id observedObject;
|
||||
NSString *keyPath;
|
||||
GSKeyValueBinding *theBinding;
|
||||
|
||||
if (!objectTable)
|
||||
return;
|
||||
|
||||
[bindingLock lock];
|
||||
bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)self);
|
||||
bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)anObject);
|
||||
if (bindings != nil)
|
||||
{
|
||||
info = [bindings objectForKey: binding];
|
||||
if (info != nil)
|
||||
theBinding = (GSKeyValueBinding*)[bindings objectForKey: binding];
|
||||
if (theBinding != nil)
|
||||
{
|
||||
observedObject = [info objectForKey: NSObservedObjectKey];
|
||||
keyPath = [info objectForKey: NSObservedKeyPathKey];
|
||||
[observedObject removeObserver: self forKeyPath: keyPath];
|
||||
observedObject = [theBinding->info objectForKey: NSObservedObjectKey];
|
||||
keyPath = [theBinding->info objectForKey: NSObservedKeyPathKey];
|
||||
[observedObject removeObserver: theBinding forKeyPath: keyPath];
|
||||
[bindings setValue: nil forKey: binding];
|
||||
}
|
||||
}
|
||||
[bindingLock unlock];
|
||||
}
|
||||
|
||||
// FIXME: This method should not be defined on this class, as it make all
|
||||
// other value observation impossible. Better add a new GSBinding class
|
||||
// to handle this. Perhaps with plenty of specific subclasses for the
|
||||
// different special cases?
|
||||
- (void) observeValueForKeyPath: (NSString *)keyPath
|
||||
ofObject: (id)object
|
||||
change: (NSDictionary *)change
|
||||
context: (void *)context
|
||||
{
|
||||
NSMutableDictionary *bindings;
|
||||
NSString *binding = (NSString *)context;
|
||||
|
||||
setup();
|
||||
[bindingLock lock];
|
||||
bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)self);
|
||||
if (bindings != nil)
|
||||
{
|
||||
NSDictionary *info;
|
||||
|
||||
info = [bindings objectForKey: binding];
|
||||
if (info != nil)
|
||||
{
|
||||
NSDictionary *options;
|
||||
id newValue;
|
||||
|
||||
options = [info objectForKey: NSOptionsKey];
|
||||
newValue = [change objectForKey: NSKeyValueChangeNewKey];
|
||||
newValue = GSBindingTransformedValue(newValue, options);
|
||||
[self setValue: newValue forKey: binding];
|
||||
}
|
||||
}
|
||||
[bindingLock unlock];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
//Helper functions
|
||||
BOOL GSBindingResolveMultipleValueBool(NSString *key, NSDictionary *bindings,
|
||||
GSBindingOperationKind operationKind)
|
||||
{
|
||||
NSString *bindingName;
|
||||
NSDictionary *info;
|
||||
int count = 1;
|
||||
id object;
|
||||
NSString *keyPath;
|
||||
id value;
|
||||
NSDictionary *options;
|
||||
|
||||
bindingName = key;
|
||||
while ((info = [bindings objectForKey: bindingName]))
|
||||
{
|
||||
object = [info objectForKey: NSObservedObjectKey];
|
||||
keyPath = [info objectForKey: NSObservedKeyPathKey];
|
||||
options = [info objectForKey: NSOptionsKey];
|
||||
|
||||
value = [object valueForKeyPath: keyPath];
|
||||
value = GSBindingTransformedValue(value, options);
|
||||
if ([value boolValue] == operationKind)
|
||||
{
|
||||
return operationKind;
|
||||
}
|
||||
bindingName = [NSString stringWithFormat: @"%@%i", key, ++count];
|
||||
}
|
||||
return !operationKind;
|
||||
}
|
||||
|
||||
void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey,
|
||||
NSDictionary *bindings)
|
||||
{
|
||||
NSString *bindingName;
|
||||
NSDictionary *info;
|
||||
NSDictionary *options;
|
||||
int count = 1;
|
||||
id object;
|
||||
id target;
|
||||
SEL selector;
|
||||
NSString *keyPath;
|
||||
NSInvocation *invocation;
|
||||
|
||||
info = [bindings objectForKey: targetKey];
|
||||
object = [info objectForKey: NSObservedObjectKey];
|
||||
keyPath = [info objectForKey: NSObservedKeyPathKey];
|
||||
options = [info objectForKey: NSOptionsKey];
|
||||
|
||||
target = [object valueForKeyPath: keyPath];
|
||||
selector = NSSelectorFromString([options objectForKey:
|
||||
NSSelectorNameBindingOption]);
|
||||
if (target == nil || selector == NULL) return;
|
||||
|
||||
invocation = [NSInvocation invocationWithMethodSignature:
|
||||
[target methodSignatureForSelector: selector]];
|
||||
[invocation setSelector: selector];
|
||||
|
||||
bindingName = argumentKey;
|
||||
while ((info = [bindings objectForKey: bindingName]))
|
||||
{
|
||||
object = [info objectForKey: NSObservedObjectKey];
|
||||
keyPath = [info objectForKey: NSObservedKeyPathKey];
|
||||
if ((object = [object valueForKeyPath: keyPath]))
|
||||
{
|
||||
[invocation setArgument: object atIndex: ++count];
|
||||
}
|
||||
bindingName = [NSString stringWithFormat: @"%@%i", argumentKey, count];
|
||||
}
|
||||
[invocation invoke];
|
||||
}
|
||||
|
||||
void GSBindingLock()
|
||||
{
|
||||
[bindingLock lock];
|
||||
}
|
||||
|
||||
void GSBindingReleaseLock()
|
||||
{
|
||||
[bindingLock unlock];
|
||||
}
|
||||
|
||||
NSMutableDictionary *GSBindingListForObject(id object)
|
||||
{
|
||||
NSMutableDictionary *list;
|
||||
|
||||
if (!objectTable)
|
||||
return nil;
|
||||
|
||||
list = (NSMutableDictionary *)NSMapGet(objectTable, (void *)object);
|
||||
if (list == nil)
|
||||
{
|
||||
list = [NSMutableDictionary dictionary];
|
||||
NSMapInsert(objectTable, (void *)object, (void *)list);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void GSBindingUnbindAll(id object)
|
||||
+ (void) unbindAllForObject: (id)anObject
|
||||
{
|
||||
NSEnumerator *enumerator;
|
||||
NSString *binding;
|
||||
|
@ -330,71 +240,105 @@ void GSBindingUnbindAll(id object)
|
|||
return;
|
||||
|
||||
[bindingLock lock];
|
||||
list = (NSDictionary *)NSMapGet(objectTable, (void *)object);
|
||||
list = (NSDictionary *)NSMapGet(objectTable, (void *)anObject);
|
||||
if (list != nil)
|
||||
{
|
||||
enumerator = [list keyEnumerator];
|
||||
while ((binding = [enumerator nextObject]))
|
||||
{
|
||||
[object unbind: binding];
|
||||
[anObject unbind: binding];
|
||||
}
|
||||
NSMapRemove(objectTable, (void *)object);
|
||||
NSMapRemove(objectTable, (void *)anObject);
|
||||
RELEASE(list);
|
||||
}
|
||||
[bindingLock unlock];
|
||||
}
|
||||
|
||||
- (id) initWithBinding: (NSString *)binding
|
||||
withName: (NSString *)name
|
||||
toObject: (id)dest
|
||||
withKeyPath: (NSString *)keyPath
|
||||
options: (NSDictionary *)options
|
||||
fromObject: (id)source
|
||||
{
|
||||
NSMutableDictionary *bindings;
|
||||
|
||||
NSArray *GSBindingExposeMultipleValueBindings(
|
||||
NSArray *bindingNames,
|
||||
NSMutableDictionary *bindingList)
|
||||
{
|
||||
NSEnumerator *nameEnum;
|
||||
NSString *name;
|
||||
NSString *numberedName;
|
||||
NSMutableArray *additionalBindings;
|
||||
int count;
|
||||
|
||||
additionalBindings = [NSMutableArray array];
|
||||
nameEnum = [bindingNames objectEnumerator];
|
||||
while ((name = [nameEnum nextObject]))
|
||||
src = source;
|
||||
if (options == nil)
|
||||
{
|
||||
count = 1;
|
||||
numberedName = name;
|
||||
while ([bindingList objectForKey: numberedName] != nil)
|
||||
{
|
||||
numberedName = [NSString stringWithFormat: @"%@%i", name, ++count];
|
||||
[additionalBindings addObject: numberedName];
|
||||
}
|
||||
info = [[NSDictionary alloc] initWithObjectsAndKeys:
|
||||
dest, NSObservedObjectKey,
|
||||
keyPath, NSObservedKeyPathKey,
|
||||
nil];
|
||||
}
|
||||
return additionalBindings;
|
||||
else
|
||||
{
|
||||
info = [[NSDictionary alloc] initWithObjectsAndKeys:
|
||||
dest, NSObservedObjectKey,
|
||||
keyPath, NSObservedKeyPathKey,
|
||||
options, NSOptionsKey,
|
||||
nil];
|
||||
}
|
||||
|
||||
[dest addObserver: self
|
||||
forKeyPath: keyPath
|
||||
options: NSKeyValueObservingOptionNew
|
||||
context: binding];
|
||||
|
||||
[bindingLock lock];
|
||||
bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)source);
|
||||
if (bindings == nil)
|
||||
{
|
||||
bindings = [NSMutableDictionary new];
|
||||
NSMapInsert(objectTable, (void*)source, (void*)bindings);
|
||||
}
|
||||
[bindings setObject: self forKey: name];
|
||||
[bindingLock unlock];
|
||||
|
||||
[self setValueFor: binding];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
NSArray *GSBindingExposePatternBindings(
|
||||
NSArray *bindingNames,
|
||||
NSMutableDictionary *bindingList)
|
||||
- (void)dealloc
|
||||
{
|
||||
NSEnumerator *nameEnum;
|
||||
NSString *name;
|
||||
NSString *numberedName;
|
||||
NSMutableArray *additionalBindings;
|
||||
int count;
|
||||
|
||||
additionalBindings = [NSMutableArray array];
|
||||
nameEnum = [bindingNames objectEnumerator];
|
||||
while ((name = [nameEnum nextObject]))
|
||||
{
|
||||
count = 1;
|
||||
numberedName = [NSString stringWithFormat:@"%@1", name];
|
||||
while ([bindingList objectForKey: numberedName] != nil)
|
||||
{
|
||||
numberedName = [NSString stringWithFormat:@"%@%i", name, ++count];
|
||||
[additionalBindings addObject: numberedName];
|
||||
}
|
||||
}
|
||||
return additionalBindings;
|
||||
DESTROY(info);
|
||||
src = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
id GSBindingTransformedValue(id value, NSDictionary *options)
|
||||
- (void) setValueFor: (NSString *)binding
|
||||
{
|
||||
id newValue;
|
||||
id dest;
|
||||
NSString *keyPath;
|
||||
NSDictionary *options;
|
||||
|
||||
dest = [info objectForKey: NSObservedObjectKey];
|
||||
keyPath = [info objectForKey: NSObservedKeyPathKey];
|
||||
options = [info objectForKey: NSOptionsKey];
|
||||
|
||||
newValue = [dest valueForKeyPath: keyPath];
|
||||
newValue = [self transformValue: newValue withOptions: options];
|
||||
[src setValue: newValue forKey: binding];
|
||||
}
|
||||
|
||||
- (void) observeValueForKeyPath: (NSString *)keyPath
|
||||
ofObject: (id)object
|
||||
change: (NSDictionary *)change
|
||||
context: (void *)context
|
||||
{
|
||||
NSString *binding = (NSString *)context;
|
||||
NSDictionary *options;
|
||||
id newValue;
|
||||
|
||||
options = [info objectForKey: NSOptionsKey];
|
||||
newValue = [change objectForKey: NSKeyValueChangeNewKey];
|
||||
newValue = [self transformValue: newValue withOptions: options];
|
||||
[src setValue: newValue forKey: binding];
|
||||
}
|
||||
|
||||
- (id) transformValue: (id)value withOptions: (NSDictionary *)options
|
||||
{
|
||||
NSString *valueTransformerName;
|
||||
NSValueTransformer *valueTransformer;
|
||||
|
@ -466,7 +410,225 @@ id GSBindingTransformedValue(id value, NSDictionary *options)
|
|||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation GSKeyValueOrBinding : GSKeyValueBinding
|
||||
|
||||
- (void) setValueFor: (NSString *)binding
|
||||
{
|
||||
NSDictionary *bindings;
|
||||
BOOL res;
|
||||
|
||||
if (!objectTable)
|
||||
return;
|
||||
|
||||
[bindingLock lock];
|
||||
bindings = (NSDictionary *)NSMapGet(objectTable, (void *)src);
|
||||
if (!bindings)
|
||||
return;
|
||||
|
||||
res = GSBindingResolveMultipleValueBool(binding, bindings,
|
||||
GSBindingOperationOr);
|
||||
[bindingLock unlock];
|
||||
[src setValue: [NSNumber numberWithBool: res] forKey: binding];
|
||||
}
|
||||
|
||||
- (void) observeValueForKeyPath: (NSString *)keyPath
|
||||
ofObject: (id)object
|
||||
change: (NSDictionary *)change
|
||||
context: (void *)context
|
||||
{
|
||||
[self setValueFor: (NSString*)context];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GSKeyValueAndBinding : GSKeyValueBinding
|
||||
|
||||
- (void) setValueFor: (NSString *)binding
|
||||
{
|
||||
NSDictionary *bindings;
|
||||
BOOL res;
|
||||
|
||||
if (!objectTable)
|
||||
return;
|
||||
|
||||
[bindingLock lock];
|
||||
bindings = (NSDictionary *)NSMapGet(objectTable, (void *)src);
|
||||
if (!bindings)
|
||||
return;
|
||||
|
||||
res = GSBindingResolveMultipleValueBool(binding, bindings,
|
||||
GSBindingOperationAnd);
|
||||
[bindingLock unlock];
|
||||
[src setValue: [NSNumber numberWithBool: res] forKey: binding];
|
||||
}
|
||||
|
||||
- (void) observeValueForKeyPath: (NSString *)keyPath
|
||||
ofObject: (id)object
|
||||
change: (NSDictionary *)change
|
||||
context: (void *)context
|
||||
{
|
||||
[self setValueFor: (NSString*)context];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
//Helper functions
|
||||
BOOL GSBindingResolveMultipleValueBool(NSString *key, NSDictionary *bindings,
|
||||
GSBindingOperationKind operationKind)
|
||||
{
|
||||
NSString *bindingName;
|
||||
NSDictionary *info;
|
||||
int count = 1;
|
||||
id object;
|
||||
NSString *keyPath;
|
||||
id value;
|
||||
NSDictionary *options;
|
||||
GSKeyValueBinding *theBinding;
|
||||
|
||||
bindingName = key;
|
||||
while ((theBinding = [bindings objectForKey: bindingName]))
|
||||
{
|
||||
info = theBinding->info;
|
||||
object = [info objectForKey: NSObservedObjectKey];
|
||||
keyPath = [info objectForKey: NSObservedKeyPathKey];
|
||||
options = [info objectForKey: NSOptionsKey];
|
||||
|
||||
value = [object valueForKeyPath: keyPath];
|
||||
value = [theBinding transformValue: value withOptions: options];
|
||||
if ([value boolValue] == operationKind)
|
||||
{
|
||||
return operationKind;
|
||||
}
|
||||
bindingName = [NSString stringWithFormat: @"%@%i", key, ++count];
|
||||
}
|
||||
return !operationKind;
|
||||
}
|
||||
|
||||
void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey,
|
||||
NSDictionary *bindings)
|
||||
{
|
||||
NSString *bindingName;
|
||||
NSDictionary *info;
|
||||
NSDictionary *options;
|
||||
int count = 1;
|
||||
id object;
|
||||
id target;
|
||||
SEL selector;
|
||||
NSString *keyPath;
|
||||
NSInvocation *invocation;
|
||||
GSKeyValueBinding *theBinding;
|
||||
|
||||
theBinding = [bindings objectForKey: targetKey];
|
||||
info = theBinding->info;
|
||||
object = [info objectForKey: NSObservedObjectKey];
|
||||
keyPath = [info objectForKey: NSObservedKeyPathKey];
|
||||
options = [info objectForKey: NSOptionsKey];
|
||||
|
||||
target = [object valueForKeyPath: keyPath];
|
||||
selector = NSSelectorFromString([options objectForKey:
|
||||
NSSelectorNameBindingOption]);
|
||||
if (target == nil || selector == NULL) return;
|
||||
|
||||
invocation = [NSInvocation invocationWithMethodSignature:
|
||||
[target methodSignatureForSelector: selector]];
|
||||
[invocation setSelector: selector];
|
||||
|
||||
bindingName = argumentKey;
|
||||
while ((theBinding = [bindings objectForKey: bindingName]))
|
||||
{
|
||||
info = theBinding->info;
|
||||
object = [info objectForKey: NSObservedObjectKey];
|
||||
keyPath = [info objectForKey: NSObservedKeyPathKey];
|
||||
if ((object = [object valueForKeyPath: keyPath]))
|
||||
{
|
||||
[invocation setArgument: object atIndex: ++count];
|
||||
}
|
||||
bindingName = [NSString stringWithFormat: @"%@%i", argumentKey, count];
|
||||
}
|
||||
[invocation invoke];
|
||||
}
|
||||
|
||||
void GSBindingLock()
|
||||
{
|
||||
[bindingLock lock];
|
||||
}
|
||||
|
||||
void GSBindingReleaseLock()
|
||||
{
|
||||
[bindingLock unlock];
|
||||
}
|
||||
|
||||
NSMutableDictionary *GSBindingListForObject(id object)
|
||||
{
|
||||
NSMutableDictionary *list;
|
||||
|
||||
if (!objectTable)
|
||||
return nil;
|
||||
|
||||
list = (NSMutableDictionary *)NSMapGet(objectTable, (void *)object);
|
||||
if (list == nil)
|
||||
{
|
||||
list = [NSMutableDictionary new];
|
||||
NSMapInsert(objectTable, (void *)object, (void *)list);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
NSArray *GSBindingExposeMultipleValueBindings(
|
||||
NSArray *bindingNames,
|
||||
NSMutableDictionary *bindingList)
|
||||
{
|
||||
NSEnumerator *nameEnum;
|
||||
NSString *name;
|
||||
NSString *numberedName;
|
||||
NSMutableArray *additionalBindings;
|
||||
int count;
|
||||
|
||||
additionalBindings = [NSMutableArray array];
|
||||
nameEnum = [bindingNames objectEnumerator];
|
||||
while ((name = [nameEnum nextObject]))
|
||||
{
|
||||
count = 1;
|
||||
numberedName = name;
|
||||
while ([bindingList objectForKey: numberedName] != nil)
|
||||
{
|
||||
numberedName = [NSString stringWithFormat: @"%@%i", name, ++count];
|
||||
[additionalBindings addObject: numberedName];
|
||||
}
|
||||
}
|
||||
return additionalBindings;
|
||||
}
|
||||
|
||||
|
||||
NSArray *GSBindingExposePatternBindings(
|
||||
NSArray *bindingNames,
|
||||
NSMutableDictionary *bindingList)
|
||||
{
|
||||
NSEnumerator *nameEnum;
|
||||
NSString *name;
|
||||
NSString *numberedName;
|
||||
NSMutableArray *additionalBindings;
|
||||
int count;
|
||||
|
||||
additionalBindings = [NSMutableArray array];
|
||||
nameEnum = [bindingNames objectEnumerator];
|
||||
while ((name = [nameEnum nextObject]))
|
||||
{
|
||||
count = 1;
|
||||
numberedName = [NSString stringWithFormat:@"%@1", name];
|
||||
while ([bindingList objectForKey: numberedName] != nil)
|
||||
{
|
||||
numberedName = [NSString stringWithFormat:@"%@%i", name, ++count];
|
||||
[additionalBindings addObject: numberedName];
|
||||
}
|
||||
}
|
||||
return additionalBindings;
|
||||
}
|
||||
|
||||
id GSBindingReverseTransformedValue(id value, NSDictionary *options)
|
||||
{
|
||||
NSValueTransformer *valueTransformer;
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
#include <Foundation/NSNotification.h>
|
||||
#include <Foundation/NSString.h>
|
||||
#include <Foundation/NSValue.h>
|
||||
#include <Foundation/NSKeyValueObserving.h>
|
||||
|
||||
#include "AppKit/NSApplication.h"
|
||||
#include "AppKit/NSCursor.h"
|
||||
|
@ -753,98 +752,38 @@ static Class textFieldCellClass;
|
|||
*/
|
||||
|
||||
- (void) bind: (NSString *)binding
|
||||
toObject: (id)object
|
||||
toObject: (id)anObject
|
||||
withKeyPath: (NSString *)keyPath
|
||||
options: (NSDictionary *)options
|
||||
{
|
||||
NSMutableDictionary *bindings;
|
||||
NSDictionary *info;
|
||||
BOOL bVal;
|
||||
|
||||
if ([binding hasPrefix: NSEditableBinding])
|
||||
{
|
||||
[self unbind: binding];
|
||||
|
||||
[object addObserver: self
|
||||
forKeyPath: keyPath
|
||||
options: 0
|
||||
context: NSEditableBinding];
|
||||
info = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
object, NSObservedObjectKey,
|
||||
keyPath, NSObservedKeyPathKey,
|
||||
options, NSOptionsKey,
|
||||
nil];
|
||||
GSBindingLock();
|
||||
bindings = GSBindingListForObject(self);
|
||||
[bindings setValue: info forKey: binding];
|
||||
bVal = GSBindingResolveMultipleValueBool(NSHiddenBinding, bindings,
|
||||
GSBindingOperationAnd);
|
||||
GSBindingReleaseLock();
|
||||
[self setEditable: bVal];
|
||||
[[GSKeyValueAndBinding alloc] initWithBinding: NSEditableBinding
|
||||
withName: binding
|
||||
toObject: anObject
|
||||
withKeyPath: keyPath
|
||||
options: options
|
||||
fromObject: self];
|
||||
}
|
||||
else if ([binding hasPrefix: NSEnabledBinding])
|
||||
{
|
||||
[object addObserver: self
|
||||
forKeyPath: keyPath
|
||||
options: 0
|
||||
context: NSEnabledBinding];
|
||||
info = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
object, NSObservedObjectKey,
|
||||
keyPath, NSObservedKeyPathKey,
|
||||
options, NSOptionsKey,
|
||||
nil];
|
||||
[self unbind: binding];
|
||||
GSBindingLock();
|
||||
bindings = GSBindingListForObject(self);
|
||||
[bindings setValue: info forKey: binding];
|
||||
bVal = GSBindingResolveMultipleValueBool(NSHiddenBinding, bindings,
|
||||
GSBindingOperationAnd);
|
||||
GSBindingReleaseLock();
|
||||
[self setEnabled: bVal];
|
||||
[[GSKeyValueAndBinding alloc] initWithBinding: NSEnabledBinding
|
||||
withName: binding
|
||||
toObject: anObject
|
||||
withKeyPath: keyPath
|
||||
options: options
|
||||
fromObject: self];
|
||||
}
|
||||
else
|
||||
{
|
||||
[super bind: binding
|
||||
toObject: object
|
||||
toObject: anObject
|
||||
withKeyPath: keyPath
|
||||
options: options];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) observeValueForKeyPath: (NSString *)keyPath
|
||||
ofObject: (id)object
|
||||
change: (NSDictionary *)change
|
||||
context: (void *)context
|
||||
{
|
||||
BOOL bVal;
|
||||
NSDictionary *bindings;
|
||||
|
||||
if (context == NSEditableBinding)
|
||||
{
|
||||
GSBindingLock();
|
||||
bindings = GSBindingListForObject(self);
|
||||
bVal = GSBindingResolveMultipleValueBool(NSEditableBinding, bindings,
|
||||
GSBindingOperationAnd);
|
||||
GSBindingReleaseLock();
|
||||
[self setEditable: bVal];
|
||||
}
|
||||
else if (context == NSEnabledBinding)
|
||||
{
|
||||
GSBindingLock();
|
||||
bindings = GSBindingListForObject(self);
|
||||
bVal = GSBindingResolveMultipleValueBool(NSEnabledBinding, bindings,
|
||||
GSBindingOperationAnd);
|
||||
GSBindingReleaseLock();
|
||||
[self setEnabled: bVal];
|
||||
}
|
||||
else
|
||||
{
|
||||
[super observeValueForKeyPath: keyPath
|
||||
ofObject: object
|
||||
change: change
|
||||
context: context];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include <Foundation/NSCalendarDate.h>
|
||||
#include <Foundation/NSCoder.h>
|
||||
#include <Foundation/NSKeyedArchiver.h>
|
||||
#include <Foundation/NSKeyValueObserving.h>
|
||||
#include <Foundation/NSDictionary.h>
|
||||
#include <Foundation/NSThread.h>
|
||||
#include <Foundation/NSLock.h>
|
||||
|
@ -511,7 +510,8 @@ GSSetDragTypes(NSView* obj, NSArray *types)
|
|||
NSView *tmp;
|
||||
unsigned count;
|
||||
|
||||
GSBindingUnbindAll(self);
|
||||
// Remove all key value bindings for this view.
|
||||
[GSKeyValueBinding unbindAllForObject: self];
|
||||
|
||||
while ([_sub_views count] > 0)
|
||||
{
|
||||
|
@ -4728,69 +4728,28 @@ static NSView* findByTag(NSView *view, int aTag, unsigned *level)
|
|||
}
|
||||
|
||||
- (void) bind: (NSString *)binding
|
||||
toObject: (id)object
|
||||
toObject: (id)anObject
|
||||
withKeyPath: (NSString *)keyPath
|
||||
options: (NSDictionary *)options
|
||||
{
|
||||
NSMutableDictionary *bindings;
|
||||
NSDictionary *info;
|
||||
BOOL hidden;
|
||||
|
||||
if ([binding hasPrefix: NSHiddenBinding])
|
||||
{
|
||||
[self unbind: binding];
|
||||
|
||||
[object addObserver: self
|
||||
forKeyPath: keyPath
|
||||
options: 0
|
||||
context: NSHiddenBinding];
|
||||
info = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
object, NSObservedObjectKey,
|
||||
keyPath, NSObservedKeyPathKey,
|
||||
options, NSOptionsKey,
|
||||
nil];
|
||||
GSBindingLock();
|
||||
bindings = GSBindingListForObject(self);
|
||||
[bindings setValue: info forKey: binding];
|
||||
hidden = GSBindingResolveMultipleValueBool(NSHiddenBinding, bindings,
|
||||
GSBindingOperationOr);
|
||||
GSBindingReleaseLock();
|
||||
[self setHidden: hidden];
|
||||
[[GSKeyValueOrBinding alloc] initWithBinding: NSHiddenBinding
|
||||
withName: binding
|
||||
toObject: anObject
|
||||
withKeyPath: keyPath
|
||||
options: options
|
||||
fromObject: self];
|
||||
}
|
||||
else
|
||||
{
|
||||
[super bind: binding
|
||||
toObject: object
|
||||
toObject: anObject
|
||||
withKeyPath: keyPath
|
||||
options: options];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) observeValueForKeyPath: (NSString *)keyPath
|
||||
ofObject: (id)object
|
||||
change: (NSDictionary *)change
|
||||
context: (void *)context
|
||||
{
|
||||
BOOL hidden;
|
||||
NSDictionary * bindings;
|
||||
|
||||
if (context == NSHiddenBinding)
|
||||
{
|
||||
GSBindingLock();
|
||||
bindings = GSBindingListForObject(self);
|
||||
hidden = GSBindingResolveMultipleValueBool(NSHiddenBinding, bindings,
|
||||
GSBindingOperationOr);
|
||||
GSBindingReleaseLock();
|
||||
[self setHidden: hidden];
|
||||
}
|
||||
else
|
||||
{
|
||||
[super observeValueForKeyPath: keyPath
|
||||
ofObject: object
|
||||
change: change
|
||||
context: context];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
Loading…
Reference in a new issue