1/** <title>NSKeyValueBinding informal protocol reference</title> 2 3 Implementation of KeyValueBinding for GNUStep 4 5 Copyright (C) 2007 Free Software Foundation, Inc. 6 7 Written by: Chris Farber <chris@chrisfarber.net> 8 Date: 2007 9 Author: Fred Kiefer <fredkiefer@gmx.de> 10 Date: Decembre 2007 11 12 This file is part of the GNUstep GUI Library. 13 14 This library is free software; you can redistribute it and/or 15 modify it under the terms of the GNU Lesser General Public 16 License as published by the Free Software Foundation; either 17 version 2 of the License, or (at your option) any later version. 18 19 This library is distributed in the hope that it will be useful, 20 but WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 Lesser General Public License for more details. 23 24 You should have received a copy of the GNU Lesser General Public 25 License along with this library; see the file COPYING.LIB. 26 If not, see <http://www.gnu.org/licenses/> or write to the 27 Free Software Foundation, 51 Franklin Street, Fifth Floor, 28 Boston, MA 02110-1301, USA. 29*/ 30 31#import <Foundation/NSArray.h> 32#import <Foundation/NSDebug.h> 33#import <Foundation/NSDictionary.h> 34#import <Foundation/NSEnumerator.h> 35#import <Foundation/NSException.h> 36#import <Foundation/NSInvocation.h> 37#import <Foundation/NSKeyValueObserving.h> 38#import <Foundation/NSKeyValueCoding.h> 39#import <Foundation/NSLock.h> 40#import <Foundation/NSMapTable.h> 41#import <Foundation/NSValue.h> 42#import <Foundation/NSValueTransformer.h> 43#import <GNUstepBase/GSLock.h> 44 45#import "AppKit/NSKeyValueBinding.h" 46#import "GSBindingHelpers.h" 47#import "GSFastEnumeration.h" 48 49@implementation NSObject (NSKeyValueBindingCreation) 50 51+ (void) exposeBinding: (NSString *)binding 52{ 53 [GSKeyValueBinding exposeBinding: binding forClass: [self class]]; 54} 55 56- (NSArray *) exposedBindings 57{ 58 NSMutableArray *exposedBindings = [NSMutableArray array]; 59 NSArray *tmp; 60 Class class = [self class]; 61 62 while (class && class != [NSObject class]) 63 { 64 tmp = [GSKeyValueBinding exposedBindingsForClass: class]; 65 if (tmp != nil) 66 { 67 [exposedBindings addObjectsFromArray: tmp]; 68 } 69 70 class = [class superclass]; 71 } 72 73 return exposedBindings; 74} 75 76- (Class) valueClassForBinding: (NSString *)binding 77{ 78 return [NSString class]; 79} 80 81- (void) bind: (NSString *)binding 82 toObject: (id)anObject 83 withKeyPath: (NSString *)keyPath 84 options: (NSDictionary *)options 85{ 86 if ((anObject == nil) 87 || (keyPath == nil)) 88 { 89 NSLog(@"No object or path for binding on %@ for %@", self, binding); 90 return; 91 } 92 93 if ([[self exposedBindings] containsObject: binding]) 94 { 95 GSKeyValueBinding *kvb; 96 97 [self unbind: binding]; 98 kvb = [[GSKeyValueBinding alloc] initWithBinding: binding 99 withName: binding 100 toObject: anObject 101 withKeyPath: keyPath 102 options: options 103 fromObject: self]; 104 // The binding will be retained in the binding table 105 RELEASE(kvb); 106 } 107 else 108 { 109 NSLog(@"No binding exposed on %@ for %@", self, binding); 110 } 111} 112 113- (NSDictionary *) infoForBinding: (NSString *)binding 114{ 115 return [GSKeyValueBinding infoForBinding: binding forObject: self]; 116} 117 118- (void) unbind: (NSString *)binding 119{ 120 [GSKeyValueBinding unbind: binding forObject: self]; 121} 122 123@end 124 125static NSRecursiveLock *bindingLock = nil; 126static NSMapTable *classTable = NULL; //available bindings 127static NSMapTable *objectTable = NULL; //bound bindings 128 129typedef enum { 130 GSBindingOperationAnd = 0, 131 GSBindingOperationOr 132} GSBindingOperationKind; 133 134//TODO: document 135BOOL GSBindingResolveMultipleValueBool(NSString *key, NSDictionary *bindings, 136 GSBindingOperationKind operationKind); 137 138//TODO: document 139void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey, 140 NSDictionary *bindings); 141 142@implementation GSKeyValueBinding 143 144+ (void) initialize 145{ 146 if (self == [GSKeyValueBinding class]) 147 { 148 bindingLock = [GSLazyRecursiveLock new]; 149 classTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, 150 NSObjectMapValueCallBacks, 128); 151 objectTable = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, 152 NSObjectMapValueCallBacks, 128); 153 } 154} 155 156+ (void) exposeBinding: (NSString *)binding forClass: (Class)clazz 157{ 158 NSMutableArray *bindings; 159 160 [bindingLock lock]; 161 bindings = (NSMutableArray *)NSMapGet(classTable, (void*)clazz); 162 if (bindings == nil) 163 { 164 bindings = [[NSMutableArray alloc] initWithCapacity: 5]; 165 NSMapInsert(classTable, (void*)clazz, (void*)bindings); 166 RELEASE(bindings); 167 } 168 [bindings addObject: binding]; 169 [bindingLock unlock]; 170} 171 172+ (NSArray *) exposedBindingsForClass: (Class)clazz 173{ 174 NSArray *tmp; 175 176 if (!classTable) 177 return nil; 178 179 [bindingLock lock]; 180 tmp = NSMapGet(classTable, (void*)clazz); 181 [bindingLock unlock]; 182 183 return tmp; 184} 185 186+ (GSKeyValueBinding *) getBinding: (NSString *)binding 187 forObject: (id)anObject 188{ 189 NSMutableDictionary *bindings; 190 GSKeyValueBinding *theBinding = nil; 191 192 if (!objectTable) 193 return nil; 194 195 [bindingLock lock]; 196 bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)anObject); 197 if (bindings != nil) 198 { 199 theBinding = (GSKeyValueBinding*)[bindings objectForKey: binding]; 200 } 201 [bindingLock unlock]; 202 203 return theBinding; 204} 205 206+ (NSDictionary *) infoForBinding: (NSString *)binding 207 forObject: (id)anObject 208{ 209 GSKeyValueBinding *theBinding; 210 211 theBinding = [self getBinding: binding forObject: anObject]; 212 if (theBinding == nil) 213 return nil; 214 215 return theBinding->info; 216} 217 218+ (void) unbind: (NSString *)binding 219 forObject: (id)anObject 220{ 221 NSMutableDictionary *bindings; 222 id observedObject; 223 NSString *keyPath; 224 GSKeyValueBinding *theBinding; 225 226 if (!objectTable) 227 return; 228 229 [bindingLock lock]; 230 bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)anObject); 231 if (bindings != nil) 232 { 233 theBinding = (GSKeyValueBinding*)[bindings objectForKey: binding]; 234 if (theBinding != nil) 235 { 236 observedObject = [theBinding->info objectForKey: NSObservedObjectKey]; 237 keyPath = [theBinding->info objectForKey: NSObservedKeyPathKey]; 238 [observedObject removeObserver: theBinding forKeyPath: keyPath]; 239 [bindings setValue: nil forKey: binding]; 240 } 241 } 242 [bindingLock unlock]; 243} 244 245+ (void) unbindAllForObject: (id)anObject 246{ 247 NSEnumerator *enumerator; 248 NSString *binding; 249 NSDictionary *list; 250 251 if (!objectTable) 252 return; 253 254 [bindingLock lock]; 255 list = (NSDictionary *)NSMapGet(objectTable, (void *)anObject); 256 if (list != nil) 257 { 258 NSArray *keys = [list allKeys]; 259 260 enumerator = [keys objectEnumerator]; 261 while ((binding = [enumerator nextObject])) 262 { 263 [anObject unbind: binding]; 264 } 265 NSMapRemove(objectTable, (void *)anObject); 266 } 267 [bindingLock unlock]; 268} 269 270- (id) initWithBinding: (NSString *)binding 271 withName: (NSString *)name 272 toObject: (id)dest 273 withKeyPath: (NSString *)keyPath 274 options: (NSDictionary *)options 275 fromObject: (id)source 276{ 277 NSMutableDictionary *bindings; 278 279 src = source; 280 if (options == nil) 281 { 282 info = [[NSDictionary alloc] initWithObjectsAndKeys: 283 dest, NSObservedObjectKey, 284 keyPath, NSObservedKeyPathKey, 285 nil]; 286 } 287 else 288 { 289 info = [[NSDictionary alloc] initWithObjectsAndKeys: 290 dest, NSObservedObjectKey, 291 keyPath, NSObservedKeyPathKey, 292 options, NSOptionsKey, 293 nil]; 294 } 295 296 [dest addObserver: self 297 forKeyPath: keyPath 298 options: NSKeyValueObservingOptionNew 299 context: binding]; 300 301 [bindingLock lock]; 302 bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)source); 303 if (bindings == nil) 304 { 305 bindings = [NSMutableDictionary new]; 306 NSMapInsert(objectTable, (void*)source, (void*)bindings); 307 RELEASE(bindings); 308 } 309 [bindings setObject: self forKey: name]; 310 [bindingLock unlock]; 311 312 [self setValueFor: binding]; 313 314 return self; 315} 316 317- (void)dealloc 318{ 319 DESTROY(info); 320 src = nil; 321 [super dealloc]; 322} 323 324- (id) destinationValue 325{ 326 id newValue; 327 id dest; 328 NSString *keyPath; 329 NSDictionary *options; 330 331 dest = [info objectForKey: NSObservedObjectKey]; 332 keyPath = [info objectForKey: NSObservedKeyPathKey]; 333 options = [info objectForKey: NSOptionsKey]; 334 newValue = [dest valueForKeyPath: keyPath]; 335 return [self transformValue: newValue withOptions: options]; 336} 337 338- (id) sourceValueFor: (NSString *)binding 339{ 340 id newValue; 341 NSDictionary *options; 342 343 options = [info objectForKey: NSOptionsKey]; 344 newValue = [src valueForKeyPath: binding]; 345 return [self reverseTransformValue: newValue withOptions: options]; 346} 347 348- (void) setValueFor: (NSString *)binding 349{ 350 id value = [self destinationValue]; 351 352 NSDebugLLog(@"NSBinding", @"setValueFor: binding %@, source %@ value %@", binding, src, value); 353 [src setValue: value forKey: binding]; 354} 355 356- (void) reverseSetValue: (id)value 357{ 358 NSString *keyPath; 359 id dest; 360 361 keyPath = [info objectForKey: NSObservedKeyPathKey]; 362 dest = [info objectForKey: NSObservedObjectKey]; 363 NSDebugLLog(@"NSBinding", @"reverseSetValue: keyPath %@, dest %@ value %@", keyPath, dest, value); 364 [dest setValue: value forKeyPath: keyPath]; 365} 366 367- (void) reverseSetValueFor: (NSString *)binding 368{ 369 [self reverseSetValue: [self sourceValueFor: binding]]; 370} 371 372- (void) observeValueForKeyPath: (NSString *)keyPath 373 ofObject: (id)object 374 change: (NSDictionary *)change 375 context: (void *)context 376{ 377 NSString *binding = (NSString *)context; 378 NSDictionary *options; 379 id newValue; 380 381 if (change != nil) 382 { 383 options = [info objectForKey: NSOptionsKey]; 384 newValue = [change objectForKey: NSKeyValueChangeNewKey]; 385 newValue = [self transformValue: newValue withOptions: options]; 386 NSDebugLLog(@"NSBinding", @"observeValueForKeyPath: binding %@, keyPath %@, source %@ value %@", binding, keyPath, src, newValue); 387 [src setValue: newValue forKey: binding]; 388 } 389} 390 391- (id) transformValue: (id)value withOptions: (NSDictionary *)options 392{ 393 NSString *valueTransformerName; 394 NSValueTransformer *valueTransformer; 395 NSString *placeholder; 396 397 if (value == NSMultipleValuesMarker) 398 { 399 placeholder = [options objectForKey: NSMultipleValuesPlaceholderBindingOption]; 400 if (placeholder == nil) 401 { 402 placeholder = @"Multiple Values"; 403 } 404 return placeholder; 405 } 406 if (value == NSNoSelectionMarker) 407 { 408 placeholder = [options objectForKey: NSNoSelectionPlaceholderBindingOption]; 409 if (placeholder == nil) 410 { 411 placeholder = @"No Selection"; 412 } 413 return placeholder; 414 } 415 if (value == NSNotApplicableMarker) 416 { 417 if ([[options objectForKey: NSRaisesForNotApplicableKeysBindingOption] 418 boolValue]) 419 { 420 [NSException raise: NSGenericException 421 format: @"This binding does not accept not applicable keys"]; 422 } 423 424 placeholder = [options objectForKey: 425 NSNotApplicablePlaceholderBindingOption]; 426 if (placeholder == nil) 427 { 428 placeholder = @"Not Applicable"; 429 } 430 return placeholder; 431 } 432 if (value == nil) 433 { 434 placeholder = [options objectForKey: 435 NSNullPlaceholderBindingOption]; 436 return placeholder; 437 } 438 439 valueTransformerName = [options objectForKey: 440 NSValueTransformerNameBindingOption]; 441 if (valueTransformerName != nil) 442 { 443 valueTransformer = [NSValueTransformer valueTransformerForName: 444 valueTransformerName]; 445 } 446 else 447 { 448 valueTransformer = [options objectForKey: 449 NSValueTransformerBindingOption]; 450 } 451 452 if (valueTransformer != nil) 453 { 454 if ([value isKindOfClass: [NSArray class]]) 455 { 456 NSArray *oldValue = (NSArray *)value; 457 NSMutableArray *newValue = [[NSMutableArray alloc] initWithCapacity: [oldValue count]]; 458 id<NSFastEnumeration> enumerator = oldValue; 459 460 FOR_IN (id, obj, enumerator) 461 [newValue addObject: [valueTransformer transformedValue: obj]]; 462 END_FOR_IN(enumerator) 463 value = AUTORELEASE(newValue); 464 } 465 else 466 { 467 value = [valueTransformer transformedValue: value]; 468 } 469 } 470 471 return value; 472} 473 474- (id) reverseTransformValue: (id)value withOptions: (NSDictionary *)options 475{ 476 NSString *valueTransformerName; 477 NSValueTransformer *valueTransformer; 478 479 valueTransformerName = [options objectForKey: 480 NSValueTransformerNameBindingOption]; 481 if (valueTransformerName != nil) 482 { 483 valueTransformer = [NSValueTransformer valueTransformerForName: 484 valueTransformerName]; 485 } 486 else 487 { 488 valueTransformer = [options objectForKey: 489 NSValueTransformerBindingOption]; 490 } 491 492 if ((valueTransformer != nil) && [[valueTransformer class] 493 allowsReverseTransformation]) 494 { 495 value = [valueTransformer reverseTransformedValue: value]; 496 } 497 498 return value; 499} 500 501- (NSString*) description 502{ 503 return [NSString stringWithFormat: 504 @"GSKeyValueBinding src (%@) info %@", src, info]; 505} 506 507@end 508 509@implementation GSKeyValueOrBinding : GSKeyValueBinding 510 511- (void) setValueFor: (NSString *)binding 512{ 513 NSDictionary *bindings; 514 BOOL res; 515 516 if (!objectTable) 517 return; 518 519 [bindingLock lock]; 520 bindings = (NSDictionary *)NSMapGet(objectTable, (void *)src); 521 if (!bindings) 522 return; 523 524 res = GSBindingResolveMultipleValueBool(binding, bindings, 525 GSBindingOperationOr); 526 [bindingLock unlock]; 527 [src setValue: [NSNumber numberWithBool: res] forKey: binding]; 528} 529 530- (void) observeValueForKeyPath: (NSString *)keyPath 531 ofObject: (id)object 532 change: (NSDictionary *)change 533 context: (void *)context 534{ 535 [self setValueFor: (NSString*)context]; 536} 537 538@end 539 540@implementation GSKeyValueAndBinding : GSKeyValueBinding 541 542- (void) setValueFor: (NSString *)binding 543{ 544 NSDictionary *bindings; 545 BOOL res; 546 547 if (!objectTable) 548 return; 549 550 [bindingLock lock]; 551 bindings = (NSDictionary *)NSMapGet(objectTable, (void *)src); 552 if (!bindings) 553 return; 554 555 res = GSBindingResolveMultipleValueBool(binding, bindings, 556 GSBindingOperationAnd); 557 [bindingLock unlock]; 558 [src setValue: [NSNumber numberWithBool: res] forKey: binding]; 559} 560 561- (void) observeValueForKeyPath: (NSString *)keyPath 562 ofObject: (id)object 563 change: (NSDictionary *)change 564 context: (void *)context 565{ 566 [self setValueFor: (NSString*)context]; 567} 568 569@end 570 571BOOL NSIsControllerMarker(id object) 572{ 573 return [NSMultipleValuesMarker isEqual: object] 574 || [NSNoSelectionMarker isEqual: object] 575 || [NSNotApplicableMarker isEqual: object]; 576} 577 578//Helper functions 579BOOL GSBindingResolveMultipleValueBool(NSString *key, NSDictionary *bindings, 580 GSBindingOperationKind operationKind) 581{ 582 NSString *bindingName; 583 NSDictionary *info; 584 int count = 1; 585 id object; 586 NSString *keyPath; 587 id value; 588 NSDictionary *options; 589 GSKeyValueBinding *theBinding; 590 591 bindingName = key; 592 while ((theBinding = [bindings objectForKey: bindingName])) 593 { 594 info = theBinding->info; 595 object = [info objectForKey: NSObservedObjectKey]; 596 keyPath = [info objectForKey: NSObservedKeyPathKey]; 597 options = [info objectForKey: NSOptionsKey]; 598 599 value = [object valueForKeyPath: keyPath]; 600 value = [theBinding transformValue: value withOptions: options]; 601 if ([value boolValue] == operationKind) 602 { 603 return operationKind; 604 } 605 bindingName = [NSString stringWithFormat: @"%@%i", key, ++count]; 606 } 607 return !operationKind; 608} 609 610void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey, 611 NSDictionary *bindings) 612{ 613 NSString *bindingName; 614 NSDictionary *info; 615 NSDictionary *options; 616 int count = 1; 617 id object; 618 id target; 619 SEL selector; 620 NSString *keyPath; 621 NSInvocation *invocation; 622 GSKeyValueBinding *theBinding; 623 624 theBinding = [bindings objectForKey: targetKey]; 625 info = theBinding->info; 626 object = [info objectForKey: NSObservedObjectKey]; 627 keyPath = [info objectForKey: NSObservedKeyPathKey]; 628 options = [info objectForKey: NSOptionsKey]; 629 630 target = [object valueForKeyPath: keyPath]; 631 selector = NSSelectorFromString([options objectForKey: 632 NSSelectorNameBindingOption]); 633 if (target == nil || selector == NULL) return; 634 635 invocation = [NSInvocation invocationWithMethodSignature: 636 [target methodSignatureForSelector: selector]]; 637 [invocation setSelector: selector]; 638 639 bindingName = argumentKey; 640 while ((theBinding = [bindings objectForKey: bindingName])) 641 { 642 info = theBinding->info; 643 object = [info objectForKey: NSObservedObjectKey]; 644 keyPath = [info objectForKey: NSObservedKeyPathKey]; 645 if ((object = [object valueForKeyPath: keyPath])) 646 { 647 [invocation setArgument: object atIndex: ++count]; 648 } 649 bindingName = [NSString stringWithFormat: @"%@%i", argumentKey, count]; 650 } 651 [invocation invoke]; 652} 653