API  1.0.0
CPKeyValueBinding.j
Go to the documentation of this file.
1 /*
2  * CPKeyValueBinding.j
3  * AppKit
4  *
5  * Created by Ross Boucher 1/13/09
6  * Copyright 280 North, Inc.
7  *
8  * Adapted from GNUStep
9  * Released under the LGPL.
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24  */
25 
26 
27 
29  bindingsMap = @{};
30 
31 @typedef CPBindingOperationKind
34 
35 @implementation CPBinder : CPObject
36 {
37  CPDictionary _info;
38  id _source;
39 
40  JSObject _suppressedNotifications;
41  JSObject _placeholderForMarker;
42 }
43 
44 + (void)exposeBinding:(CPString)aBinding forClass:(Class)aClass
45 {
46  var bindings = [exposedBindingsMap objectForKey:[aClass UID]];
47 
48  if (!bindings)
49  {
50  bindings = [];
51  [exposedBindingsMap setObject:bindings forKey:[aClass UID]];
52  }
53 
54  bindings.push(aBinding);
55 }
56 
57 + (CPArray)exposedBindingsForClass:(Class)aClass
58 {
59  return [[exposedBindingsMap objectForKey:[aClass UID]] copy];
60 }
61 
62 + (CPBinder)getBinding:(CPString)aBinding forObject:(id)anObject
63 {
64  return [[bindingsMap objectForKey:[anObject UID]] objectForKey:aBinding];
65 }
66 
67 + (void)_reverseSetValueFromExclusiveBinderForObject:(id)anObject
68 {
69  var bindersByBindingName = [bindingsMap objectForKey:[anObject UID]];
70 
71  [bindersByBindingName enumerateKeysAndObjectsUsingBlock:function(binding, binder, stop)
72  {
73  if ([binder isKindOfClass:[self class]])
74  {
75  [binder reverseSetValueFor:binding];
76  stop(YES);
77  }
78  }];
79 }
80 
81 + (CPDictionary)infoForBinding:(CPString)aBinding forObject:(id)anObject
82 {
83  var theBinding = [self getBinding:aBinding forObject:anObject];
84 
85  if (theBinding)
86  return theBinding._info;
87 
88  return nil;
89 }
90 
91 + (CPDictionary)allBindingsForObject:(id)anObject
92 {
93  return [bindingsMap objectForKey:[anObject UID]];
94 }
95 
96 + (void)unbind:(CPString)aBinding forObject:(id)anObject
97 {
98  var bindings = [bindingsMap objectForKey:[anObject UID]];
99 
100  if (!bindings)
101  return;
102 
103  var theBinding = [bindings objectForKey:aBinding];
104 
105  if (!theBinding)
106  return;
107 
108  var info = theBinding._info,
109  observedObject = [info objectForKey:CPObservedObjectKey],
110  keyPath = [info objectForKey:CPObservedKeyPathKey];
111 
112  [observedObject removeObserver:theBinding forKeyPath:keyPath];
113  [bindings removeObjectForKey:aBinding];
114 }
115 
116 + (void)unbindAllForObject:(id)anObject
117 {
118  var bindings = [bindingsMap objectForKey:[anObject UID]];
119 
120  if (!bindings)
121  return;
122 
123  var allKeys = [bindings allKeys],
124  count = allKeys.length;
125 
126  while (count--)
127  [anObject unbind:allKeys[count]];
128 
129  [bindingsMap removeObjectForKey:[anObject UID]];
130 }
131 
132 - (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
133 {
134  // We use [self init] here because subclasses override init. We can't override this method
135  // because their initialization has to occur before this method in this class is executed.
136  self = [self init];
137 
138  if (self)
139  {
140  _source = aSource;
141  _info = @{
142  CPObservedObjectKey: aDestination,
143  CPObservedKeyPathKey: aKeyPath,
144  };
145  _suppressedNotifications = {};
146  _placeholderForMarker = {};
147 
148  if (options)
149  [_info setObject:options forKey:CPOptionsKey];
150 
151  [self _updatePlaceholdersWithOptions:options forBinding:aName];
152 
153  [aDestination addObserver:self forKeyPath:aKeyPath options:0 context:aBinding];
154 
155  var bindings = [bindingsMap objectForKey:[_source UID]];
156 
157  if (!bindings)
158  {
159  bindings = @{};
160  [bindingsMap setObject:bindings forKey:[_source UID]];
161  }
162 
163  [bindings setObject:self forKey:aName];
164  [self setValueFor:aBinding];
165  }
166 
167  return self;
168 }
169 
170 + (BOOL)isBindingAllowed:(CPString)aBinding forObject:(id)anObject
171 {
172  if ([[anObject class] isBindingExclusive:aBinding])
173  {
174  var bindingsForObject = [bindingsMap objectForKey:[anObject UID]],
175  allBindings = [bindingsForObject allKeys],
176  count = [allBindings count];
177 
178  while (count--)
179  {
180  if ([[anObject class] isBindingExclusive:allBindings[count]])
181  return NO;
182  }
183  }
184 
185  return YES;
186 }
187 
188 - (void)raiseIfNotApplicable:(id)aValue forKeyPath:(CPString)keyPath options:(CPDictionary)options
189 {
190  if (aValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
191  {
192  [CPException raise:CPGenericException
193  reason:@"Cannot transform non-applicable key on: " + _source + " key path: " + keyPath + " value: " + aValue];
194  }
195 }
196 
197 - (void)setValueFor:(CPString)theBinding
198 {
199  var destination = [_info objectForKey:CPObservedObjectKey],
200  keyPath = [_info objectForKey:CPObservedKeyPathKey],
201  options = [_info objectForKey:CPOptionsKey],
202  newValue = [destination valueForKeyPath:keyPath];
203 
204  if (CPIsControllerMarker(newValue))
205  {
206  [self raiseIfNotApplicable:newValue forKeyPath:keyPath options:options];
207 
208  var value = [self _placeholderForMarker:newValue];
209  [self setPlaceholderValue:value withMarker:newValue forBinding:theBinding];
210  }
211  else
212  {
213  var value = [self transformValue:newValue withOptions:options];
214  [self setValue:value forBinding:theBinding];
215  }
216 }
217 
218 - (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
219 {
220  [_source setValue:aValue forKey:aBinding];
221 }
222 
223 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
224 {
225  [_source setValue:aValue forKey:aBinding];
226 }
227 
228 - (void)reverseSetValueFor:(CPString)aBinding
229 {
230  var destination = [_info objectForKey:CPObservedObjectKey],
231  keyPath = [_info objectForKey:CPObservedKeyPathKey],
232  options = [_info objectForKey:CPOptionsKey],
233  newValue = [self valueForBinding:aBinding];
234 
235  newValue = [self reverseTransformValue:newValue withOptions:options];
236 
237  [self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
238  [destination setValue:newValue forKeyPath:keyPath];
239  [self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
240 }
241 
242 - (id)valueForBinding:(CPString)aBinding
243 {
244  return [_source valueForKeyPath:aBinding];
245 }
246 
247 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
248 {
249  if (!changes)
250  return;
251 
252  var objectSuppressions = _suppressedNotifications[[anObject UID]];
253 
254  if (objectSuppressions && objectSuppressions[aKeyPath])
255  return;
256 
257  [self setValueFor:context];
258 }
259 
260 - (id)transformValue:(id)aValue withOptions:(CPDictionary)options
261 {
262  var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
263  valueTransformer;
264 
265  if (valueTransformerName)
266  {
267  valueTransformer = [CPValueTransformer valueTransformerForName:valueTransformerName];
268 
269  if (!valueTransformer)
270  {
271  var valueTransformerClass = CPClassFromString(valueTransformerName);
272 
273  if (valueTransformerClass)
274  {
275  valueTransformer = [[valueTransformerClass alloc] init];
276  [valueTransformerClass setValueTransformer:valueTransformer forName:valueTransformerName];
277  }
278  }
279  }
280  else
281  valueTransformer = [options objectForKey:CPValueTransformerBindingOption];
282 
283  if (valueTransformer)
284  aValue = [valueTransformer transformedValue:aValue];
285 
286  // If the value is nil AND the source doesn't respond to setPlaceholderString: then
287  // we set the value to the placeholder. Otherwise, we do not want to short cut the process
288  // of setting the placeholder that is based on the fact that the value is nil.
289  if ((aValue === undefined || aValue === nil || aValue === [CPNull null])
290  && ![_source respondsToSelector:@selector(setPlaceholderString:)])
291  aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
292 
293  return aValue;
294 }
295 
296 - (id)reverseTransformValue:(id)aValue withOptions:(CPDictionary)options
297 {
298  var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
299  valueTransformer;
300 
301  if (valueTransformerName)
302  valueTransformer = [CPValueTransformer valueTransformerForName:valueTransformerName];
303  else
304  valueTransformer = [options objectForKey:CPValueTransformerBindingOption];
305 
306  if (valueTransformer && [[valueTransformer class] allowsReverseTransformation])
307  aValue = [valueTransformer reverseTransformedValue:aValue];
308 
309  return aValue;
310 }
311 
312 - (BOOL)continuouslyUpdatesValue
313 {
314  var options = [_info objectForKey:CPOptionsKey];
315  return [[options objectForKey:CPContinuouslyUpdatesValueBindingOption] boolValue];
316 }
317 
318 - (BOOL)handlesContentAsCompoundValue
319 {
320  var options = [_info objectForKey:CPOptionsKey];
321  return [[options objectForKey:CPHandlesContentAsCompoundValueBindingOption] boolValue];
322 }
323 
327 - (void)suppressSpecificNotificationFromObject:(id)anObject keyPath:(CPString)aKeyPath
328 {
329  if (!anObject)
330  return;
331 
332  var uid = [anObject UID],
333  objectSuppressions = _suppressedNotifications[uid];
334 
335  if (!objectSuppressions)
336  _suppressedNotifications[uid] = objectSuppressions = {};
337 
338  objectSuppressions[aKeyPath] = YES;
339 }
340 
344 - (void)unsuppressSpecificNotificationFromObject:(id)anObject keyPath:(CPString)aKeyPath
345 {
346  if (!anObject)
347  return;
348 
349  var uid = [anObject UID],
350  objectSuppressions = _suppressedNotifications[uid];
351 
352  if (!objectSuppressions)
353  return;
354 
355  delete objectSuppressions[aKeyPath];
356 }
357 
358 - (void)_updatePlaceholdersWithOptions:(CPDictionary)options
359 {
360  var count = [CPBinderPlaceholderMarkers count];
361 
362  while (count--)
363  {
364  var marker = CPBinderPlaceholderMarkers[count],
365  optionName = CPBinderPlaceholderOptions[count],
366  isExplicit = [options containsKey:optionName],
367  placeholder = isExplicit ? [options objectForKey:optionName] : nil;
368 
369  [self _setPlaceholder:placeholder forMarker:marker isDefault:!isExplicit];
370  }
371 }
372 
373 - (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
374 {
375  [self _updatePlaceholdersWithOptions:options];
376 }
377 
378 - (JSObject)_placeholderForMarker:(id)aMarker
379 {
380  var placeholder = _placeholderForMarker[[aMarker UID]];
381 
382  if (placeholder)
383  return placeholder.value;
384 
385  return nil;
386 }
387 
388 - (void)_setPlaceholder:(id)aPlaceholder forMarker:(id)aMarker isDefault:(BOOL)isDefault
389 {
390  if (isDefault)
391  {
392  var existingPlaceholder = _placeholderForMarker[[aMarker UID]];
393 
394  // Don't overwrite an explicitly set placeholder with a default.
395  if (existingPlaceholder && !existingPlaceholder.isDefault)
396  return;
397  }
398 
399  _placeholderForMarker[[aMarker UID]] = { 'isDefault': isDefault, 'value': aPlaceholder };
400 }
401 
402 @end
403 
405 
406 + (void)exposeBinding:(CPString)aBinding
407 {
408  [CPBinder exposeBinding:aBinding forClass:[self class]];
409 }
410 
411 + (Class)_binderClassForBinding:(CPString)aBinding
412 {
413  return [CPBinder class];
414 }
415 
416 + (BOOL)isBindingExclusive:(CPString)aBinding
417 {
418  return NO;
419 }
420 
421 - (CPArray)exposedBindings
422 {
423  var exposedBindings = [],
424  theClass = [self class];
425 
426  while (theClass)
427  {
428  var temp = [CPBinder exposedBindingsForClass:theClass];
429 
430  if (temp)
431  [exposedBindings addObjectsFromArray:temp];
432 
433  theClass = [theClass superclass];
434  }
435 
436  return exposedBindings;
437 }
438 
439 - (Class)valueClassForBinding:(CPString)binding
440 {
441  return [CPString class];
442 }
443 
444 - (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
445 {
446  if (!anObject || !aKeyPath)
447  return CPLog.error("Invalid object or path on " + self + " for " + aBinding);
448 
449  if (![CPBinder isBindingAllowed:aBinding forObject:self])
450  {
451  CPLog.warn([self description] + " : cannot bind " + aBinding + " because another binding with the same functionality is already in use.");
452  return;
453  }
454  //if (![[self exposedBindings] containsObject:aBinding])
455  // CPLog.warn("No binding exposed on " + self + " for " + aBinding);
456 
457  var binderClass = [[self class] _binderClassForBinding:aBinding];
458 
459  [self unbind:aBinding];
460  [[binderClass alloc] initWithBinding:[self _replacementKeyPathForBinding:aBinding] name:aBinding to:anObject keyPath:aKeyPath options:options from:self];
461 }
462 
463 - (CPDictionary)infoForBinding:(CPString)aBinding
464 {
465  return [CPBinder infoForBinding:aBinding forObject:self];
466 }
467 
468 - (void)unbind:(CPString)aBinding
469 {
470  var binderClass = [[self class] _binderClassForBinding:aBinding];
471  [binderClass unbind:aBinding forObject:self];
472 }
473 
474 - (CPString)_replacementKeyPathForBinding:(CPString)binding
475 {
476  return binding;
477 }
478 
479 @end
480 
489 @implementation _CPValueBinder : CPBinder
490 {
491  id __doxygen__;
492 }
493 
494 - (void)setValueFor:(CPString)theBinding
495 {
496  [super setValueFor:@"objectValue"];
497 }
498 
499 - (void)reverseSetValueFor:(CPString)theBinding
500 {
501  [super reverseSetValueFor:@"objectValue"];
502 }
503 
504 @end
505 
506 @implementation _CPMultipleValueBooleanBinding : CPBinder
507 {
508  CPBindingOperationKind _operation;
509 }
510 
511 - (void)setValueFor:(CPString)aBinding
512 {
513  var bindings = [bindingsMap valueForKey:[_source UID]];
514 
515  if (!bindings)
516  return;
517 
518  var baseBinding = aBinding.replace(/\d$/, "");
519 
520  [_source setValue:[self resolveMultipleValuesForBinding:baseBinding bindings:bindings booleanOperation:_operation] forKey:baseBinding];
521 }
522 
523 - (void)reverseSetValueFor:(CPString)theBinding
524 {
525  // read-only
526 }
527 
528 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
529 {
530  [self setValueFor:context];
531 }
532 
533 - (BOOL)resolveMultipleValuesForBinding:(CPString)aBinding bindings:(CPDictionary)bindings booleanOperation:(CPBindingOperationKind)operation
534 {
535  var bindingName = aBinding,
536  theBinding,
537  count = 2;
538 
539  while (theBinding = [bindings objectForKey:bindingName])
540  {
541  var info = theBinding._info,
542  object = [info objectForKey:CPObservedObjectKey],
543  keyPath = [info objectForKey:CPObservedKeyPathKey],
544  options = [info objectForKey:CPOptionsKey],
545  value = [object valueForKeyPath:keyPath];
546 
547  if (CPIsControllerMarker(value))
548  {
549  [self raiseIfNotApplicable:value forKeyPath:keyPath options:options];
550  value = [theBinding _placeholderForMarker:value];
551  }
552  else
553  value = [theBinding transformValue:value withOptions:options];
554 
555  if (operation === CPBindingOperationOr)
556  {
557  // Any true condition means true for OR
558  if (value)
559  return YES;
560  }
561 
562  // Any false condition means false for AND
563  else if (!value)
564  return NO;
565 
566  bindingName = aBinding + (count++);
567  }
568 
569  // If we get here, all OR conditions were false or all AND conditions were true
570  return operation === CPBindingOperationOr ? NO : YES;
571 }
572 
573 @end
574 @implementation CPMultipleValueAndBinding : _CPMultipleValueBooleanBinding
575 {
576  id __doxygen__;
577 }
578 
579 - (id)init
580 {
581  if (self = [super init])
582  _operation = CPBindingOperationAnd;
583 
584  return self;
585 }
586 
587 @end
588 @implementation CPMultipleValueOrBinding : _CPMultipleValueBooleanBinding
589 {
590  id __doxygen__;
591 }
592 
593 - (id)init
594 {
595  if (self = [super init])
596  _operation = CPBindingOperationOr;
597 
598  return self;
599 }
600 
601 @end
602 
603 @implementation _CPMultipleValueActionBinding : CPBinder
604 {
605  CPString _argumentBinding;
606  CPString _targetBinding;
607 }
608 
609 - (void)setValueFor:(CPString)theBinding
610 {
611  // Called when the binding is first created
612  [self checkForNullBinding:theBinding initializing:YES];
613 }
614 
615 - (void)reverseSetValueFor:(CPString)theBinding
616 {
617  // no-op
618 }
619 
620 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
621 {
622  // context is the binding name
623  [self checkForNullBinding:context initializing:NO];
624 }
625 
634 - (void)checkForNullBinding:(CPString)theBinding initializing:(BOOL)isInitializing
635 {
636  // Only done for buttons
637  if (![_source isKindOfClass:CPButton])
638  return;
639 
640  // We start with the button enabled for the first argument during init,
641  // and subsequent checks can disable it.
642  if (isInitializing && theBinding === CPArgumentBinding)
643  [_source setEnabled:YES];
644 
645  var bindings = [bindingsMap valueForKey:[_source UID]],
646  binding = [bindings objectForKey:theBinding],
647  info = binding._info,
648  options = [info objectForKey:CPOptionsKey];
649 
650  if (![options valueForKey:CPAllowsNullArgumentBindingOption])
651  {
652  var object = [info objectForKey:CPObservedObjectKey],
653  keyPath = [info objectForKey:CPObservedKeyPathKey],
654  value = [object valueForKeyPath:keyPath];
655 
656  if (value === nil || value === undefined)
657  {
658  [_source setEnabled:NO];
659  return;
660  }
661  }
662 
663  // If a binding value changed and did not fail the null test, enable the button
664  if (!isInitializing)
665  [_source setEnabled:YES];
666 }
667 
668 - (void)invokeAction
669 {
670  var bindings = [bindingsMap valueForKey:[_source UID]],
671  theBinding = [bindings objectForKey:CPTargetBinding],
672 
673  info = theBinding._info,
674  object = [info objectForKey:CPObservedObjectKey],
675  keyPath = [info objectForKey:CPObservedKeyPathKey],
676  options = [info objectForKey:CPOptionsKey],
677 
678  target = [object valueForKeyPath:keyPath],
679  selector = [options objectForKey:CPSelectorNameBindingOption];
680 
681  if (!target || !selector)
682  return;
683 
684  var invocation = [CPInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]],
685  bindingName = CPArgumentBinding,
686  count = 1;
687 
688  while (theBinding = [bindings objectForKey:bindingName])
689  {
690  info = theBinding._info;
691  object = [info objectForKey:CPObservedObjectKey];
692  keyPath = [info objectForKey:CPObservedKeyPathKey];
693 
694  [invocation setArgument:[object valueForKeyPath:keyPath] atIndex:++count];
695 
696  bindingName = CPArgumentBinding + count;
697  }
698 
699  [invocation setSelector:selector];
700  [invocation invokeWithTarget:target];
701 }
702 
703 @end
704 @implementation CPActionBinding : _CPMultipleValueActionBinding
705 {
706  id __doxygen__;
707 }
708 
709 - (id)init
710 {
711  if (self = [super init])
712  {
713  _argumentBinding = CPArgumentBinding;
714  _targetBinding = CPTargetBinding;
715  }
716 
717  return self;
718 }
719 
720 @end
721 @implementation CPDoubleClickActionBinding : _CPMultipleValueActionBinding
722 {
723  id __doxygen__;
724 }
725 
726 - (id)init
727 {
728  if (self = [super init])
729  {
730  _argumentBinding = CPArgumentBinding;
731  _targetBinding = CPTargetBinding;
732  }
733 
734  return self;
735 }
736 
737 @end
738 
742 @implementation _CPPatternBinding : CPBinder
743 {
744  CPString _bindingKey;
745  CPString _patternPlaceholder;
746 }
747 
748 - (void)setValueFor:(CPString)aBinding
749 {
750  var bindings = [bindingsMap valueForKey:[_source UID]];
751 
752  if (!bindings)
753  return;
754 
755  // Strip off any trailing number from the binding name
756  var baseBinding = aBinding.replace(/\d$/, ""),
757  result = [self resolveMultipleValuesForBindings:bindings];
758 
759  if (result.isPlaceholder)
760  [self setPlaceholderValue:result.value withMarker:result.marker forBinding:baseBinding];
761  else
762  [self setValue:result.value forBinding:baseBinding];
763 }
764 
765 - (void)reverseSetValueFor:(CPString)theBinding
766 {
767  // read-only
768 }
769 
770 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
771 {
772  [self setValueFor:context];
773 }
774 
775 - (JSObject)resolveMultipleValuesForBindings:(CPDictionary)bindings
776 {
777  var theBinding,
778  result = { value:@"", isPlaceholder:NO, marker:nil };
779 
780  for (var count = 1; theBinding = [bindings objectForKey:_bindingKey + count]; ++count)
781  {
782  var info = theBinding._info,
783  object = [info objectForKey:CPObservedObjectKey],
784  keyPath = [info objectForKey:CPObservedKeyPathKey],
785  options = [info objectForKey:CPOptionsKey],
786  value = [object valueForKeyPath:keyPath];
787 
788  if (count === 1)
789  result.value = [options objectForKey:CPDisplayPatternBindingOption];
790 
791  if (CPIsControllerMarker(value))
792  {
793  [self raiseIfNotApplicable:value forKeyPath:keyPath options:options];
794 
795  result.isPlaceholder = YES;
796  result.marker = value;
797 
798  value = [theBinding _placeholderForMarker:value];
799  }
800  else
801  value = [theBinding transformValue:value withOptions:options];
802 
803  if (value === nil || value === undefined)
804  value = @"";
805 
806  result.value = result.value.replace("%{" + _patternPlaceholder + count + "}@", [value description]);
807  }
808 
809  return result;
810 }
811 
812 @end
813 
814 
820 @implementation CPValueWithPatternBinding : _CPPatternBinding
821 {
822  id __doxygen__;
823 }
824 
825 - (id)init
826 {
827  if (self = [super init])
828  {
829  _bindingKey = CPDisplayPatternValueBinding;
830  _patternPlaceholder = @"value";
831  }
832 
833  return self;
834 }
835 
836 @end
837 
838 
844 @implementation CPTitleWithPatternBinding : _CPPatternBinding
845 {
846  id __doxygen__;
847 }
848 
849 - (id)init
850 {
851  if (self = [super init])
852  {
853  _bindingKey = CPDisplayPatternTitleBinding;
854  _patternPlaceholder = @"title";
855  }
856 
857  return self;
858 }
859 
860 @end
861 
862 @implementation _CPStateMarker : CPObject
863 {
864  CPString _name;
865 }
866 
867 - (id)initWithName:(CPString)aName
868 {
869  if (self = [super init])
870  _name = aName;
871 
872  return self;
873 }
874 
876 {
877  return "<" + _name + ">";
878 }
879 
880 @end
881 
882 
883 // Keys in options dictionary
884 
885 // Keys in dictionary returned by infoForBinding
886 CPObservedObjectKey = @"CPObservedObjectKey";
887 CPObservedKeyPathKey = @"CPObservedKeyPathKey";
888 CPOptionsKey = @"CPOptionsKey";
889 
890 // special markers
891 CPNoSelectionMarker = [[_CPStateMarker alloc] initWithName:@"NO SELECTION MARKER"];
892 CPMultipleValuesMarker = [[_CPStateMarker alloc] initWithName:@"MULTIPLE VALUES MARKER"];
893 CPNotApplicableMarker = [[_CPStateMarker alloc] initWithName:@"NOT APPLICABLE MARKER"];
894 CPNullMarker = [[_CPStateMarker alloc] initWithName:@"NULL MARKER"];
895 
896 // Binding name constants
897 CPAlignmentBinding = @"alignment";
898 CPArgumentBinding = @"argument";
899 CPContentArrayBinding = @"contentArray";
900 CPContentBinding = @"content";
901 CPContentObjectBinding = @"contentObject";
902 CPContentObjectsBinding = @"contentObjects";
903 CPContentValuesBinding = @"contentValues";
904 CPDisplayPatternTitleBinding = @"displayPatternTitle";
905 CPDisplayPatternValueBinding = @"displayPatternValue";
906 CPDoubleClickArgumentBinding = @"doubleClickArgument";
907 CPDoubleClickTargetBinding = @"doubleClickTarget";
908 CPEditableBinding = @"editable";
909 CPEnabledBinding = @"enabled";
910 CPFontBinding = @"font";
911 CPFontNameBinding = @"fontName";
912 CPFontBoldBinding = @"fontBold";
913 CPHiddenBinding = @"hidden";
914 CPFilterPredicateBinding = @"filterPredicate";
915 CPMaxValueBinding = @"maxValue";
916 CPMinValueBinding = @"minValue";
917 CPPredicateBinding = @"predicate";
918 CPSelectedIndexBinding = @"selectedIndex";
919 CPSelectedLabelBinding = @"selectedLabel";
920 CPSelectedObjectBinding = @"selectedObject";
921 CPSelectedObjectsBinding = @"selectedObjects";
922 CPSelectedTagBinding = @"selectedTag";
923 CPSelectedValueBinding = @"selectedValue";
924 CPSelectedValuesBinding = @"selectedValues";
925 CPSelectionIndexesBinding = @"selectionIndexes";
926 CPTargetBinding = @"target";
927 CPTextColorBinding = @"textColor";
928 CPTitleBinding = @"title";
929 CPToolTipBinding = @"toolTip";
930 CPValueBinding = @"value";
931 CPAttributedStringBinding = @"attributedString";
932 CPValueURLBinding = @"valueURL";
933 CPValuePathBinding = @"valuePath";
934 CPDataBinding = @"data";
935 
936 // Binding options constants
937 CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelection";
938 CPAllowsNullArgumentBindingOption = @"CPAllowsNullArgument";
939 CPConditionallySetsEditableBindingOption = @"CPConditionallySetsEditable";
940 CPConditionallySetsEnabledBindingOption = @"CPConditionallySetsEnabled";
941 CPConditionallySetsHiddenBindingOption = @"CPConditionallySetsHidden";
942 CPContinuouslyUpdatesValueBindingOption = @"CPContinuouslyUpdatesValue";
943 CPCreatesSortDescriptorBindingOption = @"CPCreatesSortDescriptor";
944 CPDeletesObjectsOnRemoveBindingsOption = @"CPDeletesObjectsOnRemove";
945 CPDisplayNameBindingOption = @"CPDisplayName";
946 CPDisplayPatternBindingOption = @"CPDisplayPattern";
947 CPHandlesContentAsCompoundValueBindingOption = @"CPHandlesContentAsCompoundValue";
948 CPInsertsNullPlaceholderBindingOption = @"CPInsertsNullPlaceholder";
949 CPInvokesSeparatelyWithArrayObjectsBindingOption = @"CPInvokesSeparatelyWithArrayObjects";
950 CPMultipleValuesPlaceholderBindingOption = @"CPMultipleValuesPlaceholder";
951 CPNoSelectionPlaceholderBindingOption = @"CPNoSelectionPlaceholder";
952 CPNotApplicablePlaceholderBindingOption = @"CPNotApplicablePlaceholder";
953 CPNullPlaceholderBindingOption = @"CPNullPlaceholder";
954 CPPredicateFormatBindingOption = @"CPPredicateFormat";
955 CPRaisesForNotApplicableKeysBindingOption = @"CPRaisesForNotApplicableKeys";
956 CPSelectorNameBindingOption = @"CPSelectorName";
957 CPSelectsAllWhenSettingContentBindingOption = @"CPSelectsAllWhenSettingContent";
958 CPValidatesImmediatelyBindingOption = @"CPValidatesImmediately";
959 CPValueTransformerNameBindingOption = @"CPValueTransformerName";
960 CPValueTransformerBindingOption = @"CPValueTransformer";
961 
962 CPIsControllerMarker = function(/*id*/anObject)
963 {
964  return anObject === CPMultipleValuesMarker || anObject === CPNoSelectionMarker || anObject === CPNotApplicableMarker || anObject === CPNullMarker;
965 };
966 
967 var CPBinderPlaceholderMarkers = [CPMultipleValuesMarker, CPNoSelectionMarker, CPNotApplicableMarker, CPNullMarker],
968  CPBinderPlaceholderOptions = [CPMultipleValuesPlaceholderBindingOption, CPNoSelectionPlaceholderBindingOption, CPNotApplicablePlaceholderBindingOption, CPNullPlaceholderBindingOption];
969 
970 
971 @implementation CPBinder (CPSynthesizedAccessors)
972 
976 - (id)source
977 {
978  return _source;
979 }
980 
981 @end
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
CPArray exposedBindings()
void setPlaceholderValue:withMarker:forBinding:(id aValue, [withMarker] CPString aMarker, [forBinding] CPString aBinding)
id init()
Definition: CALayer.j:126
BOOL containsKey:(id aKey)
Definition: CPDictionary.j:645
CPDictionary infoForBinding:forObject:(CPString aBinding, [forObject] id anObject)
id invocationWithMethodSignature:(CPMethodSignature aMethodSignature)
Definition: CPInvocation.j:43
An object representation of nil.
Definition: CPNull.h:2
void setValue:forBinding:(id aValue, [forBinding] CPString aBinding)
CPValueTransformer valueTransformerForName:(CPString aName)
void suppressSpecificNotificationFromObject:keyPath:(id anObject, [keyPath] CPString aKeyPath)
void raiseIfNotApplicable:forKeyPath:options:(id aValue, [forKeyPath] CPString keyPath, [options] CPDictionary options)
CPBindingOperationKind var CPBindingOperationAnd
id valueForBinding:(CPString aBinding)
CPObservedKeyPathKey
CPString UID()
Definition: CPString.j:592
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPOptionsKey
An object representation of a message.
Definition: CPInvocation.h:2
id reverseTransformValue:withOptions:(id aValue, [withOptions] CPDictionary options)
An immutable string (collection of characters).
Definition: CPString.h:2
id objectForKey:(id aKey)
Definition: CPDictionary.j:515
if(CPFeatureIsCompatible(CPHTMLCanvasFeature))
void unbind:(CPString aBinding)
void exposeBinding:forClass:(CPString aBinding, [forClass] Class aClass)
CPBinder getBinding:forObject:(CPString aBinding, [forObject] id anObject)
void setValueFor:(CPString theBinding)
int length()
Definition: CPString.j:186
CPBindingOperationKind var CPBindingOperationOr
id transformValue:withOptions:(id aValue, [withOptions] CPDictionary options)
id init()
Definition: CPObject.j:145
CPArray exposedBindingsForClass:(Class aClass)
var exposedBindingsMap
var source
Class class()
Definition: CPObject.j:179
void unsuppressSpecificNotificationFromObject:keyPath:(id anObject, [keyPath] CPString aKeyPath)
var bindingsMap
CPObservedObjectKey
function CPClassFromString(aClassName)
Definition: CPObjJRuntime.j:33
FrameUpdater prototype description