API  0.9.7
 All Classes Files Functions Variables Macros Groups Pages
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 @class CPButton
28 
29 var exposedBindingsMap = @{},
30  bindingsMap = @{};
31 
32 var CPBindingOperationAnd = 0,
33  CPBindingOperationOr = 1;
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 + (CPDictionary)infoForBinding:(CPString)aBinding forObject:(id)anObject
68 {
69  var theBinding = [self getBinding:aBinding forObject:anObject];
70 
71  if (theBinding)
72  return theBinding._info;
73 
74  return nil;
75 }
76 
77 + (CPDictionary)allBindingsForObject:(id)anObject
78 {
79  return [bindingsMap objectForKey:[anObject UID]];
80 }
81 
82 + (void)unbind:(CPString)aBinding forObject:(id)anObject
83 {
84  var bindings = [bindingsMap objectForKey:[anObject UID]];
85 
86  if (!bindings)
87  return;
88 
89  var theBinding = [bindings objectForKey:aBinding];
90 
91  if (!theBinding)
92  return;
93 
94  var info = theBinding._info,
95  observedObject = [info objectForKey:CPObservedObjectKey],
96  keyPath = [info objectForKey:CPObservedKeyPathKey];
97 
98  [observedObject removeObserver:theBinding forKeyPath:keyPath];
99  [bindings removeObjectForKey:aBinding];
100 }
101 
102 + (void)unbindAllForObject:(id)anObject
103 {
104  var bindings = [bindingsMap objectForKey:[anObject UID]];
105 
106  if (!bindings)
107  return;
108 
109  var allKeys = [bindings allKeys],
110  count = allKeys.length;
111 
112  while (count--)
113  [anObject unbind:[bindings objectForKey:allKeys[count]]];
114 
115  [bindingsMap removeObjectForKey:[anObject UID]];
116 }
117 
118 - (id)initWithBinding:(CPString)aBinding name:(CPString)aName to:(id)aDestination keyPath:(CPString)aKeyPath options:(CPDictionary)options from:(id)aSource
119 {
120  // We use [self init] here because subclasses override init. We can't override this method
121  // because their initialization has to occur before this method in this class is executed.
122  self = [self init];
123 
124  if (self)
125  {
126  _source = aSource;
127  _info = @{
128  CPObservedObjectKey: aDestination,
129  CPObservedKeyPathKey: aKeyPath,
130  };
131  _suppressedNotifications = {};
132  _placeholderForMarker = {};
133 
134  if (options)
135  [_info setObject:options forKey:CPOptionsKey];
136 
137  [self _updatePlaceholdersWithOptions:options forBinding:aName];
138 
139  [aDestination addObserver:self forKeyPath:aKeyPath options:CPKeyValueObservingOptionNew context:aBinding];
140 
141  var bindings = [bindingsMap objectForKey:[_source UID]];
142 
143  if (!bindings)
144  {
145  bindings = @{};
146  [bindingsMap setObject:bindings forKey:[_source UID]];
147  }
148 
149  [bindings setObject:self forKey:aName];
150  [self setValueFor:aBinding];
151  }
152 
153  return self;
154 }
155 
156 - (void)raiseIfNotApplicable:(id)aValue forKeyPath:(CPString)keyPath options:(CPDictionary)options
157 {
158  if (aValue === CPNotApplicableMarker && [options objectForKey:CPRaisesForNotApplicableKeysBindingOption])
159  {
160  [CPException raise:CPGenericException
161  reason:@"Cannot transform non-applicable key on: " + _source + " key path: " + keyPath + " value: " + aValue];
162  }
163 }
164 
165 - (void)setValueFor:(CPString)theBinding
166 {
167  var destination = [_info objectForKey:CPObservedObjectKey],
168  keyPath = [_info objectForKey:CPObservedKeyPathKey],
169  options = [_info objectForKey:CPOptionsKey],
170  newValue = [destination valueForKeyPath:keyPath];
171 
172  if (CPIsControllerMarker(newValue))
173  {
174  [self raiseIfNotApplicable:newValue forKeyPath:keyPath options:options];
175 
176  var value = [self _placeholderForMarker:newValue];
177  [self setPlaceholderValue:value withMarker:newValue forBinding:theBinding];
178  }
179  else
180  {
181  var value = [self transformValue:newValue withOptions:options];
182  [self setValue:value forBinding:theBinding];
183  }
184 }
185 
186 - (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
187 {
188  [_source setValue:aValue forKey:aBinding];
189 }
190 
191 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
192 {
193  [_source setValue:aValue forKey:aBinding];
194 }
195 
196 - (void)reverseSetValueFor:(CPString)aBinding
197 {
198  var destination = [_info objectForKey:CPObservedObjectKey],
199  keyPath = [_info objectForKey:CPObservedKeyPathKey],
200  options = [_info objectForKey:CPOptionsKey],
201  newValue = [self valueForBinding:aBinding];
202 
203  newValue = [self reverseTransformValue:newValue withOptions:options];
204 
205  [self suppressSpecificNotificationFromObject:destination keyPath:keyPath];
206  [destination setValue:newValue forKeyPath:keyPath];
207  [self unsuppressSpecificNotificationFromObject:destination keyPath:keyPath];
208 }
209 
210 - (id)valueForBinding:(CPString)aBinding
211 {
212  return [_source valueForKeyPath:aBinding];
213 }
214 
215 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
216 {
217  if (!changes)
218  return;
219 
220  var objectSuppressions = _suppressedNotifications[[anObject UID]];
221 
222  if (objectSuppressions && objectSuppressions[aKeyPath])
223  return;
224 
225  [self setValueFor:context];
226 }
227 
228 - (id)transformValue:(id)aValue withOptions:(CPDictionary)options
229 {
230  var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
231  valueTransformer;
232 
233  if (valueTransformerName)
234  {
235  valueTransformer = [CPValueTransformer valueTransformerForName:valueTransformerName];
236 
237  if (!valueTransformer)
238  {
239  var valueTransformerClass = CPClassFromString(valueTransformerName);
240 
241  if (valueTransformerClass)
242  {
243  valueTransformer = [[valueTransformerClass alloc] init];
244  [valueTransformerClass setValueTransformer:valueTransformer forName:valueTransformerName];
245  }
246  }
247  }
248  else
249  valueTransformer = [options objectForKey:CPValueTransformerBindingOption];
250 
251  if (valueTransformer)
252  aValue = [valueTransformer transformedValue:aValue];
253 
254  // If the value is nil AND the source doesn't respond to setPlaceholderString: then
255  // we set the value to the placeholder. Otherwise, we do not want to short cut the process
256  // of setting the placeholder that is based on the fact that the value is nil.
257  if ((aValue === undefined || aValue === nil || aValue === [CPNull null])
258  && ![_source respondsToSelector:@selector(setPlaceholderString:)])
259  aValue = [options objectForKey:CPNullPlaceholderBindingOption] || nil;
260 
261  return aValue;
262 }
263 
264 - (id)reverseTransformValue:(id)aValue withOptions:(CPDictionary)options
265 {
266  var valueTransformerName = [options objectForKey:CPValueTransformerNameBindingOption],
267  valueTransformer;
268 
269  if (valueTransformerName)
270  valueTransformer = [CPValueTransformer valueTransformerForName:valueTransformerName];
271  else
272  valueTransformer = [options objectForKey:CPValueTransformerBindingOption];
273 
274  if (valueTransformer && [[valueTransformer class] allowsReverseTransformation])
275  aValue = [valueTransformer reverseTransformedValue:aValue];
276 
277  return aValue;
278 }
279 
280 - (BOOL)continuouslyUpdatesValue
281 {
282  var options = [_info objectForKey:CPOptionsKey];
283  return [[options objectForKey:CPContinuouslyUpdatesValueBindingOption] boolValue];
284 }
285 
286 - (BOOL)handlesContentAsCompoundValue
287 {
288  var options = [_info objectForKey:CPOptionsKey];
289  return [[options objectForKey:CPHandlesContentAsCompoundValueBindingOption] boolValue];
290 }
291 
295 - (void)suppressSpecificNotificationFromObject:(id)anObject keyPath:(CPString)aKeyPath
296 {
297  if (!anObject)
298  return;
299 
300  var uid = [anObject UID],
301  objectSuppressions = _suppressedNotifications[uid];
302 
303  if (!objectSuppressions)
304  _suppressedNotifications[uid] = objectSuppressions = {};
305 
306  objectSuppressions[aKeyPath] = YES;
307 }
308 
312 - (void)unsuppressSpecificNotificationFromObject:(id)anObject keyPath:(CPString)aKeyPath
313 {
314  if (!anObject)
315  return;
316 
317  var uid = [anObject UID],
318  objectSuppressions = _suppressedNotifications[uid];
319 
320  if (!objectSuppressions)
321  return;
322 
323  delete objectSuppressions[aKeyPath];
324 }
325 
326 - (void)_updatePlaceholdersWithOptions:(CPDictionary)options
327 {
328  var count = [CPBinderPlaceholderMarkers count];
329 
330  while (count--)
331  {
332  var marker = CPBinderPlaceholderMarkers[count],
333  optionName = CPBinderPlaceholderOptions[count],
334  isExplicit = [options containsKey:optionName],
335  placeholder = isExplicit ? [options objectForKey:optionName] : nil;
336 
337  [self _setPlaceholder:placeholder forMarker:marker isDefault:!isExplicit];
338  }
339 }
340 
341 - (void)_updatePlaceholdersWithOptions:(CPDictionary)options forBinding:(CPString)aBinding
342 {
343  [self _updatePlaceholdersWithOptions:options];
344 }
345 
346 - (JSObject)_placeholderForMarker:(id)aMarker
347 {
348  var placeholder = _placeholderForMarker[[aMarker UID]];
349 
350  if (placeholder)
351  return placeholder.value;
352 
353  return nil;
354 }
355 
356 - (void)_setPlaceholder:(id)aPlaceholder forMarker:(id)aMarker isDefault:(BOOL)isDefault
357 {
358  if (isDefault)
359  {
360  var existingPlaceholder = _placeholderForMarker[[aMarker UID]];
361 
362  // Don't overwrite an explicitly set placeholder with a default.
363  if (existingPlaceholder && !existingPlaceholder.isDefault)
364  return;
365  }
366 
367  _placeholderForMarker[[aMarker UID]] = { 'isDefault': isDefault, 'value': aPlaceholder };
368 }
369 
370 @end
371 
372 @implementation CPObject (KeyValueBindingCreation)
373 
374 + (void)exposeBinding:(CPString)aBinding
375 {
376  [CPBinder exposeBinding:aBinding forClass:[self class]];
377 }
378 
379 + (Class)_binderClassForBinding:(CPString)aBinding
380 {
381  return [CPBinder class];
382 }
383 
384 - (CPArray)exposedBindings
385 {
386  var exposedBindings = [],
387  theClass = [self class];
388 
389  while (theClass)
390  {
391  var temp = [CPBinder exposedBindingsForClass:theClass];
392 
393  if (temp)
394  [exposedBindings addObjectsFromArray:temp];
395 
396  theClass = [theClass superclass];
397  }
398 
399  return exposedBindings;
400 }
401 
402 - (Class)valueClassForBinding:(CPString)binding
403 {
404  return [CPString class];
405 }
406 
407 - (void)bind:(CPString)aBinding toObject:(id)anObject withKeyPath:(CPString)aKeyPath options:(CPDictionary)options
408 {
409  if (!anObject || !aKeyPath)
410  return CPLog.error("Invalid object or path on " + self + " for " + aBinding);
411 
412  //if (![[self exposedBindings] containsObject:aBinding])
413  // CPLog.warn("No binding exposed on " + self + " for " + aBinding);
414 
415  var binderClass = [[self class] _binderClassForBinding:aBinding];
416 
417  [self unbind:aBinding];
418  [[binderClass alloc] initWithBinding:[self _replacementKeyPathForBinding:aBinding] name:aBinding to:anObject keyPath:aKeyPath options:options from:self];
419 }
420 
421 - (CPDictionary)infoForBinding:(CPString)aBinding
422 {
423  return [CPBinder infoForBinding:aBinding forObject:self];
424 }
425 
426 - (void)unbind:(CPString)aBinding
427 {
428  var binderClass = [[self class] _binderClassForBinding:aBinding];
429  [binderClass unbind:aBinding forObject:self];
430 }
431 
432 - (CPString)_replacementKeyPathForBinding:(CPString)binding
433 {
434  return binding;
435 }
436 
437 @end
438 
447 @implementation _CPValueBinder : CPBinder
448 {
449  id __doxygen__;
450 }
451 
452 - (void)setValueFor:(CPString)theBinding
453 {
454  [super setValueFor:@"objectValue"];
455 }
456 
457 - (void)reverseSetValueFor:(CPString)theBinding
458 {
459  [super reverseSetValueFor:@"objectValue"];
460 }
461 
462 @end
463 
464 @implementation _CPMultipleValueBooleanBinding : CPBinder
465 {
466  CPBindingOperationKind _operation;
467 }
468 
469 - (void)setValueFor:(CPString)aBinding
470 {
471  var bindings = [bindingsMap valueForKey:[_source UID]];
472 
473  if (!bindings)
474  return;
475 
476  var baseBinding = aBinding.replace(/\d$/, "");
477 
478  [_source setValue:[self resolveMultipleValuesForBinding:baseBinding bindings:bindings booleanOperation:_operation] forKey:baseBinding];
479 }
480 
481 - (void)reverseSetValueFor:(CPString)theBinding
482 {
483  // read-only
484 }
485 
486 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
487 {
488  [self setValueFor:context];
489 }
490 
491 - (BOOL)resolveMultipleValuesForBinding:(CPString)aBinding bindings:(CPDictionary)bindings booleanOperation:(CPBindingOperationKind)operation
492 {
493  var bindingName = aBinding,
494  theBinding,
495  count = 2;
496 
497  while (theBinding = [bindings objectForKey:bindingName])
498  {
499  var info = theBinding._info,
500  object = [info objectForKey:CPObservedObjectKey],
501  keyPath = [info objectForKey:CPObservedKeyPathKey],
502  options = [info objectForKey:CPOptionsKey],
503  value = [object valueForKeyPath:keyPath];
504 
505  if (CPIsControllerMarker(value))
506  {
507  [self raiseIfNotApplicable:value forKeyPath:keyPath options:options];
508  value = [theBinding _placeholderForMarker:value];
509  }
510  else
511  value = [theBinding transformValue:value withOptions:options];
512 
513  if (operation === CPBindingOperationOr)
514  {
515  // Any true condition means true for OR
516  if (value)
517  return YES;
518  }
519 
520  // Any false condition means false for AND
521  else if (!value)
522  return NO;
523 
524  bindingName = aBinding + (count++);
525  }
526 
527  // If we get here, all OR conditions were false or all AND conditions were true
528  return operation === CPBindingOperationOr ? NO : YES;
529 }
530 
531 @end
532 @implementation CPMultipleValueAndBinding : _CPMultipleValueBooleanBinding
533 {
534  id __doxygen__;
535 }
536 
537 - (id)init
538 {
539  if (self = [super init])
540  _operation = CPBindingOperationAnd;
541 
542  return self;
543 }
544 
545 @end
546 @implementation CPMultipleValueOrBinding : _CPMultipleValueBooleanBinding
547 {
548  id __doxygen__;
549 }
550 
551 - (id)init
552 {
553  if (self = [super init])
554  _operation = CPBindingOperationOr;
555 
556  return self;
557 }
558 
559 @end
560 
561 @implementation _CPMultipleValueActionBinding : CPBinder
562 {
563  CPString _argumentBinding;
564  CPString _targetBinding;
565 }
566 
567 - (void)setValueFor:(CPString)theBinding
568 {
569  // Called when the binding is first created
570  [self checkForNullBinding:theBinding initializing:YES];
571 }
572 
573 - (void)reverseSetValueFor:(CPString)theBinding
574 {
575  // no-op
576 }
577 
578 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
579 {
580  // context is the binding name
581  [self checkForNullBinding:context initializing:NO];
582 }
583 
592 - (void)checkForNullBinding:(CPString)theBinding initializing:(BOOL)isInitializing
593 {
594  // Only done for buttons
595  if (![_source isKindOfClass:CPButton])
596  return;
597 
598  // We start with the button enabled for the first argument during init,
599  // and subsequent checks can disable it.
600  if (isInitializing && theBinding === CPArgumentBinding)
601  [_source setEnabled:YES];
602 
603  var bindings = [bindingsMap valueForKey:[_source UID]],
604  binding = [bindings objectForKey:theBinding],
605  info = binding._info,
606  options = [info objectForKey:CPOptionsKey];
607 
608  if (![options valueForKey:CPAllowsNullArgumentBindingOption])
609  {
610  var object = [info objectForKey:CPObservedObjectKey],
611  keyPath = [info objectForKey:CPObservedKeyPathKey],
612  value = [object valueForKeyPath:keyPath];
613 
614  if (value === nil || value === undefined)
615  {
616  [_source setEnabled:NO];
617  return;
618  }
619  }
620 
621  // If a binding value changed and did not fail the null test, enable the button
622  if (!isInitializing)
623  [_source setEnabled:YES];
624 }
625 
626 - (void)invokeAction
627 {
628  var bindings = [bindingsMap valueForKey:[_source UID]],
629  theBinding = [bindings objectForKey:CPTargetBinding],
630 
631  info = theBinding._info,
632  object = [info objectForKey:CPObservedObjectKey],
633  keyPath = [info objectForKey:CPObservedKeyPathKey],
634  options = [info objectForKey:CPOptionsKey],
635 
636  target = [object valueForKeyPath:keyPath],
637  selector = [options objectForKey:CPSelectorNameBindingOption];
638 
639  if (!target || !selector)
640  return;
641 
642  var invocation = [CPInvocation invocationWithMethodSignature:[target methodSignatureForSelector:selector]],
643  bindingName = CPArgumentBinding,
644  count = 1;
645 
646  while (theBinding = [bindings objectForKey:bindingName])
647  {
648  info = theBinding._info;
649  object = [info objectForKey:CPObservedObjectKey];
650  keyPath = [info objectForKey:CPObservedKeyPathKey];
651 
652  [invocation setArgument:[object valueForKeyPath:keyPath] atIndex:++count];
653 
654  bindingName = CPArgumentBinding + count;
655  }
656 
657  [invocation setSelector:selector];
658  [invocation invokeWithTarget:target];
659 }
660 
661 @end
662 @implementation CPActionBinding : _CPMultipleValueActionBinding
663 {
664  id __doxygen__;
665 }
666 
667 - (id)init
668 {
669  if (self = [super init])
670  {
671  _argumentBinding = CPArgumentBinding;
672  _targetBinding = CPTargetBinding;
673  }
674 
675  return self;
676 }
677 
678 @end
679 @implementation CPDoubleClickActionBinding : _CPMultipleValueActionBinding
680 {
681  id __doxygen__;
682 }
683 
684 - (id)init
685 {
686  if (self = [super init])
687  {
688  _argumentBinding = CPArgumentBinding;
689  _targetBinding = CPTargetBinding;
690  }
691 
692  return self;
693 }
694 
695 @end
696 
700 @implementation _CPPatternBinding : CPBinder
701 {
702  CPString _bindingKey;
703  CPString _patternPlaceholder;
704 }
705 
706 - (void)setValueFor:(CPString)aBinding
707 {
708  var bindings = [bindingsMap valueForKey:[_source UID]];
709 
710  if (!bindings)
711  return;
712 
713  // Strip off any trailing number from the binding name
714  var baseBinding = aBinding.replace(/\d$/, ""),
715  result = [self resolveMultipleValuesForBindings:bindings];
716 
717  if (result.isPlaceholder)
718  [self setPlaceholderValue:result.value withMarker:result.marker forBinding:baseBinding];
719  else
720  [self setValue:result.value forBinding:baseBinding];
721 }
722 
723 - (void)reverseSetValueFor:(CPString)theBinding
724 {
725  // read-only
726 }
727 
728 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)context
729 {
730  [self setValueFor:context];
731 }
732 
733 - (JSObject)resolveMultipleValuesForBindings:(CPDictionary)bindings
734 {
735  var theBinding,
736  result = { value:@"", isPlaceholder:NO, marker:nil };
737 
738  for (var count = 1; theBinding = [bindings objectForKey:_bindingKey + count]; ++count)
739  {
740  var info = theBinding._info,
741  object = [info objectForKey:CPObservedObjectKey],
742  keyPath = [info objectForKey:CPObservedKeyPathKey],
743  options = [info objectForKey:CPOptionsKey],
744  value = [object valueForKeyPath:keyPath];
745 
746  if (count === 1)
747  result.value = [options objectForKey:CPDisplayPatternBindingOption];
748 
749  if (CPIsControllerMarker(value))
750  {
751  [self raiseIfNotApplicable:value forKeyPath:keyPath options:options];
752 
753  result.isPlaceholder = YES;
754  result.marker = value;
755 
756  value = [theBinding _placeholderForMarker:value];
757  }
758  else
759  value = [theBinding transformValue:value withOptions:options];
760 
761  if (value === nil || value === undefined)
762  value = @"";
763 
764  result.value = result.value.replace("%{" + _patternPlaceholder + count + "}@", [value description]);
765  }
766 
767  return result;
768 }
769 
770 @end
771 
772 
778 @implementation CPValueWithPatternBinding : _CPPatternBinding
779 {
780  id __doxygen__;
781 }
782 
783 - (id)init
784 {
785  if (self = [super init])
786  {
787  _bindingKey = CPDisplayPatternValueBinding;
788  _patternPlaceholder = @"value";
789  }
790 
791  return self;
792 }
793 
794 @end
795 
796 
802 @implementation CPTitleWithPatternBinding : _CPPatternBinding
803 {
804  id __doxygen__;
805 }
806 
807 - (id)init
808 {
809  if (self = [super init])
810  {
811  _bindingKey = CPDisplayPatternTitleBinding;
812  _patternPlaceholder = @"title";
813  }
814 
815  return self;
816 }
817 
818 @end
819 
820 @implementation _CPStateMarker : CPObject
821 {
822  CPString _name;
823 }
824 
825 - (id)initWithName:(CPString)aName
826 {
827  if (self = [super init])
828  _name = aName
829 
830  return self;
831 }
832 
833 - (CPString)description
834 {
835  return "<" + _name + ">";
836 }
837 
838 @end
839 
840 
841 // Keys in options dictionary
842 
843 // Keys in dictionary returned by infoForBinding
844 CPObservedObjectKey = @"CPObservedObjectKey";
845 CPObservedKeyPathKey = @"CPObservedKeyPathKey";
846 CPOptionsKey = @"CPOptionsKey";
847 
848 // special markers
849 CPNoSelectionMarker = [[_CPStateMarker alloc] initWithName:@"NO SELECTION MARKER"];
850 CPMultipleValuesMarker = [[_CPStateMarker alloc] initWithName:@"MULTIPLE VALUES MARKER"];
851 CPNotApplicableMarker = [[_CPStateMarker alloc] initWithName:@"NOT APPLICABLE MARKER"];
852 CPNullMarker = [[_CPStateMarker alloc] initWithName:@"NULL MARKER"];
853 
854 // Binding name constants
855 CPAlignmentBinding = @"alignment";
856 CPArgumentBinding = @"argument";
857 CPContentArrayBinding = @"contentArray";
858 CPContentBinding = @"content";
859 CPContentObjectBinding = @"contentObject";
860 CPContentObjectsBinding = @"contentObjects";
861 CPContentValuesBinding = @"contentValues";
862 CPDisplayPatternTitleBinding = @"displayPatternTitle";
863 CPDisplayPatternValueBinding = @"displayPatternValue";
864 CPDoubleClickArgumentBinding = @"doubleClickArgument";
865 CPDoubleClickTargetBinding = @"doubleClickTarget";
866 CPEditableBinding = @"editable";
867 CPEnabledBinding = @"enabled";
868 CPFontBinding = @"font";
869 CPFontNameBinding = @"fontName";
870 CPFontBoldBinding = @"fontBold";
871 CPHiddenBinding = @"hidden";
872 CPFilterPredicateBinding = @"filterPredicate";
873 CPMaxValueBinding = @"maxValue";
874 CPMinValueBinding = @"minValue";
875 CPPredicateBinding = @"predicate";
876 CPSelectedIndexBinding = @"selectedIndex";
877 CPSelectedLabelBinding = @"selectedLabel";
878 CPSelectedObjectBinding = @"selectedObject";
879 CPSelectedObjectsBinding = @"selectedObjects";
880 CPSelectedTagBinding = @"selectedTag";
881 CPSelectedValueBinding = @"selectedValue";
882 CPSelectedValuesBinding = @"selectedValues";
883 CPSelectionIndexesBinding = @"selectionIndexes";
884 CPTargetBinding = @"target";
885 CPTextColorBinding = @"textColor";
886 CPTitleBinding = @"title";
887 CPToolTipBinding = @"toolTip";
888 CPValueBinding = @"value";
889 CPValueURLBinding = @"valueURL";
890 CPValuePathBinding = @"valuePath";
891 CPDataBinding = @"data";
892 
893 // Binding options constants
894 CPAllowsEditingMultipleValuesSelectionBindingOption = @"CPAllowsEditingMultipleValuesSelection";
895 CPAllowsNullArgumentBindingOption = @"CPAllowsNullArgument";
896 CPConditionallySetsEditableBindingOption = @"CPConditionallySetsEditable";
897 CPConditionallySetsEnabledBindingOption = @"CPConditionallySetsEnabled";
898 CPConditionallySetsHiddenBindingOption = @"CPConditionallySetsHidden";
899 CPContinuouslyUpdatesValueBindingOption = @"CPContinuouslyUpdatesValue";
900 CPCreatesSortDescriptorBindingOption = @"CPCreatesSortDescriptor";
901 CPDeletesObjectsOnRemoveBindingsOption = @"CPDeletesObjectsOnRemove";
902 CPDisplayNameBindingOption = @"CPDisplayName";
903 CPDisplayPatternBindingOption = @"CPDisplayPattern";
904 CPHandlesContentAsCompoundValueBindingOption = @"CPHandlesContentAsCompoundValue";
905 CPInsertsNullPlaceholderBindingOption = @"CPInsertsNullPlaceholder";
906 CPInvokesSeparatelyWithArrayObjectsBindingOption = @"CPInvokesSeparatelyWithArrayObjects";
907 CPMultipleValuesPlaceholderBindingOption = @"CPMultipleValuesPlaceholder";
908 CPNoSelectionPlaceholderBindingOption = @"CPNoSelectionPlaceholder";
909 CPNotApplicablePlaceholderBindingOption = @"CPNotApplicablePlaceholder";
910 CPNullPlaceholderBindingOption = @"CPNullPlaceholder";
911 CPPredicateFormatBindingOption = @"CPPredicateFormat";
912 CPRaisesForNotApplicableKeysBindingOption = @"CPRaisesForNotApplicableKeys";
913 CPSelectorNameBindingOption = @"CPSelectorName";
914 CPSelectsAllWhenSettingContentBindingOption = @"CPSelectsAllWhenSettingContent";
915 CPValidatesImmediatelyBindingOption = @"CPValidatesImmediately";
916 CPValueTransformerNameBindingOption = @"CPValueTransformerName";
917 CPValueTransformerBindingOption = @"CPValueTransformer";
918 
919 CPIsControllerMarker = function(/*id*/anObject)
920 {
921  return anObject === CPMultipleValuesMarker || anObject === CPNoSelectionMarker || anObject === CPNotApplicableMarker || anObject === CPNullMarker;
922 };
923 
924 var CPBinderPlaceholderMarkers = [CPMultipleValuesMarker, CPNoSelectionMarker, CPNotApplicableMarker, CPNullMarker],
925  CPBinderPlaceholderOptions = [CPMultipleValuesPlaceholderBindingOption, CPNoSelectionPlaceholderBindingOption, CPNotApplicablePlaceholderBindingOption, CPNullPlaceholderBindingOption];
926 
927 
928 @implementation CPBinder (CPSynthesizedAccessors)
929 
933 - (id)source
934 {
935  return _source;
936 }
937 
938 @end