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 <Foundation/Foundation.h>
25 #import <Foundation/NSNotification.h>
28 @interface EODataSource(DGQualifierSetting)
29 - (void)setAuxiliaryQualifier:(EOQualifier *)_q;
30 - (void)setQualifier:(EOQualifier *)_q;
31 - (void)setQualifierBindings:(NSDictionary *)_bindings;
34 #if APPLE_FOUNDATION_LIBRARY || NeXT_Foundation_LIBRARY
35 @interface NSObject(Miss)
36 - (void)notImplemented:(SEL)cmd;
41 @interface NSObject(EditingContext)
43 - (void)addEditor:(id)_editor;
44 - (void)removeEditor:(id)_editor;
45 - (void)setMessageHandler:(id)_handler;
50 @implementation WODisplayGroup
52 static NSNumber *uint0 = nil;
53 static NSArray *uint0Array = nil;
57 uint0 = [[NSNumber alloc] initWithUnsignedInt:0];
58 if (uint0Array == nil)
59 uint0Array = [[NSArray alloc] initWithObjects:&uint0 count:1];
63 if ((self = [super init])) {
64 [self setDefaultStringMatchFormat:
65 [[self class] globalDefaultStringMatchFormat]];
66 [self setDefaultStringMatchOperator:
67 [[self class] globalDefaultStringMatchOperator]];
68 self->currentBatchIndex = 1;
74 [[NSNotificationCenter defaultCenter] removeObserver:self];
75 [self setDataSource:nil];
77 [self->_queryMatch release];
78 [self->_queryMin release];
79 [self->_queryMax release];
80 [self->_queryOperator release];
81 [self->_queryBindings release];
82 [self->defaultStringMatchFormat release];
83 [self->defaultStringMatchOperator release];
84 [self->qualifier release];
85 [self->objects release];
86 [self->displayObjects release];
87 [self->selectionIndexes release];
88 [self->sortOrderings release];
89 [self->insertedObjectDefaults release];
95 - (void)_objectsChangedInEC:(NSNotification *)_notification {
100 if ((d = [self delegate]) != nil) {
101 if ([d respondsToSelector:
102 @selector(displayGroup:shouldRedisplayForChangesInEditingContext:)]) {
103 doRedisplay = [d displayGroup:self
104 shouldRedisplayForEditingContextChangeNotification:
116 /* contents changed notification ??? */
121 - (void)setDelegate:(id)_delegate {
122 self->delegate = _delegate;
125 return self->delegate;
128 - (void)setDataSource:(EODataSource *)_ds {
129 NSNotificationCenter *nc = nil;
132 if (_ds == self->dataSource)
135 /* unregister with old editing context */
136 if ([self->dataSource respondsToSelector:@selector(editingContext)]) {
137 if ((ec = [self->dataSource editingContext]) != nil) {
138 [ec removeEditor:self];
139 if ([ec messageHandler] == self)
140 [ec setMessageHandler:nil];
142 [[NSNotificationCenter defaultCenter]
144 name:@"EOObjectsChangedInEditingContext"
149 ASSIGN(self->dataSource, _ds);
151 /* register with new editing context */
152 if ([self->dataSource respondsToSelector:@selector(editingContext)]) {
153 if ((ec = [self->dataSource editingContext]) != nil) {
155 if ([ec messageHandler] == nil)
156 [ec setMessageHandler:self];
159 selector:@selector(_objectsChangedInEC:)
160 name:@"EOObjectsChangedInEditingContext"
165 if ([self->delegate respondsToSelector:
166 @selector(displayGroupDidChangeDataSource:)])
167 [self->delegate displayGroupDidChangeDataSource:self];
169 - (EODataSource *)dataSource {
170 return self->dataSource;
173 - (void)setSortOrderings:(NSArray *)_orderings {
174 ASSIGNCOPY(self->sortOrderings, _orderings);
176 - (NSArray *)sortOrderings {
177 return self->sortOrderings;
180 - (void)setFetchesOnLoad:(BOOL)_flag {
181 self->flags.fetchesOnLoad = _flag ? 1 : 0;
183 - (BOOL)fetchesOnLoad {
184 return self->flags.fetchesOnLoad ? YES : NO;
187 - (void)setInsertedObjectDefaultValues:(NSDictionary *)_values {
188 ASSIGNCOPY(self->insertedObjectDefaults, [_values copy]);
190 - (NSDictionary *)insertedObjectDefaultValues {
191 return self->insertedObjectDefaults;
194 - (void)setNumberOfObjectsPerBatch:(unsigned)_count {
195 self->numberOfObjectsPerBatch = _count;
197 - (unsigned)numberOfObjectsPerBatch {
198 return self->numberOfObjectsPerBatch;
201 - (void)setSelectsFirstObjectAfterFetch:(BOOL)_flag {
202 self->flags.selectFirstAfterFetch = _flag ? 1 : 0;
204 - (BOOL)selectsFirstObjectAfterFetch {
205 return self->flags.selectFirstAfterFetch ? YES : NO;
208 - (void)setValidatesChangesImmediatly:(BOOL)_flag {
209 self->flags.validatesChangesImmediatly = _flag ? 1 : 0;
211 - (BOOL)validatesChangesImmediatly {
212 return self->flags.validatesChangesImmediatly ? YES : NO;
217 - (BOOL)hasMultipleBatches {
218 return [self batchCount] > 1 ? YES : NO;
220 - (unsigned)batchCount {
223 doc = [[self allObjects] count];
224 nob = [self numberOfObjectsPerBatch];
228 : doc / nob + ((doc % nob) ? 1 : 0) ;
231 - (void)setCurrentBatchIndex:(unsigned)_index {
232 self->currentBatchIndex = (_index <= [self batchCount]) ? _index : 1;
234 - (unsigned)currentBatchIndex {
235 if (self->currentBatchIndex > [self batchCount])
236 self->currentBatchIndex = 1;
237 return self->currentBatchIndex;
240 - (unsigned)indexOfFirstDisplayedObject {
241 return ([self currentBatchIndex] - 1) * [self numberOfObjectsPerBatch];
244 - (unsigned)indexOfLastDisplayedObject {
245 unsigned nob = [self numberOfObjectsPerBatch];
246 unsigned cnt = [[self allObjects] count];
251 return (([self indexOfFirstDisplayedObject] + nob) < cnt)
252 ? ([self indexOfFirstDisplayedObject] + nob) - 1
256 - (id)displayNextBatch {
257 [self clearSelection];
259 self->currentBatchIndex++;
260 if (self->currentBatchIndex > [self batchCount])
261 self->currentBatchIndex = 1;
263 [self updateDisplayedObjects];
267 - (id)displayPreviousBatch {
268 [self clearSelection];
270 self->currentBatchIndex--;
271 if ([self currentBatchIndex] <= 0)
272 self->currentBatchIndex = [self batchCount];
274 [self updateDisplayedObjects];
278 - (id)displayBatchContainingSelectedObject {
279 [self warnWithFormat:@"%s not implemenented", __PRETTY_FUNCTION__];
280 [self updateDisplayedObjects];
286 - (BOOL)setSelectionIndexes:(NSArray *)_selection {
289 NSSet *before, *after;
292 if ((d = [self delegate])) {
293 if ([d respondsToSelector:
294 @selector(displayGroup:shouldChangeSelectionToIndexes:)]) {
295 ok = [d displayGroup:self shouldChangeSelectionToIndexes:_selection];
301 /* apply selection */
303 before = [NSSet setWithArray:self->selectionIndexes];
304 after = [NSSet setWithArray:_selection];
306 ASSIGN(self->selectionIndexes, _selection);
308 if (![before isEqual:after]) {
309 [d displayGroupDidChangeSelection:self];
310 [d displayGroupDidChangeSelectedObjects:self];
314 - (NSArray *)selectionIndexes {
315 return self->selectionIndexes;
318 - (BOOL)clearSelection {
319 static NSArray *emptyArray = nil;
320 if (emptyArray == nil) emptyArray = [[NSArray alloc] init];
321 return [self setSelectionIndexes:emptyArray];
327 if (![self->displayObjects isNotEmpty])
330 if (![self->selectionIndexes isNotEmpty]) {
331 [self setSelectionIndexes:uint0Array];
335 idx = [[self->selectionIndexes lastObject] unsignedIntValue];
336 if (idx >= ([self->displayObjects count] - 1)) {
337 /* last object is already selected, select first one */
338 [self setSelectionIndexes:uint0Array];
342 /* select next object .. */
343 [self setSelectionIndexes:
344 [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:(idx + 1)]]];
348 - (id)selectPrevious {
351 if (![self->displayObjects isNotEmpty])
354 if (![self->selectionIndexes isNotEmpty]) {
355 [self setSelectionIndexes:uint0Array];
359 idx = [[self->selectionIndexes objectAtIndex:0] unsignedIntValue];
361 /* first object is selected, now select last one */
363 sidx = [NSNumber numberWithUnsignedInt:([self->displayObjects count] - 1)];
364 [self setSelectionIndexes:[NSArray arrayWithObject:sidx]];
367 /* select previous object .. */
368 [self setSelectionIndexes:
369 [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:(idx - 1)]]];
373 - (void)setSelectedObject:(id)_obj {
377 // TODO: maybe we need to retain the selection array and just swap the first
379 idx = [self->objects indexOfObject:_obj];
380 idxNumber = (idx != NSNotFound) ? [NSNumber numberWithUnsignedInt:idx] : nil;
382 if (idxNumber != nil)
383 [self setSelectionIndexes:[NSArray arrayWithObjects:&idxNumber count:1]];
385 [self setSelectionIndexes:nil];
387 - (id)selectedObject {
388 unsigned int i, sCount;
390 if ((sCount = [self->selectionIndexes count]) == 0)
393 i = [[self->selectionIndexes objectAtIndex:0] unsignedIntValue];
394 if (i >= [self->objects count])
397 // TODO: need to ensure selection is in displayedObjects?
398 return [self->objects objectAtIndex:i];
401 - (void)setSelectedObjects:(NSArray *)_objs {
402 [self selectObjectsIdenticalTo:_objs];
403 // [self warnWithFormat:@"%s not implemented.", __PRETTY_FUNCTION__];
405 - (NSArray *)selectedObjects {
406 NSMutableArray *result;
407 unsigned int i, sCount, oCount;
409 sCount = [self->selectionIndexes count];
410 oCount = [self->objects count];
411 result = [NSMutableArray arrayWithCapacity:sCount];
413 for (i = 0; i < sCount; i++) {
416 idx = [[self->selectionIndexes objectAtIndex:i] unsignedIntValue];
418 [result addObject:[self->objects objectAtIndex:idx]];
423 - (BOOL)selectObject:(id)_obj {
424 /* returns YES if displayedObjects contains _obj otherwise NO */
428 if (![self->displayObjects containsObject:_obj])
431 idx = [self->objects indexOfObject:_obj];
432 idxNumber = (idx != NSNotFound) ? [NSNumber numberWithUnsignedInt:idx] : nil;
434 // TODO: should we just exchange the first item and/or call
435 // -setSelectedObject: ?
437 #if 0 /* this was wrong? */
438 if ([self->selectionIndexes containsObject:idxNumber])
439 /* already selected => could be many => move to top? */
442 tmp = [NSMutableArray arrayWithObjects:self->selectionIndexes];
443 [tmp addObject:idxNumber];
444 [self setSelectionIndexes:tmp];
446 if (idxNumber != nil)
447 [self setSelectionIndexes:[NSArray arrayWithObjects:&idxNumber count:1]];
449 [self setSelectionIndexes:nil];
455 /* returns YES if at least one obj matches otherwise NO */
456 - (BOOL)selectObjectsIdenticalTo:(NSArray *)_objs {
457 NSMutableArray *newIndexes;
466 newIndexes = [NSMutableArray arrayWithCapacity:cnt];
468 for (i=0; i<cnt; i++) {
473 obj = [_objs objectAtIndex:i];
474 if (![self->objects containsObject:obj])
478 idx = [self->objects indexOfObject:obj];
479 idxNumber = [NSNumber numberWithUnsignedInt:idx];
481 if ([self->selectionIndexes containsObject:idxNumber])
484 [newIndexes addObject:idxNumber];
489 [newIndexes addObjectsFromArray:self->selectionIndexes];
490 [self setSelectionIndexes:newIndexes];
495 - (BOOL)selectObjectsIdenticalTo:(NSArray *)_objs
496 selectFirstOnNoMatch:(BOOL)_flag
498 if ([self selectObjectsIdenticalTo:_objs])
502 return [self selectObject:[self->displayObjects objectAtIndex:0]];
509 - (void)setObjectArray:(NSArray *)_objects {
510 ASSIGN(self->objects, _objects);
512 /* should try to restore selection */
513 [self clearSelection];
514 if ([_objects isNotEmpty] && [self selectsFirstObjectAfterFetch])
515 [self setSelectionIndexes:uint0Array];
518 - (NSArray *)allObjects {
519 return self->objects;
522 - (NSArray *)displayedObjects {
523 return self->displayObjects;
529 if ([self->delegate respondsToSelector:@selector(displayGroupShouldFetch:)]){
530 if (![self->delegate displayGroupShouldFetch:self])
531 /* delegate rejected fetch-request */
535 objs = [[self dataSource] fetchObjects];
537 [self setObjectArray:objs];
539 if ([self->delegate respondsToSelector:
540 @selector(displayGroup:didFetchObjects:)]) {
541 [self->delegate displayGroup:self didFetchObjects:objs];
544 [self updateDisplayedObjects];
546 if ([self selectsFirstObjectAfterFetch]) {
547 [self clearSelection];
549 if ([objs isNotEmpty])
550 [self setSelectedObject:[objs objectAtIndex:0]];
553 return nil /* stay on page */;
556 - (void)updateDisplayedObjects {
557 NSArray *darray; // display objects
558 NSArray *sarray; // selected objects
560 sarray = [self selectedObjects];
562 if ([self->delegate respondsToSelector:
563 @selector(displayGroup:displayArrayForObjects:)]) {
564 darray = [self->delegate displayGroup:self
565 displayArrayForObjects:[self allObjects]];
567 ASSIGNCOPY(self->displayObjects, darray);
575 ao = [self allObjects];
577 /* apply qualifier */
579 if ((q = [self qualifier]))
580 ao = [ao filteredArrayUsingQualifier:q];
581 #endif // should be done in qualifyDisplayGroup
583 /* apply sort orderings */
584 if ((so = [self sortOrderings]))
585 ao = [ao sortedArrayUsingKeyOrderArray:so];
587 if (ao != self->objects)
588 [self setObjectArray:ao];
593 if ([self batchCount] > 1) {
594 unsigned first = [self indexOfFirstDisplayedObject];
595 unsigned last = [self indexOfLastDisplayedObject];
597 darray = [darray subarrayWithRange:NSMakeRange(first, last-first+1)];
601 darray = [darray copy];
602 RELEASE(self->displayObjects);
603 self->displayObjects = darray;
605 [self selectObjectsIdenticalTo:sarray];
610 - (void)setInQueryMode:(BOOL)_flag {
611 self->flags.inQueryMode = _flag ? 1 : 0;
613 - (BOOL)inQueryMode {
614 return self->flags.inQueryMode ? YES : NO;
617 - (EOQualifier *)qualifierFromQueryValues {
618 NSMutableDictionary *qm, *qmin, *qmax, *qop;
619 NSMutableArray *quals;
623 qm = [self queryMatch];
624 qmin = [self queryMin];
625 qmax = [self queryMax];
626 qop = [self queryOperator];
628 quals = [NSMutableArray arrayWithCapacity:[qm count]];
630 /* construct qualifier for all query-match entries */
632 keys = [qm keyEnumerator];
633 while ((key = [keys nextObject]) != nil) {
639 value = [qm objectForKey:key];
641 if ((op = [qop objectForKey:key]) == nil) {
642 /* default operator is equality */
644 ops = EOQualifierOperatorEqual;
646 else if ([value isKindOfClass:[NSString class]]) {
647 /* strings are treated in a special way */
650 fmt = [self defaultStringMatchFormat];
651 op = [self defaultStringMatchOperator];
652 ops = [EOQualifier operatorSelectorForString:op];
654 value = [NSString stringWithFormat:fmt, value];
657 ops = [EOQualifier operatorSelectorForString:op];
660 q = [[EOKeyValueQualifier alloc]
665 [q release]; q = nil;
668 /* construct min qualifiers */
670 keys = [qmin keyEnumerator];
671 while ((key = [keys nextObject]) != nil) {
675 value = [qmin objectForKey:key];
677 q = [[EOKeyValueQualifier alloc]
679 operatorSelector:EOQualifierOperatorGreaterThan
685 /* construct max qualifiers */
687 keys = [qmax keyEnumerator];
688 while ((key = [keys nextObject]) != nil) {
692 value = [qmax objectForKey:key];
694 q = [[EOKeyValueQualifier alloc]
696 operatorSelector:EOQualifierOperatorLessThan
702 if (![quals isNotEmpty])
704 if ([quals count] == 1)
705 return [quals objectAtIndex:0];
707 return [[[EOAndQualifier alloc] initWithQualifierArray:quals] autorelease];
710 - (NSMutableDictionary *)queryBindings {
711 if (self->_queryBindings == nil)
712 self->_queryBindings = [[NSMutableDictionary alloc] initWithCapacity:8];
713 return self->_queryBindings;
715 - (NSMutableDictionary *)queryMatch {
716 if (self->_queryMatch == nil)
717 self->_queryMatch = [[NSMutableDictionary alloc] initWithCapacity:8];
718 return self->_queryMatch;
720 - (NSMutableDictionary *)queryMin {
721 if (self->_queryMin == nil)
722 self->_queryMin = [[NSMutableDictionary alloc] initWithCapacity:8];
723 return self->_queryMin;
725 - (NSMutableDictionary *)queryMax {
726 if (self->_queryMax == nil)
727 self->_queryMax = [[NSMutableDictionary alloc] initWithCapacity:8];
728 return self->_queryMax;
730 - (NSMutableDictionary *)queryOperator {
731 if (self->_queryOperator == nil)
732 self->_queryOperator = [[NSMutableDictionary alloc] initWithCapacity:8];
733 return self->_queryOperator;
736 - (void)setDefaultStringMatchFormat:(NSString *)_tmp {
737 ASSIGNCOPY(self->defaultStringMatchFormat, _tmp);
739 - (NSString *)defaultStringMatchFormat {
740 return self->defaultStringMatchFormat;
742 - (void)setDefaultStringMatchOperator:(NSString *)_tmp {
743 ASSIGNCOPY(self->defaultStringMatchOperator, _tmp);
745 - (NSString *)defaultStringMatchOperator {
746 return self->defaultStringMatchOperator;
748 + (NSString *)globalDefaultStringMatchFormat {
751 + (NSString *)globalDefaultStringMatchOperator {
752 return @"caseInsensitiveLike";
758 - (void)setQualifier:(EOQualifier *)_q {
759 ASSIGN(self->qualifier, _q);
761 - (EOQualifier *)qualifier {
762 return self->qualifier;
765 - (NSArray *)allQualifierOperators {
766 static NSArray *quals = nil;
768 quals = [[NSArray alloc] initWithObjects:
769 @"=", @"!=", @"<", @"<=", @">", @">=",
770 @"like", @"caseInsensitiveLike", nil];
774 - (NSArray *)stringQualifierOperators {
775 static NSArray *quals = nil;
777 quals = [[NSArray alloc] initWithObjects:
787 - (NSArray *)relationalQualifierOperators {
788 static NSArray *quals = nil;
790 quals = [[NSArray alloc] initWithObjects:
791 @"=", @"!=", @"<", @"<=", @">", @">=", nil];
796 - (void)qualifyDisplayGroup {
799 if ((q = [self qualifierFromQueryValues]) != nil)
800 [self setQualifier:q];
802 [self updateDisplayedObjects];
804 if ([self inQueryMode])
805 [self setInQueryMode:NO];
808 - (void)qualifyDataSource {
811 NSDictionary *bindings;
813 if ((ds = [self dataSource]) == nil)
814 [self warnWithFormat:@"no datasource set: %@", NSStringFromSelector(_cmd)];
816 /* build qualifier */
818 if ((q = [self qualifierFromQueryValues]) != nil)
819 [self setQualifier:q];
821 /* set qualifier in datasource */
823 if ([ds respondsToSelector:@selector(setAuxiliaryQualifier:)]) {
824 [ds setAuxiliaryQualifier:[self qualifier]];
825 //[self logWithFormat:@"set aux qualifier in %@: %@", ds,[self qualifier]];
827 else if ([ds respondsToSelector:@selector(setQualifier:)])
828 [ds setQualifier:[self qualifier]];
830 /* could not qualify ds */
831 [self warnWithFormat:@"could not qualify datasource: %@", ds];
834 /* set bindings in datasource */
836 if ([(bindings = [self queryBindings]) isNotEmpty]) {
837 if ([ds respondsToSelector:@selector(setQualifierBindings:)])
838 [ds setQualifierBindings:bindings];
840 [self warnWithFormat:@"could not set bindings in datasource %@: %@",
847 /* action method, returns 'nil' to stay on page */
850 if ([self inQueryMode])
851 [self setInQueryMode:NO];
854 - (id)qualifyDataSourceAndReturnDisplayCount {
856 This is a 'hack' created because we can't bind (and therefore 'call')
857 'void' methods in .wod files.
859 [self qualifyDataSource];
860 return [NSNumber numberWithUnsignedInt:[[self displayedObjects] count]];
863 /* object creation */
868 idx = [self->selectionIndexes isNotEmpty]
869 ? ([[self->selectionIndexes objectAtIndex:0] unsignedIntValue] + 1)
870 : [self->objects count];
872 return [self insertObjectAtIndex:idx]; /* returns 'nil' */
875 - (id)insertObjectAtIndex:(unsigned)_idx {
878 if ((newObject = [[self dataSource] createObject]) == nil) {
879 [self errorWithFormat:@"Failed to create new object in datasource: %@",
882 if ([self->delegate respondsToSelector:
883 @selector(displayGroup:createObjectFailedForDataSource:)]) {
884 [self->delegate displayGroup:self
885 createObjectFailedForDataSource:[self dataSource]];
887 return nil /* refresh page */;
890 /* apply default values */
892 [newObject takeValuesFromDictionary:[self insertedObjectDefaultValues]];
896 [self insertObject:newObject atIndex:_idx];
898 return nil /* refresh page */;
901 - (void)insertObject:(id)_o atIndex:(unsigned)_idx {
904 /* ask delegate whether we should insert */
905 if ([self->delegate respondsToSelector:
906 @selector(displayGroup:shouldInsertObject:atIndex:)]) {
907 if (![self->delegate displayGroup:self shouldInsertObject:_o atIndex:_idx])
911 /* insert in datasource */
913 [[self dataSource] insertObject:_o];
915 /* update object-array (ignores qualifier for new objects!) */
917 ma = [self->objects mutableCopy];
918 if (_idx <= [ma count])
919 [ma insertObject:_o atIndex:_idx];
923 [self setObjectArray:ma];
924 [ma release]; ma = nil;
925 [self updateDisplayedObjects];
929 [self selectObject:_o]; // TODO: or use setSelectedObject:?
931 /* let delegate know */
932 if ([self->delegate respondsToSelector:
933 @selector(displayGroup:didInsertObject:)])
934 [self->delegate displayGroup:self didInsertObject:_o];
938 /* object deletion */
941 [self deleteSelection];
945 - (BOOL)deleteSelection {
946 NSArray *objsToDelete;
949 objsToDelete = [[[self selectedObjects] shallowCopy] autorelease];
951 for (i = 0, count = [objsToDelete count]; i < count; i++) {
954 idx = [self->objects indexOfObject:[objsToDelete objectAtIndex:i]];
955 if (idx == NSNotFound) {
956 [self errorWithFormat:@"Did not find object in selection: %@",
961 if (![self deleteObjectAtIndex:idx])
967 - (BOOL)deleteObjectAtIndex:(unsigned)_idx {
974 object = (_idx < [self->objects count])
975 ? [[[self->objects objectAtIndex:_idx] retain] autorelease]
977 // TODO: check for nil?
981 if ([self->delegate respondsToSelector:
982 @selector(displayGroup:shouldDeleteObject:)]) {
983 if (![self->delegate displayGroup:self shouldDeleteObject:object])
987 /* delete in datasource */
991 [[self dataSource] deleteObject:object];
1001 ma = [self->objects mutableCopy];
1002 [ma removeObject:object];
1003 [self setObjectArray:ma];
1004 [ma release]; ma = nil;
1005 [self updateDisplayedObjects];
1007 /* notify delegate */
1009 if ([self->delegate respondsToSelector:
1010 @selector(displayGroup:didDeleteObject:)])
1011 [self->delegate displayGroup:self didDeleteObject:object];
1016 /* master / detail */
1018 - (BOOL)hasDetailDataSource {
1019 return [[self dataSource] isKindOfClass:[EODetailDataSource class]];
1022 - (void)setDetailKey:(NSString *)_key {
1023 // TODO: fix me, probably we want to store the key for later
1027 if ([(ds = [self dataSource]) respondsToSelector:_cmd])
1028 [(EODetailDataSource *)ds setDetailKey:_key];
1031 - (NSString *)detailKey {
1034 return ([(ds = [self dataSource]) respondsToSelector:_cmd])
1035 ? [(EODetailDataSource *)ds detailKey] : nil;
1038 - (void)setMasterObject:(id)_object {
1039 [[self dataSource] qualifyWithRelationshipKey:[self detailKey]
1042 if ([self fetchesOnLoad])
1045 - (id)masterObject {
1048 return ([(ds = [self dataSource]) respondsToSelector:_cmd])
1049 ? [(EODetailDataSource *)ds masterObject] : nil;
1055 - (void)takeValue:(id)_value forKeyPath:(NSString *)_keyPath {
1056 if([_keyPath hasPrefix:@"queryMatch."]) {
1057 [[self queryMatch] takeValue:_value
1058 forKey:[_keyPath substringFromIndex:11]];
1060 else if([_keyPath hasPrefix:@"queryMax."])
1061 [[self queryMax] takeValue:_value forKey:[_keyPath substringFromIndex:9]];
1062 else if([_keyPath hasPrefix:@"queryMin."])
1063 [[self queryMin] takeValue:_value forKey:[_keyPath substringFromIndex:9]];
1064 else if([_keyPath hasPrefix:@"queryOperator."]) {
1065 [[self queryOperator] takeValue:_value
1066 forKey:[_keyPath substringFromIndex:14]];
1069 [super takeValue:_value forKeyPath:_keyPath];
1071 - (id)valueForKeyPath:(NSString *)_keyPath {
1072 if ([_keyPath hasPrefix:@"queryMatch."])
1073 return [[self queryMatch] valueForKey:[_keyPath substringFromIndex:11]];
1074 if ([_keyPath hasPrefix:@"queryMax."])
1075 return [[self queryMax] valueForKey:[_keyPath substringFromIndex:9]];
1076 if ([_keyPath hasPrefix:@"queryMin."])
1077 return [[self queryMin] valueForKey:[_keyPath substringFromIndex:9]];
1078 if ([_keyPath hasPrefix:@"queryOperator."])
1079 return [[self queryOperator] valueForKey:[_keyPath substringFromIndex:14]];
1081 return [super valueForKeyPath:_keyPath];
1086 - (id)initWithCoder:(NSCoder *)_coder {
1087 self->dataSource = [[_coder decodeObject] retain];
1088 self->delegate = [_coder decodeObject];
1089 self->sortOrderings = [[_coder decodeObject] copy];
1090 self->insertedObjectDefaults = [[_coder decodeObject] copy];
1091 self->qualifier = [[_coder decodeObject] copy];
1092 self->defaultStringMatchFormat = [[_coder decodeObject] copy];
1093 self->defaultStringMatchOperator = [[_coder decodeObject] copy];
1094 self->_queryBindings = [[_coder decodeObject] copy];
1095 self->_queryMatch = [[_coder decodeObject] copy];
1096 self->_queryMin = [[_coder decodeObject] copy];
1097 self->_queryMax = [[_coder decodeObject] copy];
1098 self->_queryOperator = [[_coder decodeObject] copy];
1103 - (void)encodeWithCoder:(NSCoder *)_coder {
1104 [_coder encodeObject:self->dataSource];
1105 [_coder encodeObject:self->delegate];
1106 [_coder encodeObject:self->sortOrderings];
1107 [_coder encodeObject:self->insertedObjectDefaults];
1108 [_coder encodeObject:self->qualifier];
1109 [_coder encodeObject:self->defaultStringMatchFormat];
1110 [_coder encodeObject:self->defaultStringMatchOperator];
1111 [_coder encodeObject:self->_queryBindings];
1112 [_coder encodeObject:self->_queryMatch];
1113 [_coder encodeObject:self->_queryMin];
1114 [_coder encodeObject:self->_queryMax];
1115 [_coder encodeObject:self->_queryOperator];
1117 [self notImplemented:_cmd];
1122 - (id)initWithKeyValueUnarchiver:(EOKeyValueUnarchiver *)_unarchiver {
1123 if ((self = [self init]) != nil) {
1126 if ((tmp = [_unarchiver decodeObjectForKey:@"formatForLikeQualifier"]))
1127 [self setDefaultStringMatchFormat:tmp];
1129 if ((tmp = [_unarchiver decodeObjectForKey:@"dataSource"]))
1130 [self setDataSource:tmp];
1132 if ((tmp = [_unarchiver decodeObjectForKey:@"numberOfObjectsPerBatch"]))
1133 [self setNumberOfObjectsPerBatch:[tmp intValue]];
1135 [self setFetchesOnLoad:[_unarchiver decodeBoolForKey:@"fetchesOnLoad"]];
1136 [self setSelectsFirstObjectAfterFetch:
1137 [_unarchiver decodeBoolForKey:@"selectsFirstObjectAfterFetch"]];
1142 - (void)encodeWithKeyValueArchiver:(EOKeyValueArchiver *)_archiver {
1143 [_archiver encodeObject:[self defaultStringMatchFormat]
1144 forKey:@"formatForLikeQualifier"];
1145 [_archiver encodeObject:[self dataSource]
1146 forKey:@"dataSource"];
1147 [_archiver encodeObject:
1148 [NSNumber numberWithUnsignedInt:[self numberOfObjectsPerBatch]]
1149 forKey:@"numberOfObjectsPerBatch"];
1150 [_archiver encodeBool:[self fetchesOnLoad]
1151 forKey:@"fetchesOnLoad"];
1152 [_archiver encodeBool:[self selectsFirstObjectAfterFetch]
1153 forKey:@"selectFirstAfterFetch"];
1156 - (void)awakeFromKeyValueUnarchiver:(EOKeyValueUnarchiver *)_unarchiver {
1157 if ([self fetchesOnLoad])
1163 - (void)editingContextWillSaveChanges:(id)_ec {
1165 - (BOOL)editorHasChangesForEditingContext:(id)_ec {
1169 /* EOMessageHandlersImpl */
1171 - (void)editingContext:(id)_ec
1172 presentErrorMessage:(NSString *)_msg
1176 - (BOOL)editingContext:(id)_ec
1177 shouldContinueFetchingWithCurrentObjectCount:(unsigned)_oc
1178 originalLimit:(unsigned)_olimit
1179 objectStore:(id)_store
1186 - (NSString *)description {
1187 return [NSString stringWithFormat:@"<0x%08X %@: ds=%@>",
1188 self, NSStringFromClass([self class]),
1192 @end /* WODisplayGroup */