API  0.9.9
CPObjectController.j
Go to the documentation of this file.
1 /*
2  * CPObjectController.j
3  * AppKit
4  *
5  * Created by Ross Boucher.
6  * Copyright 2009, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 
35 @implementation CPObjectController : CPController
36 {
37  id _contentObject;
38  id _selection;
39 
40  Class _objectClass;
41  CPString _objectClassName;
42 
43  BOOL _isEditable;
44  BOOL _automaticallyPreparesContent;
45  BOOL _usesLazyFetching;
46  BOOL _isUsingManagedProxy;
47  _CPManagedProxy _managedProxy;
48 
49  CPCountedSet _observedKeys;
50 }
51 
52 + (void)initialize
53 {
54  if (self !== [CPObjectController class])
55  return;
56 
57  [self exposeBinding:@"editable"];
58  [self exposeBinding:@"contentObject"];
59 }
60 
62 {
63  return [CPSet setWithObjects:"content"];
64 }
65 
66 + (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey
67 {
68  if (aKey === @"contentObject")
69  return NO;
70 
71  return YES;
72 }
73 
75 {
76  return [CPSet setWithObject:"editable"];
77 }
78 
80 {
81  return [CPSet setWithObject:"editable"];
82 }
83 
85 {
86  return [CPSet setWithObjects:"editable", "selection"];
87 }
88 
92 - (id)init
93 {
94  return [self initWithContent:nil];
95 }
96 
103 - (id)initWithContent:(id)aContent
104 {
105  if (self = [super init])
106  {
107  [self setEditable:YES];
109 
110  _observedKeys = [[CPCountedSet alloc] init];
111  _selection = [[CPControllerSelectionProxy alloc] initWithController:self];
112 
113  [self setContent:aContent];
114  }
115 
116  return self;
117 }
118 
123 - (id)content
124 {
125  return _contentObject;
126 }
127 
132 - (void)setContent:(id)aContent
133 {
134  [self willChangeValueForKey:@"contentObject"];
135  [self _selectionWillChange];
136 
137  _contentObject = aContent;
138 
139  [self _selectionDidChange];
140  [self didChangeValueForKey:@"contentObject"];
141 }
142 
146 - (void)_setContentObject:(id)aContent
147 {
148  [self setContent:aContent];
149 }
150 
154 - (id)_contentObject
155 {
156  return [self content];
157 }
158 
166 - (void)setAutomaticallyPreparesContent:(BOOL)shouldAutomaticallyPrepareContent
167 {
168  _automaticallyPreparesContent = shouldAutomaticallyPrepareContent;
169 }
170 
176 {
177  return _automaticallyPreparesContent;
178 }
179 
185 - (void)setEntityName:(CPString)newEntityName
186 {
187  [_managedProxy setEntityName:newEntityName];
188 }
189 
196 {
197  return [_managedProxy entityName];
198 }
199 
205 - (void)setFetchPredicate:(CPPredicate)newPredicate
206 {
207  [_managedProxy setFetchPredicate:newPredicate];
208 }
209 
215 - (CPPredicate)fetchPredicate
216 {
217  return [_managedProxy fetchPredicate];
218 }
219 
224 {
225  [self setContent:[self newObject]];
226 }
227 
233 - (void)setObjectClass:(Class)aClass
234 {
235  _objectClass = aClass;
236 }
237 
243 - (Class)objectClass
244 {
245  return _objectClass;
246 }
247 
251 - (id)_defaultNewObject
252 {
253  return [[[self objectClass] alloc] init];
254 }
255 
261 {
262  return [self _defaultNewObject];
263 }
264 
269 - (void)addObject:(id)anObject
270 {
271  [self setContent:anObject];
272 
273  var binderClass = [[self class] _binderClassForBinding:@"contentObject"];
274  [[binderClass getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
275 }
276 
281 - (void)removeObject:(id)anObject
282 {
283  if ([self content] === anObject)
284  [self setContent:nil];
285 
286  var binderClass = [[self class] _binderClassForBinding:@"contentObject"];
287  [[binderClass getBinding:@"contentObject" forObject:self] reverseSetValueFor:@"contentObject"];
288 }
289 
294 - (void)add:(id)aSender
295 {
296  // FIXME: This should happen on the next run loop?
297  [self addObject:[self newObject]];
298 }
299 
303 - (BOOL)canAdd
304 {
305  return [self isEditable];
306 }
307 
312 - (void)remove:(id)aSender
313 {
314  // FIXME: This should happen on the next run loop?
315  [self removeObject:[self content]];
316 }
317 
321 - (BOOL)canRemove
322 {
323  return [self isEditable] && [[self selectedObjects] count];
324 }
325 
330 - (void)setEditable:(BOOL)shouldBeEditable
331 {
332  _isEditable = shouldBeEditable;
333 }
334 
338 - (BOOL)isEditable
339 {
340  return _isEditable;
341 }
342 
346 - (CPArray)selectedObjects
347 {
348  return [[_CPObservableArray alloc] initWithArray:[_contentObject]];
349 }
350 
355 {
356  return _selection;
357 }
358 
362 - (void)_selectionWillChange
363 {
364  [_selection controllerWillChange];
365  [self willChangeValueForKey:@"selection"];
366 }
367 
371 - (void)_selectionDidChange
372 {
373  if (_selection === undefined || _selection === nil)
374  _selection = [[CPControllerSelectionProxy alloc] initWithController:self];
375 
376  [_selection controllerDidChange];
377  [self didChangeValueForKey:@"selection"];
378 }
379 
384 {
385  return _observedKeys;
386 }
387 
388 - (void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
389 {
390  [_observedKeys addObject:aKeyPath];
391  [super addObserver:anObserver forKeyPath:aKeyPath options:options context:context];
392 }
393 
394 - (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
395 {
396  [_observedKeys removeObject:aKeyPath];
397  [super removeObserver:anObserver forKeyPath:aKeyPath];
398 }
399 
400 @end
401 
402 var CPObjectControllerContentKey = @"CPObjectControllerContentKey",
403  CPObjectControllerObjectClassNameKey = @"CPObjectControllerObjectClassNameKey",
404  CPObjectControllerIsEditableKey = @"CPObjectControllerIsEditableKey",
405  CPObjectControllerAutomaticallyPreparesContentKey = @"CPObjectControllerAutomaticallyPreparesContentKey",
406  CPObjectControllerUsesLazyFetchingKey = @"CPObjectControllerUsesLazyFetchingKey",
407  CPObjectControllerIsUsingManagedProxyKey = @"CPObjectControllerIsUsingManagedProxyKey",
408  CPObjectControllerManagedProxyKey = @"CPObjectControllerManagedProxyKey";
409 
411 
412 - (id)initWithCoder:(CPCoder)aCoder
413 {
414  self = [super init];
415 
416  if (self)
417  {
418  var objectClassName = [aCoder decodeObjectForKey:CPObjectControllerObjectClassNameKey],
419  objectClass = CPClassFromString(objectClassName),
420  content = [aCoder decodeObjectForKey:CPObjectControllerContentKey];
421 
422  [self setObjectClass:objectClass || [CPMutableDictionary class]];
423  [self setEditable:[aCoder decodeBoolForKey:CPObjectControllerIsEditableKey]];
424  [self setAutomaticallyPreparesContent:[aCoder decodeBoolForKey:CPObjectControllerAutomaticallyPreparesContentKey]];
425  [self setUsesLazyFetching:[aCoder decodeBoolForKey:CPObjectControllerUsesLazyFetchingKey]];
426  _isUsingManagedProxy = [aCoder decodeBoolForKey:CPObjectControllerIsUsingManagedProxyKey];
427  _managedProxy = [aCoder decodeObjectForKey:CPObjectControllerManagedProxyKey];
428 
429  if (content != nil)
430  [self setContent:content];
431 
432  _observedKeys = [[CPCountedSet alloc] init];
433  }
434 
435  return self;
436 }
437 
438 - (void)encodeWithCoder:(CPCoder)aCoder
439 {
440  [aCoder encodeObject:[self content] forKey:CPObjectControllerContentKey];
441 
442  if (_objectClass)
443  [aCoder encodeObject:CPStringFromClass(_objectClass) forKey:CPObjectControllerObjectClassNameKey];
444  else if (_objectClassName)
445  [aCoder encodeObject:_objectClassName forKey:CPObjectControllerObjectClassNameKey];
446 
447  [aCoder encodeBool:[self isEditable] forKey:CPObjectControllerIsEditableKey];
448  [aCoder encodeBool:[self automaticallyPreparesContent] forKey:CPObjectControllerAutomaticallyPreparesContentKey];
449  [aCoder encodeBool:[self usesLazyFetching] forKey:CPObjectControllerUsesLazyFetchingKey];
450  [aCoder encodeBool:_isUsingManagedProxy forKey:CPObjectControllerIsUsingManagedProxyKey];
451 
452  if (_managedProxy)
453  [aCoder encodeObject:_managedProxy forKey:CPObjectControllerManagedProxyKey];
454 }
455 
457 {
458  if (![self content] && [self automaticallyPreparesContent])
459  [self prepareContent];
460 }
461 
462 @end
463 
464 @implementation _CPObservationProxy : CPObject
465 {
466  id _keyPath;
467  id _observer;
468  id _object;
469 
470  BOOL _notifyObject;
471 
472  id _context;
473  int _options;
474 }
475 
476 - (id)initWithKeyPath:(id)aKeyPath observer:(id)anObserver object:(id)anObject
477 {
478  if (self = [super init])
479  {
480  _keyPath = aKeyPath;
481  _observer = anObserver;
482  _object = anObject;
483  }
484 
485  return self;
486 }
487 
488 - (id)observer
489 {
490  return _observer;
491 }
492 
493 - (id)keyPath
494 {
495  return _keyPath;
496 }
497 
498 - (id)context
499 {
500  return _context;
501 }
502 
503 - (int)options
504 {
505  return _options;
506 }
507 
508 - (void)setNotifyObject:(BOOL)notify
509 {
510  _notifyObject = notify;
511 }
512 
513 - (BOOL)isEqual:(id)anObject
514 {
515  if (self === anObject)
516  return YES;
517 
518  if (!anObject || [anObject class] !== [self class] || anObject._observer !== _observer || anObject._keyPath !== _keyPath || anObject._object !== _object)
519  return NO;
520 
521  return YES;
522 }
523 
524 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)change context:(id)context
525 {
526  if (_notifyObject)
527  [_object observeValueForKeyPath:aKeyPath ofObject:_object change:change context:context];
528 
529  [_observer observeValueForKeyPath:aKeyPath ofObject:_object change:change context:context];
530 }
531 
533 {
534  return [super description] + [CPString stringWithFormat:@"observation proxy for %@ on key path %@", _observer, _keyPath];
535 }
536 
537 @end
538 
539 // FIXME: This should subclass CPMutableArray not _CPJavaScriptArray
540 @implementation _CPObservableArray : _CPJavaScriptArray
541 {
542  CPArray _observationProxies;
543 }
544 
545 + (id)alloc
546 {
547  var a = [];
548  a.isa = self;
549 
550  var ivars = class_copyIvarList(self),
551  count = ivars.length;
552 
553  while (count--)
554  a[ivar_getName(ivars[count])] = nil;
555 
556  return a;
557 }
558 
560 {
561  return "<_CPObservableArray: " + [super description] + " >";
562 }
563 
564 - (id)initWithArray:(CPArray)anArray
565 {
566  self = [super initWithArray:anArray];
567 
568  self.isa = [_CPObservableArray class];
569  self._observationProxies = [];
570 
571  return self;
572 }
573 
574 - (void)addObserver:(id)anObserver forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
575 {
576  if (aKeyPath.charAt(0) === "@")
577  {
578  // Simple collection operators are scalar and can't be proxied
579  if ([_CPCollectionKVCOperator isSimpleCollectionOperator:aKeyPath])
580  return;
581 
582  var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self];
583 
584  proxy._options = options;
585  proxy._context = context;
586 
587  [_observationProxies addObject:proxy];
588 
589  var dotIndex = aKeyPath.indexOf("."),
590  remaining = aKeyPath.substring(dotIndex + 1),
591  indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
592 
593  [self addObserver:proxy toObjectsAtIndexes:indexes forKeyPath:remaining options:options context:context];
594  }
595  else
596  {
597  var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
598  [self addObserver:anObserver toObjectsAtIndexes:indexes forKeyPath:aKeyPath options:options context:context];
599  }
600 }
601 
602 - (void)removeObserver:(id)anObserver forKeyPath:(CPString)aKeyPath
603 {
604  if (aKeyPath.charAt(0) === "@")
605  {
606  // Simple collection operators are scalar and can't be proxied
607  if ([_CPCollectionKVCOperator isSimpleCollectionOperator:aKeyPath])
608  return;
609 
610  var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObserver object:self],
611  index = [_observationProxies indexOfObject:proxy];
612 
613  proxy = [_observationProxies objectAtIndex:index];
614 
615  var dotIndex = aKeyPath.indexOf("."),
616  remaining = aKeyPath.substring(dotIndex + 1),
617  indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
618 
619  [self removeObserver:proxy fromObjectsAtIndexes:indexes forKeyPath:remaining];
620  }
621  else
622  {
623  var indexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self count])];
624  [self removeObserver:anObserver fromObjectsAtIndexes:indexes forKeyPath:aKeyPath];
625  }
626 }
627 
628 - (void)insertObject:(id)anObject atIndex:(CPUInteger)anIndex
629 {
630  for (var i = 0, count = [_observationProxies count]; i < count; i++)
631  {
632  var proxy = [_observationProxies objectAtIndex:i],
633  keyPath = [proxy keyPath],
634  operator = keyPath.charAt(0) === ".";
635 
636  if (operator)
637  [self willChangeValueForKey:keyPath];
638 
639  [anObject addObserver:proxy forKeyPath:keyPath options:[proxy options] context:[proxy context]];
640 
641  if (operator)
642  [self didChangeValueForKey:keyPath];
643  }
644 
645  [super insertObject:anObject atIndex:anIndex];
646 }
647 
648 - (void)removeObjectAtIndex:(CPUInteger)anIndex
649 {
650  var currentObject = [self objectAtIndex:anIndex];
651 
652  for (var i = 0, count = [_observationProxies count]; i < count; i++)
653  {
654  var proxy = [_observationProxies objectAtIndex:i],
655  keyPath = [proxy keyPath],
656  operator = keyPath.charAt(0) === ".";
657 
658  if (operator)
659  [self willChangeValueForKey:keyPath];
660 
661  [currentObject removeObserver:proxy forKeyPath:keyPath];
662 
663  if (operator)
664  [self didChangeValueForKey:keyPath];
665  }
666 
667  [super removeObjectAtIndex:anIndex];
668 }
669 
670 - (CPArray)objectsAtIndexes:(CPIndexSet)theIndexes
671 {
672  return [_CPObservableArray arrayWithArray:[super objectsAtIndexes:theIndexes]];
673 }
674 
675 - (void)addObject:(id)anObject
676 {
677  [self insertObject:anObject atIndex:[self count]];
678 }
679 
680 - (void)removeLastObject
681 {
682  [self removeObjectAtIndex:[self count]];
683 }
684 
685 - (void)replaceObjectAtIndex:(CPUInteger)anIndex withObject:(id)anObject
686 {
687  var currentObject = [self objectAtIndex:anIndex];
688 
689  for (var i = 0, count = [_observationProxies count]; i < count; i++)
690  {
691  var proxy = [_observationProxies objectAtIndex:i],
692  keyPath = [proxy keyPath],
693  operator = keyPath.charAt(0) === ".";
694 
695  if (operator)
696  [self willChangeValueForKey:keyPath];
697 
698  [currentObject removeObserver:proxy forKeyPath:keyPath];
699  [anObject addObserver:proxy forKeyPath:keyPath options:[proxy options] context:[proxy context]];
700 
701  if (operator)
702  [self didChangeValueForKey:keyPath];
703  }
704 
705  [super replaceObjectAtIndex:anIndex withObject:anObject];
706 }
707 
708 @end
709 
710 @implementation CPControllerSelectionProxy : CPObject
711 {
712  id _controller;
713  id _keys;
714 
715  CPDictionary _cachedValues;
716  CPArray _observationProxies;
717 
718  Object _observedObjectsByKeyPath;
719 }
720 
721 - (id)initWithController:(id)aController
722 {
723  if (self = [super init])
724  {
725  _cachedValues = @{};
726  _observationProxies = [CPArray array];
727  _controller = aController;
728  _observedObjectsByKeyPath = {};
729  }
730 
731  return self;
732 }
733 
734 - (id)_controllerMarkerForValues:(CPArray)theValues
735 {
736  var count = [theValues count],
737  value;
738 
739  if (!count)
740  value = CPNoSelectionMarker;
741  else if (count === 1)
742  value = [theValues objectAtIndex:0];
743  else
744  {
745  if ([_controller alwaysUsesMultipleValuesMarker])
746  value = CPMultipleValuesMarker;
747  else
748  {
749  value = [theValues objectAtIndex:0];
750 
751  for (var i = 0, count = [theValues count]; i < count && value != CPMultipleValuesMarker; i++)
752  {
753  if (![value isEqual:[theValues objectAtIndex:i]])
754  value = CPMultipleValuesMarker;
755  }
756  }
757  }
758 
759  if (value === nil || value.isa && [value isEqual:[CPNull null]])
760  value = CPNullMarker;
761 
762  return value;
763 }
764 
765 - (id)valueForKeyPath:(CPString)theKeyPath
766 {
767  var values = [[_controller selectedObjects] valueForKeyPath:theKeyPath];
768 
769  // Simple collection operators like @count return a scalar value, not an array or set
770  if ([values isKindOfClass:CPArray] || [values isKindOfClass:CPSet])
771  {
772  var value = [self _controllerMarkerForValues:values];
773  [_cachedValues setObject:value forKey:theKeyPath];
774 
775  return value;
776  }
777  else
778  return values;
779 }
780 
781 - (id)valueForKey:(CPString)theKeyPath
782 {
783  return [self valueForKeyPath:theKeyPath];
784 }
785 
786 - (void)setValue:(id)theValue forKeyPath:(CPString)theKeyPath
787 {
788  [[_controller selectedObjects] setValue:theValue forKeyPath:theKeyPath];
789  [_cachedValues removeObjectForKey:theKeyPath];
790 
791  // Allow handlesContentAsCompoundValue to work, based on observation of Cocoa's
792  // NSArrayController - when handlesContentAsCompoundValue and setValue:forKey:@"selection.X"
793  // is called, the array controller causes the compound value to be rewritten if
794  // handlesContentAsCompoundValue == YES. Note that
795  // A) this doesn't use observation (observe: X is not visible in backtraces)
796  // B) it only happens through the selection proxy and not on arrangedObject.X, content.X
797  // or even selectedObjects.X.
798  // FIXME The main code for this should somehow be in CPArrayController and also work
799  // for table based row edits.
800  [[CPBinder getBinding:@"contentArray" forObject:_controller] _contentArrayDidChange];
801 }
802 
803 - (void)setValue:(id)theValue forKey:(CPString)theKeyPath
804 {
805  [self setValue:theValue forKeyPath:theKeyPath];
806 }
807 
808 - (unsigned)count
809 {
810  return [_cachedValues count];
811 }
812 
813 - (id)keyEnumerator
814 {
815  return [_cachedValues keyEnumerator];
816 }
817 
818 - (void)controllerWillChange
819 {
820  _keys = [_cachedValues allKeys];
821 
822  if (!_keys)
823  return;
824 
825  for (var i = 0, count = _keys.length; i < count; i++)
826  [self willChangeValueForKey:_keys[i]];
827 
828  [_cachedValues removeAllObjects];
829 }
830 
831 - (void)controllerDidChange
832 {
833  [_cachedValues removeAllObjects];
834 
835  if (!_keys)
836  return;
837 
838  for (var i = 0, count = _keys.length; i < count; i++)
839  [self didChangeValueForKey:_keys[i]];
840 
841  _keys = nil;
842 }
843 
844 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)change context:(id)context
845 {
846  [_cachedValues removeObjectForKey:aKeyPath];
847 }
848 
849 - (void)addObserver:(id)anObject forKeyPath:(CPString)aKeyPath options:(CPKeyValueObservingOptions)options context:(id)context
850 {
851  var proxy = [[_CPObservationProxy alloc] initWithKeyPath:aKeyPath observer:anObject object:self];
852 
853  [proxy setNotifyObject:YES];
854  [_observationProxies addObject:proxy];
855 
856  // We keep a reference to the observed objects because removeObserver: will be called after the selection changes.
857  var observedObjects = [_controller selectedObjects];
858  _observedObjectsByKeyPath[aKeyPath] = observedObjects;
859  [observedObjects addObserver:proxy forKeyPath:aKeyPath options:options context:context];
860 }
861 
862 - (void)removeObserver:(id)anObject forKeyPath:(CPString)aKeyPath
863 {
864  [_observationProxies enumerateObjectsUsingBlock:function(aProxy, idx, stop)
865  {
866  if (aProxy._object === self && aProxy._keyPath == aKeyPath && aProxy._observer === anObject)
867  {
868  var observedObjects = _observedObjectsByKeyPath[aKeyPath];
869 
870  [observedObjects removeObserver:aProxy forKeyPath:aKeyPath];
871  [_observationProxies removeObjectAtIndex:idx];
872 
873  _observedObjectsByKeyPath[aKeyPath] = nil;
874 
875  stop(YES);
876  }
877  }];
878 }
879 
880 @end
881 
882 
883 @implementation _CPManagedProxy : CPObject
884 {
885  CPString _entityName;
886  CPPredicate _fetchPredicate;
887 }
888 
889 @end
890 
891 var CPManagedProxyEntityNameKey = @"CPManagedProxyEntityNameKey",
892  CPManagedProxyFetchPredicateKey = @"CPManagedProxyFetchPredicateKey";
893 
894 @implementation _CPManagedProxy (CPCoding)
895 
896 - (id)initWithCoder:(CPCoder)aCoder
897 {
898  self = [super init];
899 
900  if (self)
901  {
902  [self setEntityName:[aCoder decodeObjectForKey:CPManagedProxyEntityNameKey]];
903  [self setFetchPredicate:[aCoder decodeObjectForKey:CPManagedProxyFetchPredicateKey]];
904  }
905 
906  return self;
907 }
908 
909 - (void)encodeWithCoder:(CPCoder)aCoder
910 {
911  [aCoder encodeObject:[self entityName] forKey:CPManagedProxyEntityNameKey];
912  [aCoder encodeObject:[self fetchPredicate] forKey:CPManagedProxyFetchPredicateKey];
913 }
914 
915 @end
916 
918 
923 {
924  return _usesLazyFetching;
925 }
926 
930 - (void)setUsesLazyFetching:(BOOL)aValue
931 {
932  _usesLazyFetching = aValue;
933 }
934 
935 @end
void setValue:forKeyPath:(id theValue, [forKeyPath] CPString theKeyPath)
void setAutomaticallyPreparesContent:(BOOL shouldAutomaticallyPrepareContent)
CPSet keyPathsForValuesAffectingCanInsert()
void removeObject:(id anObject)
void willChangeValueForKey:(CPString aKey)
CPSet keyPathsForValuesAffectingCanRemove()
var isEqual
An object representation of nil.
Definition: CPNull.h:2
var CPObjectControllerAutomaticallyPreparesContentKey
void addObject:(id anObject)
var CPManagedProxyEntityNameKey
id valueForKeyPath:(CPString theKeyPath)
A collection of unique integers.
Definition: CPIndexSet.h:2
void setEditable:(BOOL shouldBeEditable)
CPString description()
Definition: CPObject.j:348
void setUsesLazyFetching:(BOOL aValue)
A mutable key-value pair collection.
Definition: CPDictionary.h:2
var CPManagedProxyFetchPredicateKey
var CPObjectControllerManagedProxyKey
void setContent:(id aContent)
CPMultipleValuesMarker
void encodeObject:forKey:(id anObject, [forKey] CPString aKey)
An immutable string (collection of characters).
Definition: CPString.h:2
var CPObjectControllerObjectClassNameKey
CPSet keyPathsForValuesAffectingContentObject()
CPBinder getBinding:forObject:(CPString aBinding, [forObject] id anObject)
var CPObjectControllerUsesLazyFetchingKey
CPSet keyPathsForValuesAffectingCanAdd()
void addObserver:forKeyPath:options:context:(id anObserver, [forKeyPath] CPString aPath, [options] CPKeyValueObservingOptions options, [context] id aContext)
void exposeBinding:(CPString aBinding)
void didChangeValueForKey:(CPString aKey)
An mutable collection which may contain a specific object numerous times.
Definition: CPCountedSet.h:2
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
void removeObserver:forKeyPath:(id anObserver, [forKeyPath] CPString aPath)
var CPObjectControllerIsEditableKey
var CPObjectControllerContentKey
id stringWithFormat:(CPString format, [,]...)
Definition: CPString.j:166
CPNoSelectionMarker
id indexSetWithIndexesInRange:(CPRange aRange)
Definition: CPIndexSet.j:60
id initWithContent:(id aContent)
Class class()
Definition: CPObject.j:179
CPPredicate fetchPredicate()
void setObjectClass:(Class aClass)
var CPObjectControllerIsUsingManagedProxyKey
function CPClassFromString(aClassName)
Definition: CPObjJRuntime.j:33
CPNullMarker
id alloc()
Definition: CPObject.j:130