2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include <NGObjWeb/WODisplayGroup.h>
23 #import <EOControl/EOControl.h>
24 #import <EOControl/EOKeyValueArchiver.h>
25 #import <Foundation/Foundation.h>
26 #import <Foundation/NSNotification.h>
29 @interface EODataSource(DGQualifierSetting)
30 - (void)setAuxiliaryQualifier:(EOQualifier *)_q;
31 - (void)setQualifier:(EOQualifier *)_q;
32 - (void)setQualifierBindings:(NSDictionary *)_bindings;
35 #if APPLE_FOUNDATION_LIBRARY || NeXT_Foundation_LIBRARY
36 @interface NSObject(Miss)
37 - (void)notImplemented:(SEL)cmd;
42 @interface NSObject(EditingContext)
44 - (void)addEditor:(id)_editor;
45 - (void)removeEditor:(id)_editor;
46 - (void)setMessageHandler:(id)_handler;
51 @implementation WODisplayGroup
53 static NSNumber *uint0 = nil;
54 static NSArray *uint0Array = nil;
58 uint0 = [[NSNumber alloc] initWithUnsignedInt:0];
59 if (uint0Array == nil)
60 uint0Array = [[NSArray alloc] initWithObjects:&uint0 count:1];
64 if ((self = [super init])) {
65 [self setDefaultStringMatchFormat:
66 [[self class] globalDefaultStringMatchFormat]];
67 [self setDefaultStringMatchOperator:
68 [[self class] globalDefaultStringMatchOperator]];
69 self->currentBatchIndex = 1;
75 [[NSNotificationCenter defaultCenter] removeObserver:self];
76 [self setDataSource:nil];
78 [self->_queryMatch release];
79 [self->_queryMin release];
80 [self->_queryMax release];
81 [self->_queryOperator release];
82 [self->_queryBindings release];
83 [self->defaultStringMatchFormat release];
84 [self->defaultStringMatchOperator release];
85 [self->qualifier release];
86 [self->objects release];
87 [self->displayObjects release];
88 [self->selectionIndexes release];
89 [self->sortOrderings release];
90 [self->insertedObjectDefaults release];
96 - (void)_objectsChangedInEC:(NSNotification *)_notification {
101 if ((d = [self delegate]) != nil) {
102 if ([d respondsToSelector:
103 @selector(displayGroup:shouldRedisplayForChangesInEditingContext:)]) {
104 doRedisplay = [d displayGroup:self
105 shouldRedisplayForEditingContextChangeNotification:
117 /* contents changed notification ??? */
122 - (void)setDelegate:(id)_delegate {
123 self->delegate = _delegate;
126 return self->delegate;
129 - (void)setDataSource:(EODataSource *)_ds {
130 NSNotificationCenter *nc = nil;
133 if (_ds == self->dataSource)
136 /* unregister with old editing context */
137 if ([self->dataSource respondsToSelector:@selector(editingContext)]) {
138 if ((ec = [self->dataSource editingContext]) != nil) {
139 [ec removeEditor:self];
140 if ([ec messageHandler] == self)
141 [ec setMessageHandler:nil];
143 [[NSNotificationCenter defaultCenter]
145 name:@"EOObjectsChangedInEditingContext"
150 ASSIGN(self->dataSource, _ds);
152 /* register with new editing context */
153 if ([self->dataSource respondsToSelector:@selector(editingContext)]) {
154 if ((ec = [self->dataSource editingContext]) != nil) {
156 if ([ec messageHandler] == nil)
157 [ec setMessageHandler:self];
160 selector:@selector(_objectsChangedInEC:)
161 name:@"EOObjectsChangedInEditingContext"
166 if ([self->delegate respondsToSelector:
167 @selector(displayGroupDidChangeDataSource:)])
168 [self->delegate displayGroupDidChangeDataSource:self];
170 - (EODataSource *)dataSource {
171 return self->dataSource;
174 - (void)setSortOrderings:(NSArray *)_orderings {
175 ASSIGNCOPY(self->sortOrderings, _orderings);
177 - (NSArray *)sortOrderings {
178 return self->sortOrderings;
181 - (void)setFetchesOnLoad:(BOOL)_flag {
182 self->flags.fetchesOnLoad = _flag ? 1 : 0;
184 - (BOOL)fetchesOnLoad {
185 return self->flags.fetchesOnLoad ? YES : NO;
188 - (void)setInsertedObjectDefaultValues:(NSDictionary *)_values {
189 ASSIGNCOPY(self->insertedObjectDefaults, [_values copy]);
191 - (NSDictionary *)insertedObjectDefaultValues {
192 return self->insertedObjectDefaults;
195 - (void)setNumberOfObjectsPerBatch:(unsigned)_count {
196 self->numberOfObjectsPerBatch = _count;
198 - (unsigned)numberOfObjectsPerBatch {
199 return self->numberOfObjectsPerBatch;
202 - (void)setSelectsFirstObjectAfterFetch:(BOOL)_flag {
203 self->flags.selectFirstAfterFetch = _flag ? 1 : 0;
205 - (BOOL)selectsFirstObjectAfterFetch {
206 return self->flags.selectFirstAfterFetch ? YES : NO;
209 - (void)setValidatesChangesImmediatly:(BOOL)_flag {
210 self->flags.validatesChangesImmediatly = _flag ? 1 : 0;
212 - (BOOL)validatesChangesImmediatly {
213 return self->flags.validatesChangesImmediatly ? YES : NO;
218 - (BOOL)hasMultipleBatches {
219 return [self batchCount] > 1 ? YES : NO;
221 - (unsigned)batchCount {
224 doc = [[self allObjects] count];
225 nob = [self numberOfObjectsPerBatch];
229 : doc / nob + ((doc % nob) ? 1 : 0) ;
232 - (void)setCurrentBatchIndex:(unsigned)_index {
233 self->currentBatchIndex = (_index <= [self batchCount]) ? _index : 1;
235 - (unsigned)currentBatchIndex {
236 if (self->currentBatchIndex > [self batchCount])
237 self->currentBatchIndex = 1;
238 return self->currentBatchIndex;
241 - (unsigned)indexOfFirstDisplayedObject {
242 return ([self currentBatchIndex] - 1) * [self numberOfObjectsPerBatch];
245 - (unsigned)indexOfLastDisplayedObject {
246 unsigned nob = [self numberOfObjectsPerBatch];
247 unsigned cnt = [[self allObjects] count];
252 return (([self indexOfFirstDisplayedObject] + nob) < cnt)
253 ? ([self indexOfFirstDisplayedObject] + nob) - 1
257 - (id)displayNextBatch {
258 [self clearSelection];
260 self->currentBatchIndex++;
261 if (self->currentBatchIndex > [self batchCount])
262 self->currentBatchIndex = 1;
264 [self updateDisplayedObjects];
268 - (id)displayPreviousBatch {
269 [self clearSelection];
271 self->currentBatchIndex--;
272 if ([self currentBatchIndex] <= 0)
273 self->currentBatchIndex = [self batchCount];
275 [self updateDisplayedObjects];
279 - (id)displayBatchContainingSelectedObject {
280 [self warnWithFormat:@"%s not implemenented", __PRETTY_FUNCTION__];
281 [self updateDisplayedObjects];
287 - (BOOL)setSelectionIndexes:(NSArray *)_selection {
290 NSSet *before, *after;
293 if ((d = [self delegate])) {
294 if ([d respondsToSelector:
295 @selector(displayGroup:shouldChangeSelectionToIndexes:)]) {
296 ok = [d displayGroup:self shouldChangeSelectionToIndexes:_selection];
302 /* apply selection */
304 before = [NSSet setWithArray:self->selectionIndexes];
305 after = [NSSet setWithArray:_selection];
307 ASSIGN(self->selectionIndexes, _selection);
309 if (![before isEqual:after]) {
310 [d displayGroupDidChangeSelection:self];
311 [d displayGroupDidChangeSelectedObjects:self];
315 - (NSArray *)selectionIndexes {
316 return self->selectionIndexes;
319 - (BOOL)clearSelection {
320 static NSArray *emptyArray = nil;
321 if (emptyArray == nil) emptyArray = [[NSArray alloc] init];
322 return [self setSelectionIndexes:emptyArray];
328 if (![self->displayObjects isNotEmpty])
331 if (![self->selectionIndexes isNotEmpty]) {
332 [self setSelectionIndexes:uint0Array];
336 idx = [[self->selectionIndexes lastObject] unsignedIntValue];
337 if (idx >= ([self->displayObjects count] - 1)) {
338 /* last object is already selected, select first one */
339 [self setSelectionIndexes:uint0Array];
343 /* select next object .. */
344 [self setSelectionIndexes:
345 [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:(idx + 1)]]];
349 - (id)selectPrevious {
352 if (![self->displayObjects isNotEmpty])
355 if (![self->selectionIndexes isNotEmpty]) {
356 [self setSelectionIndexes:uint0Array];
360 idx = [[self->selectionIndexes objectAtIndex:0] unsignedIntValue];
362 /* first object is selected, now select last one */
364 sidx = [NSNumber numberWithUnsignedInt:([self->displayObjects count] - 1)];
365 [self setSelectionIndexes:[NSArray arrayWithObject:sidx]];
368 /* select previous object .. */
369 [self setSelectionIndexes:
370 [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:(idx - 1)]]];
374 - (void)setSelectedObject:(id)_obj {
378 // TODO: maybe we need to retain the selection array and just swap the first
380 idx = [self->objects indexOfObject:_obj];
381 idxNumber = (idx != NSNotFound)
382 ? [NSNumber numberWithUnsignedInt:idx] : (NSNumber *)nil;
384 if (idxNumber != nil) {
387 a = [[NSArray alloc] initWithObjects:&idxNumber count:1];
388 [self setSelectionIndexes:a];
389 [a release]; a = nil;
392 [self setSelectionIndexes:nil];
394 - (id)selectedObject {
395 unsigned int i, sCount;
397 if ((sCount = [self->selectionIndexes count]) == 0)
400 i = [[self->selectionIndexes objectAtIndex:0] unsignedIntValue];
401 if (i >= [self->objects count])
404 // TODO: need to ensure selection is in displayedObjects?
405 return [self->objects objectAtIndex:i];
408 - (void)setSelectedObjects:(NSArray *)_objs {
409 [self selectObjectsIdenticalTo:_objs];
410 // [self warnWithFormat:@"%s not implemented.", __PRETTY_FUNCTION__];
412 - (NSArray *)selectedObjects {
413 NSMutableArray *result;
414 unsigned int i, sCount, oCount;
416 sCount = [self->selectionIndexes count];
417 oCount = [self->objects count];
418 result = [NSMutableArray arrayWithCapacity:sCount];
420 for (i = 0; i < sCount; i++) {
423 idx = [[self->selectionIndexes objectAtIndex:i] unsignedIntValue];
425 [result addObject:[self->objects objectAtIndex:idx]];
430 - (BOOL)selectObject:(id)_obj {
431 /* returns YES if displayedObjects contains _obj otherwise NO */
435 if (![self->displayObjects containsObject:_obj])
438 idx = [self->objects indexOfObject:_obj];
439 idxNumber = (idx != NSNotFound)
440 ? [NSNumber numberWithUnsignedInt:idx] : (NSNumber *)nil;
442 // TODO: should we just exchange the first item and/or call
443 // -setSelectedObject: ?
445 #if 0 /* this was wrong? */
446 if ([self->selectionIndexes containsObject:idxNumber])
447 /* already selected => could be many => move to top? */
450 tmp = [NSMutableArray arrayWithObjects:self->selectionIndexes];
451 [tmp addObject:idxNumber];
452 [self setSelectionIndexes:tmp];
454 if (idxNumber != nil)
455 [self setSelectionIndexes:[NSArray arrayWithObjects:&idxNumber count:1]];
457 [self setSelectionIndexes:nil];
463 /* returns YES if at least one obj matches otherwise NO */
464 - (BOOL)selectObjectsIdenticalTo:(NSArray *)_objs {
465 NSMutableArray *newIndexes;
474 newIndexes = [NSMutableArray arrayWithCapacity:cnt];
476 for (i=0; i<cnt; i++) {
481 obj = [_objs objectAtIndex:i];
482 if (![self->objects containsObject:obj])
486 idx = [self->objects indexOfObject:obj];
487 idxNumber = [NSNumber numberWithUnsignedInt:idx];
489 if ([self->selectionIndexes containsObject:idxNumber])
492 [newIndexes addObject:idxNumber];
497 [newIndexes addObjectsFromArray:self->selectionIndexes];
498 [self setSelectionIndexes:newIndexes];
503 - (BOOL)selectObjectsIdenticalTo:(NSArray *)_objs
504 selectFirstOnNoMatch:(BOOL)_flag
506 if ([self selectObjectsIdenticalTo:_objs])
510 return [self selectObject:[self->displayObjects objectAtIndex:0]];
517 - (void)setObjectArray:(NSArray *)_objects {
518 ASSIGN(self->objects, _objects);
520 /* should try to restore selection */
521 [self clearSelection];
522 if ([_objects isNotEmpty] && [self selectsFirstObjectAfterFetch])
523 [self setSelectionIndexes:uint0Array];
526 - (NSArray *)allObjects {
527 return self->objects;
530 - (NSArray *)displayedObjects {
531 return self->displayObjects;
537 if ([self->delegate respondsToSelector:@selector(displayGroupShouldFetch:)]){
538 if (![self->delegate displayGroupShouldFetch:self])
539 /* delegate rejected fetch-request */
543 objs = [[self dataSource] fetchObjects];
545 [self setObjectArray:objs];
547 if ([self->delegate respondsToSelector:
548 @selector(displayGroup:didFetchObjects:)]) {
549 [self->delegate displayGroup:self didFetchObjects:objs];
552 [self updateDisplayedObjects];
554 if ([self selectsFirstObjectAfterFetch]) {
555 [self clearSelection];
557 if ([objs isNotEmpty])
558 [self setSelectedObject:[objs objectAtIndex:0]];
561 return nil /* stay on page */;
564 - (void)updateDisplayedObjects {
565 NSArray *darray; // display objects
566 NSArray *sarray; // selected objects
568 sarray = [self selectedObjects];
570 if ([self->delegate respondsToSelector:
571 @selector(displayGroup:displayArrayForObjects:)]) {
572 darray = [self->delegate displayGroup:self
573 displayArrayForObjects:[self allObjects]];
575 ASSIGNCOPY(self->displayObjects, darray);
583 ao = [self allObjects];
585 /* apply qualifier */
587 if ((q = [self qualifier]))
588 ao = [ao filteredArrayUsingQualifier:q];
589 #endif // should be done in qualifyDisplayGroup
591 /* apply sort orderings */
592 if ((so = [self sortOrderings]))
593 ao = [ao sortedArrayUsingKeyOrderArray:so];
595 if (ao != self->objects)
596 [self setObjectArray:ao];
601 if ([self batchCount] > 1) {
602 unsigned first = [self indexOfFirstDisplayedObject];
603 unsigned last = [self indexOfLastDisplayedObject];
605 darray = [darray subarrayWithRange:NSMakeRange(first, last-first+1)];
609 darray = [darray copy];
610 RELEASE(self->displayObjects);
611 self->displayObjects = darray;
613 [self selectObjectsIdenticalTo:sarray];
618 - (void)setInQueryMode:(BOOL)_flag {
619 self->flags.inQueryMode = _flag ? 1 : 0;
621 - (BOOL)inQueryMode {
622 return self->flags.inQueryMode ? YES : NO;
625 - (EOQualifier *)qualifierFromQueryValues {
626 NSMutableDictionary *qm, *qmin, *qmax, *qop;
627 NSMutableArray *quals;
631 qm = [self queryMatch];
632 qmin = [self queryMin];
633 qmax = [self queryMax];
634 qop = [self queryOperator];
636 quals = [NSMutableArray arrayWithCapacity:[qm count]];
638 /* construct qualifier for all query-match entries */
640 keys = [qm keyEnumerator];
641 while ((key = [keys nextObject]) != nil) {
647 value = [qm objectForKey:key];
649 if ((op = [qop objectForKey:key]) == nil) {
650 /* default operator is equality */
652 ops = EOQualifierOperatorEqual;
654 else if ([value isKindOfClass:[NSString class]]) {
655 /* strings are treated in a special way */
658 fmt = [self defaultStringMatchFormat];
659 op = [self defaultStringMatchOperator];
660 ops = [EOQualifier operatorSelectorForString:op];
662 value = [NSString stringWithFormat:fmt, value];
665 ops = [EOQualifier operatorSelectorForString:op];
668 q = [[EOKeyValueQualifier alloc]
673 [q release]; q = nil;
676 /* construct min qualifiers */
678 keys = [qmin keyEnumerator];
679 while ((key = [keys nextObject]) != nil) {
683 value = [qmin objectForKey:key];
685 q = [[EOKeyValueQualifier alloc]
687 operatorSelector:EOQualifierOperatorGreaterThan
693 /* construct max qualifiers */
695 keys = [qmax keyEnumerator];
696 while ((key = [keys nextObject]) != nil) {
700 value = [qmax objectForKey:key];
702 q = [[EOKeyValueQualifier alloc]
704 operatorSelector:EOQualifierOperatorLessThan
710 if (![quals isNotEmpty])
712 if ([quals count] == 1)
713 return [quals objectAtIndex:0];
715 return [[[EOAndQualifier alloc] initWithQualifierArray:quals] autorelease];
718 - (NSMutableDictionary *)queryBindings {
719 if (self->_queryBindings == nil)
720 self->_queryBindings = [[NSMutableDictionary alloc] initWithCapacity:8];
721 return self->_queryBindings;
723 - (NSMutableDictionary *)queryMatch {
724 if (self->_queryMatch == nil)
725 self->_queryMatch = [[NSMutableDictionary alloc] initWithCapacity:8];
726 return self->_queryMatch;
728 - (NSMutableDictionary *)queryMin {
729 if (self->_queryMin == nil)
730 self->_queryMin = [[NSMutableDictionary alloc] initWithCapacity:8];
731 return self->_queryMin;
733 - (NSMutableDictionary *)queryMax {
734 if (self->_queryMax == nil)
735 self->_queryMax = [[NSMutableDictionary alloc] initWithCapacity:8];
736 return self->_queryMax;
738 - (NSMutableDictionary *)queryOperator {
739 if (self->_queryOperator == nil)
740 self->_queryOperator = [[NSMutableDictionary alloc] initWithCapacity:8];
741 return self->_queryOperator;
744 - (void)setDefaultStringMatchFormat:(NSString *)_tmp {
745 ASSIGNCOPY(self->defaultStringMatchFormat, _tmp);
747 - (NSString *)defaultStringMatchFormat {
748 return self->defaultStringMatchFormat;
750 - (void)setDefaultStringMatchOperator:(NSString *)_tmp {
751 ASSIGNCOPY(self->defaultStringMatchOperator, _tmp);
753 - (NSString *)defaultStringMatchOperator {
754 return self->defaultStringMatchOperator;
756 + (NSString *)globalDefaultStringMatchFormat {
759 + (NSString *)globalDefaultStringMatchOperator {
760 return @"caseInsensitiveLike";
766 - (void)setQualifier:(EOQualifier *)_q {
767 ASSIGN(self->qualifier, _q);
769 - (EOQualifier *)qualifier {
770 return self->qualifier;
773 - (NSArray *)allQualifierOperators {
774 static NSArray *quals = nil;
776 quals = [[NSArray alloc] initWithObjects:
777 @"=", @"!=", @"<", @"<=", @">", @">=",
778 @"like", @"caseInsensitiveLike", nil];
782 - (NSArray *)stringQualifierOperators {
783 static NSArray *quals = nil;
785 quals = [[NSArray alloc] initWithObjects:
795 - (NSArray *)relationalQualifierOperators {
796 static NSArray *quals = nil;
798 quals = [[NSArray alloc] initWithObjects:
799 @"=", @"!=", @"<", @"<=", @">", @">=", nil];
804 - (void)qualifyDisplayGroup {
807 if ((q = [self qualifierFromQueryValues]) != nil)
808 [self setQualifier:q];
810 [self updateDisplayedObjects];
812 if ([self inQueryMode])
813 [self setInQueryMode:NO];
816 - (void)qualifyDataSource {
819 NSDictionary *bindings;
821 if ((ds = [self dataSource]) == nil)
822 [self warnWithFormat:@"no datasource set: %@", NSStringFromSelector(_cmd)];
824 /* build qualifier */
826 if ((q = [self qualifierFromQueryValues]) != nil)
827 [self setQualifier:q];
829 /* set qualifier in datasource */
831 if ([ds respondsToSelector:@selector(setAuxiliaryQualifier:)]) {
832 [ds setAuxiliaryQualifier:[self qualifier]];
833 //[self logWithFormat:@"set aux qualifier in %@: %@", ds,[self qualifier]];
835 else if ([ds respondsToSelector:@selector(setQualifier:)])
836 [ds setQualifier:[self qualifier]];
838 /* could not qualify ds */
839 [self warnWithFormat:@"could not qualify datasource: %@", ds];
842 /* set bindings in datasource */
844 if ([(bindings = [self queryBindings]) isNotEmpty]) {
845 if ([ds respondsToSelector:@selector(setQualifierBindings:)])
846 [ds setQualifierBindings:bindings];
848 [self warnWithFormat:@"could not set bindings in datasource %@: %@",
855 /* action method, returns 'nil' to stay on page */
858 if ([self inQueryMode])
859 [self setInQueryMode:NO];
862 - (id)qualifyDataSourceAndReturnDisplayCount {
864 This is a 'hack' created because we can't bind (and therefore 'call')
865 'void' methods in .wod files.
867 [self qualifyDataSource];
868 return [NSNumber numberWithUnsignedInt:[[self displayedObjects] count]];
871 /* object creation */
876 idx = [self->selectionIndexes isNotEmpty]
877 ? ([[self->selectionIndexes objectAtIndex:0] unsignedIntValue] + 1)
878 : [self->objects count];
880 return [self insertObjectAtIndex:idx]; /* returns 'nil' */
883 - (id)insertObjectAtIndex:(unsigned)_idx {
886 if ((newObject = [[self dataSource] createObject]) == nil) {
887 [self errorWithFormat:@"Failed to create new object in datasource: %@",
890 if ([self->delegate respondsToSelector:
891 @selector(displayGroup:createObjectFailedForDataSource:)]) {
892 [self->delegate displayGroup:self
893 createObjectFailedForDataSource:[self dataSource]];
895 return nil /* refresh page */;
898 /* apply default values */
900 [newObject takeValuesFromDictionary:[self insertedObjectDefaultValues]];
904 [self insertObject:newObject atIndex:_idx];
906 return nil /* refresh page */;
909 - (void)insertObject:(id)_o atIndex:(unsigned)_idx {
912 /* ask delegate whether we should insert */
913 if ([self->delegate respondsToSelector:
914 @selector(displayGroup:shouldInsertObject:atIndex:)]) {
915 if (![self->delegate displayGroup:self shouldInsertObject:_o atIndex:_idx])
919 /* insert in datasource */
921 [[self dataSource] insertObject:_o];
923 /* update object-array (ignores qualifier for new objects!) */
925 ma = [self->objects mutableCopy];
926 if (_idx <= [ma count])
927 [ma insertObject:_o atIndex:_idx];
931 [self setObjectArray:ma];
932 [ma release]; ma = nil;
933 [self updateDisplayedObjects];
937 [self selectObject:_o]; // TODO: or use setSelectedObject:?
939 /* let delegate know */
940 if ([self->delegate respondsToSelector:
941 @selector(displayGroup:didInsertObject:)])
942 [self->delegate displayGroup:self didInsertObject:_o];
946 /* object deletion */
949 [self deleteSelection];
953 - (BOOL)deleteSelection {
954 NSArray *objsToDelete;
957 objsToDelete = [[[self selectedObjects] shallowCopy] autorelease];
959 for (i = 0, count = [objsToDelete count]; i < count; i++) {
962 idx = [self->objects indexOfObject:[objsToDelete objectAtIndex:i]];
963 if (idx == NSNotFound) {
964 [self errorWithFormat:@"Did not find object in selection: %@",
969 if (![self deleteObjectAtIndex:idx])
975 - (BOOL)deleteObjectAtIndex:(unsigned)_idx {
982 object = (_idx < [self->objects count])
983 ? [[[self->objects objectAtIndex:_idx] retain] autorelease]
985 // TODO: check for nil?
989 if ([self->delegate respondsToSelector:
990 @selector(displayGroup:shouldDeleteObject:)]) {
991 if (![self->delegate displayGroup:self shouldDeleteObject:object])
995 /* delete in datasource */
999 [[self dataSource] deleteObject:object];
1009 ma = [self->objects mutableCopy];
1010 [ma removeObject:object];
1011 [self setObjectArray:ma];
1012 [ma release]; ma = nil;
1013 [self updateDisplayedObjects];
1015 /* notify delegate */
1017 if ([self->delegate respondsToSelector:
1018 @selector(displayGroup:didDeleteObject:)])
1019 [self->delegate displayGroup:self didDeleteObject:object];
1024 /* master / detail */
1026 - (BOOL)hasDetailDataSource {
1027 return [[self dataSource] isKindOfClass:[EODetailDataSource class]];
1030 - (void)setDetailKey:(NSString *)_key {
1031 // TODO: fix me, probably we want to store the key for later
1035 if ([(ds = [self dataSource]) respondsToSelector:_cmd])
1036 [(EODetailDataSource *)ds setDetailKey:_key];
1039 - (NSString *)detailKey {
1042 return ([(ds = [self dataSource]) respondsToSelector:_cmd])
1043 ? [(EODetailDataSource *)ds detailKey] : (NSString *)nil;
1046 - (void)setMasterObject:(id)_object {
1047 [[self dataSource] qualifyWithRelationshipKey:[self detailKey]
1050 if ([self fetchesOnLoad])
1053 - (id)masterObject {
1056 return ([(ds = [self dataSource]) respondsToSelector:_cmd])
1057 ? [(EODetailDataSource *)ds masterObject] : nil;
1063 - (void)takeValue:(id)_value forKeyPath:(NSString *)_keyPath {
1064 if([_keyPath hasPrefix:@"queryMatch."]) {
1065 [[self queryMatch] takeValue:_value
1066 forKey:[_keyPath substringFromIndex:11]];
1068 else if([_keyPath hasPrefix:@"queryMax."])
1069 [[self queryMax] takeValue:_value forKey:[_keyPath substringFromIndex:9]];
1070 else if([_keyPath hasPrefix:@"queryMin."])
1071 [[self queryMin] takeValue:_value forKey:[_keyPath substringFromIndex:9]];
1072 else if([_keyPath hasPrefix:@"queryOperator."]) {
1073 [[self queryOperator] takeValue:_value
1074 forKey:[_keyPath substringFromIndex:14]];
1077 [super takeValue:_value forKeyPath:_keyPath];
1079 - (id)valueForKeyPath:(NSString *)_keyPath {
1080 if ([_keyPath hasPrefix:@"queryMatch."])
1081 return [[self queryMatch] valueForKey:[_keyPath substringFromIndex:11]];
1082 if ([_keyPath hasPrefix:@"queryMax."])
1083 return [[self queryMax] valueForKey:[_keyPath substringFromIndex:9]];
1084 if ([_keyPath hasPrefix:@"queryMin."])
1085 return [[self queryMin] valueForKey:[_keyPath substringFromIndex:9]];
1086 if ([_keyPath hasPrefix:@"queryOperator."])
1087 return [[self queryOperator] valueForKey:[_keyPath substringFromIndex:14]];
1089 return [super valueForKeyPath:_keyPath];
1094 - (id)initWithCoder:(NSCoder *)_coder {
1095 self->dataSource = [[_coder decodeObject] retain];
1096 self->delegate = [_coder decodeObject];
1097 self->sortOrderings = [[_coder decodeObject] copy];
1098 self->insertedObjectDefaults = [[_coder decodeObject] copy];
1099 self->qualifier = [[_coder decodeObject] copy];
1100 self->defaultStringMatchFormat = [[_coder decodeObject] copy];
1101 self->defaultStringMatchOperator = [[_coder decodeObject] copy];
1102 self->_queryBindings = [[_coder decodeObject] copy];
1103 self->_queryMatch = [[_coder decodeObject] copy];
1104 self->_queryMin = [[_coder decodeObject] copy];
1105 self->_queryMax = [[_coder decodeObject] copy];
1106 self->_queryOperator = [[_coder decodeObject] copy];
1111 - (void)encodeWithCoder:(NSCoder *)_coder {
1112 [_coder encodeObject:self->dataSource];
1113 [_coder encodeObject:self->delegate];
1114 [_coder encodeObject:self->sortOrderings];
1115 [_coder encodeObject:self->insertedObjectDefaults];
1116 [_coder encodeObject:self->qualifier];
1117 [_coder encodeObject:self->defaultStringMatchFormat];
1118 [_coder encodeObject:self->defaultStringMatchOperator];
1119 [_coder encodeObject:self->_queryBindings];
1120 [_coder encodeObject:self->_queryMatch];
1121 [_coder encodeObject:self->_queryMin];
1122 [_coder encodeObject:self->_queryMax];
1123 [_coder encodeObject:self->_queryOperator];
1125 [self notImplemented:_cmd];
1130 - (id)initWithKeyValueUnarchiver:(EOKeyValueUnarchiver *)_unarchiver {
1131 if ((self = [self init]) != nil) {
1134 if ((tmp = [_unarchiver decodeObjectForKey:@"formatForLikeQualifier"]))
1135 [self setDefaultStringMatchFormat:tmp];
1137 if ((tmp = [_unarchiver decodeObjectForKey:@"dataSource"]))
1138 [self setDataSource:tmp];
1140 if ((tmp = [_unarchiver decodeObjectForKey:@"numberOfObjectsPerBatch"]))
1141 [self setNumberOfObjectsPerBatch:[tmp intValue]];
1143 [self setFetchesOnLoad:[_unarchiver decodeBoolForKey:@"fetchesOnLoad"]];
1144 [self setSelectsFirstObjectAfterFetch:
1145 [_unarchiver decodeBoolForKey:@"selectsFirstObjectAfterFetch"]];
1150 - (void)encodeWithKeyValueArchiver:(EOKeyValueArchiver *)_archiver {
1151 [_archiver encodeObject:[self defaultStringMatchFormat]
1152 forKey:@"formatForLikeQualifier"];
1153 [_archiver encodeObject:[self dataSource]
1154 forKey:@"dataSource"];
1155 [_archiver encodeObject:
1156 [NSNumber numberWithUnsignedInt:[self numberOfObjectsPerBatch]]
1157 forKey:@"numberOfObjectsPerBatch"];
1158 [_archiver encodeBool:[self fetchesOnLoad]
1159 forKey:@"fetchesOnLoad"];
1160 [_archiver encodeBool:[self selectsFirstObjectAfterFetch]
1161 forKey:@"selectFirstAfterFetch"];
1164 - (void)awakeFromKeyValueUnarchiver:(EOKeyValueUnarchiver *)_unarchiver {
1165 if ([self fetchesOnLoad])
1171 - (void)editingContextWillSaveChanges:(id)_ec {
1173 - (BOOL)editorHasChangesForEditingContext:(id)_ec {
1177 /* EOMessageHandlersImpl */
1179 - (void)editingContext:(id)_ec
1180 presentErrorMessage:(NSString *)_msg
1184 - (BOOL)editingContext:(id)_ec
1185 shouldContinueFetchingWithCurrentObjectCount:(unsigned)_oc
1186 originalLimit:(unsigned)_olimit
1187 objectStore:(id)_store
1194 - (NSString *)description {
1195 return [NSString stringWithFormat:@"<0x%p %@: ds=%@>",
1196 self, NSStringFromClass([self class]),
1200 @end /* WODisplayGroup */