]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WODisplayGroup.m
fixed WOContext superclass version
[sope] / sope-appserver / NGObjWeb / WODisplayGroup.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include <NGObjWeb/WODisplayGroup.h>
23 #import <EOControl/EOControl.h>
24 #import <Foundation/Foundation.h>
25 #import <Foundation/NSNotification.h>
26 #include "common.h"
27
28 @interface EODataSource(DGQualifierSetting)
29 - (void)setAuxiliaryQualifier:(EOQualifier *)_q;
30 - (void)setQualifier:(EOQualifier *)_q;
31 - (void)setQualifierBindings:(NSDictionary *)_bindings;
32 @end
33
34 #if APPLE_FOUNDATION_LIBRARY || NeXT_Foundation_LIBRARY
35 @interface NSObject(Miss)
36 - (void)notImplemented:(SEL)cmd;
37 @end
38 #endif
39
40 @implementation WODisplayGroup
41
42 static NSNumber *uint0 = nil;
43 static NSArray  *uint0Array = nil;
44
45 + (void)initialize {
46   if (uint0 == nil)
47     uint0 = [[NSNumber alloc] initWithUnsignedInt:0];
48   if (uint0Array == nil)
49     uint0Array = [[NSArray alloc] initWithObjects:&uint0 count:1];
50 }
51
52 - (id)init {
53   if ((self = [super init])) {
54     [self setDefaultStringMatchFormat:
55             [[self class] globalDefaultStringMatchFormat]];
56     [self setDefaultStringMatchOperator:
57             [[self class] globalDefaultStringMatchOperator]];
58     self->currentBatchIndex = 1;
59   }
60   return self;
61 }
62
63 - (void)dealloc {
64   [[NSNotificationCenter defaultCenter] removeObserver:self];
65   [self setDataSource:nil];
66
67   RELEASE(self->_queryMatch);
68   RELEASE(self->_queryMin);
69   RELEASE(self->_queryMax);
70   RELEASE(self->_queryOperator);
71   RELEASE(self->_queryBindings);
72   RELEASE(self->defaultStringMatchFormat);
73   RELEASE(self->defaultStringMatchOperator);
74   RELEASE(self->qualifier);
75   RELEASE(self->objects);
76   RELEASE(self->displayObjects);
77   RELEASE(self->selectionIndexes);
78   RELEASE(self->sortOrderings);
79   RELEASE(self->insertedObjectDefaults);
80   [super dealloc];
81 }
82
83 /* notifications */
84
85 - (void)_objectsChangedInEC:(NSNotification *)_notification {
86   id d;
87   BOOL doRedisplay;
88
89   doRedisplay = YES;
90   if ((d = [self delegate])) {
91     if ([d respondsToSelector:
92            @selector(displayGroup:shouldRedisplayForChangesInEditingContext:)]) {
93       doRedisplay = [d displayGroup:self
94                        shouldRedisplayForEditingContextChangeNotification:
95                          _notification];
96     }
97   }
98
99   if (doRedisplay)
100     [self redisplay];
101 }
102
103 /* display */
104
105 - (void)redisplay {
106   /* contents changed notification ??? */
107 }
108
109 /* accessors */
110
111 - (void)setDelegate:(id)_delegate {
112   self->delegate = _delegate;
113 }
114 - (id)delegate {
115   return self->delegate;
116 }
117
118 - (void)setDataSource:(EODataSource *)_ds {
119   if (_ds != self->dataSource) {
120 #if WITH_EC
121     NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
122
123     id ec;
124
125     if ((ec = [self->dataSource editingContext])) {
126       [ec removeEditor:self];
127       if ([ec messageHandler] == self)
128         [ec setMessageHandler:nil];
129     
130       [nc removeObserver:self
131           name:@"EOObjectsChangedInEditingContext"
132           object:ec];
133     }
134 #endif
135     
136     ASSIGN(self->dataSource, _ds);
137
138 #if WITH_EC
139     if ((ec = [_ds editingContext])) {
140       [ec addEditor:self];
141       if ([ec messageHandler] == nil)
142         [ec setMessageHandler:self];
143       
144       [nc addObserver:self
145           selector:@selector(_objectsChangedInEC:)
146           name:@"EOObjectsChangedInEditingContext"
147           object:ec];
148     }
149 #endif
150     
151     if ([self->delegate respondsToSelector:
152                @selector(displayGroupDidChangeDataSource:)])
153       [self->delegate displayGroupDidChangeDataSource:self];
154   }
155 }
156 - (EODataSource *)dataSource {
157   return self->dataSource;
158 }
159
160 - (void)setSortOrderings:(NSArray *)_orderings {
161   id tmp = self->sortOrderings;
162   self->sortOrderings = [_orderings copy];
163   RELEASE(tmp);
164 }
165 - (NSArray *)sortOrderings {
166   return self->sortOrderings;
167 }
168
169 - (void)setFetchesOnLoad:(BOOL)_flag {
170   self->flags.fetchesOnLoad = _flag ? 1 : 0;
171 }
172 - (BOOL)fetchesOnLoad {
173   return self->flags.fetchesOnLoad ? YES : NO;
174 }
175
176 - (void)setInsertedObjectDefaultValues:(NSDictionary *)_values {
177   id tmp = self->insertedObjectDefaults;
178   self->insertedObjectDefaults = [_values copy];
179   RELEASE(tmp);
180 }
181 - (NSDictionary *)insertedObjectDefaultValues {
182   return self->insertedObjectDefaults;
183 }
184
185 - (void)setNumberOfObjectsPerBatch:(unsigned)_count {
186   self->numberOfObjectsPerBatch = _count;
187 }
188 - (unsigned)numberOfObjectsPerBatch {
189   return self->numberOfObjectsPerBatch;
190 }
191
192 - (void)setSelectsFirstObjectAfterFetch:(BOOL)_flag {
193   self->flags.selectFirstAfterFetch = _flag ? 1 : 0;
194 }
195 - (BOOL)selectsFirstObjectAfterFetch {
196   return self->flags.selectFirstAfterFetch ? YES : NO;
197 }
198
199 - (void)setValidatesChangesImmediatly:(BOOL)_flag {
200   self->flags.validatesChangesImmediatly = _flag ? 1 : 0;
201 }
202 - (BOOL)validatesChangesImmediatly {
203   return self->flags.validatesChangesImmediatly ? YES : NO;
204 }
205
206 /* batches */
207
208 - (BOOL)hasMultipleBatches {
209   return [self batchCount] > 1 ? YES : NO;
210 }
211 - (unsigned)batchCount {
212   unsigned doc, nob;
213   
214   doc = [[self allObjects] count];
215   nob = [self numberOfObjectsPerBatch];
216   
217   return (nob == 0)
218     ? 1
219     : doc / nob + ((doc % nob) ? 1 : 0) ;
220 }
221
222 - (void)setCurrentBatchIndex:(unsigned)_index {
223   self->currentBatchIndex = (_index <= [self batchCount]) ? _index : 1;
224 }
225 - (unsigned)currentBatchIndex {
226   if (self->currentBatchIndex > [self batchCount])
227     self->currentBatchIndex = 1;
228   return self->currentBatchIndex;
229 }
230
231 - (unsigned)indexOfFirstDisplayedObject {
232   return ([self currentBatchIndex] - 1) * [self numberOfObjectsPerBatch];
233 }
234
235 - (unsigned)indexOfLastDisplayedObject {
236   unsigned nob = [self numberOfObjectsPerBatch];
237   unsigned cnt = [[self allObjects] count];
238
239   if (nob == 0)
240     return cnt-1;
241   else
242     return (([self indexOfFirstDisplayedObject] + nob) < cnt)
243       ? ([self indexOfFirstDisplayedObject] + nob) - 1
244       : cnt-1;
245 }
246
247 - (id)displayNextBatch {
248   [self clearSelection];
249   
250   self->currentBatchIndex++;
251   if (self->currentBatchIndex > [self batchCount])
252     self->currentBatchIndex = 1;
253
254   [self updateDisplayedObjects];
255   
256   return nil;
257 }
258 - (id)displayPreviousBatch {
259   [self clearSelection];
260
261   self->currentBatchIndex--;
262   if ([self currentBatchIndex] <= 0)
263     self->currentBatchIndex = [self batchCount];
264   
265   [self updateDisplayedObjects];
266   
267   return nil;
268 }
269 - (id)displayBatchContainingSelectedObject {
270   [self warnWithFormat:@"%s not implemenented", __PRETTY_FUNCTION__];
271   [self updateDisplayedObjects];
272   return nil;
273 }
274
275 /* selection */
276
277 - (BOOL)setSelectionIndexes:(NSArray *)_selection {
278   BOOL ok;
279   id   d;
280   NSSet *before, *after;
281
282   ok = YES;
283   if ((d = [self delegate])) {
284     if ([d respondsToSelector:
285              @selector(displayGroup:shouldChangeSelectionToIndexes:)]) {
286       ok = [d displayGroup:self shouldChangeSelectionToIndexes:_selection];
287     }
288   }
289   if (!ok)
290     return NO;
291   
292   /* apply selection */
293
294   before = [NSSet setWithArray:self->selectionIndexes];
295   after  = [NSSet setWithArray:_selection];
296   
297   ASSIGN(self->selectionIndexes, _selection);
298   
299   if (![before isEqual:after]) {
300     [d displayGroupDidChangeSelection:self];
301     [d displayGroupDidChangeSelectedObjects:self];
302   }
303   return YES;
304 }
305 - (NSArray *)selectionIndexes {
306   return self->selectionIndexes;
307 }
308
309 - (BOOL)clearSelection {
310   static NSArray *emptyArray = nil;
311   if (emptyArray == nil) emptyArray = [[NSArray alloc] init];
312   return [self setSelectionIndexes:emptyArray];
313 }
314
315 - (id)selectNext {
316   unsigned int idx;
317   
318   if ([self->displayObjects count] == 0)
319     return nil;
320   
321   if ([self->selectionIndexes count] == 0) {
322     [self setSelectionIndexes:uint0Array];
323     return nil;
324   }
325   
326   idx = [[self->selectionIndexes lastObject] unsignedIntValue];
327   if (idx >= ([self->displayObjects count] - 1)) {
328     /* last object is already selected, select first one */
329     [self setSelectionIndexes:uint0Array];
330     return nil;
331   }
332   
333   /* select next object .. */
334   [self setSelectionIndexes:
335           [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:(idx + 1)]]];
336   return nil;
337 }
338
339 - (id)selectPrevious {
340   unsigned int idx;
341   
342   if ([self->displayObjects count] == 0)
343     return nil;
344   
345   if ([self->selectionIndexes count] == 0) {
346     [self setSelectionIndexes:uint0Array];
347     return nil;
348   }
349   
350   idx = [[self->selectionIndexes objectAtIndex:0] unsignedIntValue];
351   if (idx == 0) {
352     /* first object is selected, now select last one */
353     NSNumber *sidx;
354     sidx = [NSNumber numberWithUnsignedInt:([self->displayObjects count] - 1)];
355     [self setSelectionIndexes:[NSArray arrayWithObject:sidx]];
356   }
357   
358   /* select previous object .. */
359   [self setSelectionIndexes:
360           [NSArray arrayWithObject:[NSNumber numberWithUnsignedInt:(idx - 1)]]];
361   return nil;
362 }
363
364 - (void)setSelectedObject:(id)_obj {
365   [self warnWithFormat:@"%s not implemented.", __PRETTY_FUNCTION__];
366 }
367 - (id)selectedObject {
368   return nil;
369 }
370
371 - (void)setSelectedObjects:(NSArray *)_objs {
372   [self selectObjectsIdenticalTo:_objs];
373   //  [self warnWithFormat:@"%s not implemented.", __PRETTY_FUNCTION__];
374 }
375 - (NSArray *)selectedObjects {
376   NSMutableArray *result;
377   unsigned int i, sCount, oCount;
378
379   sCount = [self->selectionIndexes count];
380   oCount = [self->objects count];
381   result = [NSMutableArray arrayWithCapacity:sCount];
382   
383   for (i=0; i<sCount; i++) {
384     unsigned int idx;
385
386     idx = [[self->selectionIndexes objectAtIndex:i] unsignedIntValue];
387     if (idx < oCount)
388       [result addObject:[self->objects objectAtIndex:idx]];
389   }
390   return result;
391 }
392
393 /* returns YES if displayedObjects contains _obj otherwise NO */
394 - (BOOL)selectObject:(id)_obj {
395   NSNumber *idxNumber;
396   unsigned idx;
397   
398   if (![self->displayObjects containsObject:_obj])
399     return NO;
400
401   idx = [self->displayObjects indexOfObject:_obj];
402   idxNumber = [NSNumber numberWithUnsignedInt:idx];
403   
404   if ([self->selectionIndexes containsObject:idxNumber])
405     return YES;
406   else {
407     NSMutableArray *tmp;
408
409     tmp = [NSMutableArray arrayWithObjects:self->selectionIndexes];
410     [tmp addObject:idxNumber];
411     [self setSelectionIndexes:tmp];
412     return YES;
413   }
414 }
415
416
417 /* returns YES if at least one obj matches otherwise NO */
418 - (BOOL)selectObjectsIdenticalTo:(NSArray *)_objs {
419   NSMutableArray *newIndexes;
420   unsigned       i, cnt;
421   BOOL           ok = NO;
422
423   cnt = [_objs count];
424   
425   if (cnt == 0)
426     return NO;
427
428   newIndexes = [NSMutableArray arrayWithCapacity:cnt];
429   
430   for (i=0; i<cnt; i++) {
431     NSNumber *idxNumber;
432     unsigned idx;
433     id       obj;
434
435     obj = [_objs objectAtIndex:i];
436     if (![self->objects containsObject:obj])
437       continue;
438
439     ok = YES;
440     idx = [self->objects indexOfObject:obj];
441     idxNumber = [NSNumber numberWithUnsignedInt:idx];
442     
443     if ([self->selectionIndexes containsObject:idxNumber])
444       continue;
445
446     [newIndexes addObject:idxNumber];
447   }
448   if (!ok)
449     return NO;
450
451   [newIndexes addObjectsFromArray:self->selectionIndexes];
452   [self setSelectionIndexes:newIndexes];
453   
454   return YES;
455 }
456
457 - (BOOL)selectObjectsIdenticalTo:(NSArray *)_objs
458             selectFirstOnNoMatch:(BOOL)_flag
459 {
460   if ([self selectObjectsIdenticalTo:_objs])
461     return YES;
462   
463   if (_flag)
464     return [self selectObject:[self->displayObjects objectAtIndex:0]];
465   else
466     return NO;
467 }
468
469 /* objects */
470
471 - (void)setObjectArray:(NSArray *)_objects {
472   ASSIGN(self->objects, _objects);
473   
474   /* should try to restore selection */
475   [self clearSelection];
476   if ([_objects count] > 0 && [self selectsFirstObjectAfterFetch]) {
477     [self setSelectionIndexes:uint0Array];
478   }
479 }
480  
481 - (NSArray *)allObjects {
482   return self->objects;
483 }
484
485 - (NSArray *)displayedObjects {
486   return self->displayObjects;
487 }
488
489 - (id)fetch {
490   NSArray *objs;
491   
492   if ([self->delegate respondsToSelector:@selector(displayGroupShouldFetch:)]) {
493     if (![self->delegate displayGroupShouldFetch:self])
494       /* delegate rejected fetch-request */
495       return nil;
496   }
497
498   objs = [[self dataSource] fetchObjects];
499
500   [self setObjectArray:objs];
501
502   if ([self->delegate respondsToSelector:
503            @selector(displayGroup:didFetchObjects:)]) {
504     [self->delegate displayGroup:self didFetchObjects:objs];
505   }
506
507   [self updateDisplayedObjects];
508
509   if ([self selectsFirstObjectAfterFetch]) {
510     [self clearSelection];
511     
512     if ([objs count] > 0)
513       [self setSelectedObject:[objs objectAtIndex:0]];
514   }  
515   return nil;
516 }
517 - (void)updateDisplayedObjects {
518   NSArray *darray; // display  objects
519   NSArray *sarray; // selected objects
520
521   sarray = [self selectedObjects];
522   
523   if ([self->delegate respondsToSelector:
524            @selector(displayGroup:displayArrayForObjects:)]) {
525     darray = [self->delegate displayGroup:self
526                              displayArrayForObjects:[self allObjects]];
527
528     darray = [darray copy];
529     RELEASE(self->displayObjects);
530     self->displayObjects = darray;
531
532     return;
533   }
534   else {
535 //    EOQualifier *q;
536     NSArray     *so, *ao;
537     
538     ao = [self allObjects];
539
540     /* apply qualifier */
541 #if 0
542     if ((q = [self qualifier]))
543       ao = [ao filteredArrayUsingQualifier:q];
544 #endif // should be done in qualifyDisplayGroup
545
546     /* apply sort orderings */
547     if ((so = [self sortOrderings]))
548       ao = [ao sortedArrayUsingKeyOrderArray:so];
549
550     if (ao != self->objects)
551       [self setObjectArray:ao];
552
553     darray = ao;
554
555     /* apply batch */
556     if ([self batchCount] > 1) {
557       unsigned first = [self indexOfFirstDisplayedObject];
558       unsigned last  = [self indexOfLastDisplayedObject];
559
560       darray = [darray subarrayWithRange:NSMakeRange(first, last-first+1)];
561     }
562   }
563   
564   darray = [darray copy];
565   RELEASE(self->displayObjects);
566   self->displayObjects = darray;
567
568   [self selectObjectsIdenticalTo:sarray];
569 }
570
571 /* query */
572
573 - (void)setInQueryMode:(BOOL)_flag {
574   self->flags.inQueryMode = _flag ? 1 : 0;
575 }
576 - (BOOL)inQueryMode {
577   return self->flags.inQueryMode ? YES : NO;
578 }
579
580 - (EOQualifier *)qualifierFromQueryValues {
581   NSMutableDictionary *qm, *qmin, *qmax, *qop;
582   NSMutableArray *quals;
583   NSEnumerator   *keys;
584   NSString       *key;
585   
586   qm   = [self queryMatch];
587   qmin = [self queryMin];
588   qmax = [self queryMax];
589   qop  = [self queryOperator];
590   
591   quals = [NSMutableArray arrayWithCapacity:[qm count]];
592   
593   /* construct qualifier for all query-match entries */
594   
595   keys = [qm keyEnumerator];
596   while ((key = [keys nextObject])) {
597     NSString *op;
598     SEL      ops;
599     id       value;
600     EOQualifier *q;
601     
602     value = [qm objectForKey:key];
603     
604     if ((op = [qop objectForKey:key]) == nil) {
605       /* default operator is equality */
606       op  = @"=";
607       ops = EOQualifierOperatorEqual;
608     }
609     else if ([value isKindOfClass:[NSString class]]) {
610       /* strings are treated in a special way */
611       NSString *fmt;
612
613       fmt = [self defaultStringMatchFormat];
614       op  = [self defaultStringMatchOperator];
615       ops = [EOQualifier operatorSelectorForString:op];
616       
617       value = [NSString stringWithFormat:fmt, value];
618     }
619     else {
620       ops = [EOQualifier operatorSelectorForString:op];
621     }
622
623     q = [[EOKeyValueQualifier alloc]
624                               initWithKey:key
625                               operatorSelector:ops
626                               value:value];
627     [quals addObject:q];
628     RELEASE(q); q = nil;
629   }
630   
631   /* construct min qualifiers */
632
633   keys = [qmin keyEnumerator];
634   while ((key = [keys nextObject])) {
635     EOQualifier *q;
636     id value;
637     
638     value = [qmin objectForKey:key];
639
640     q = [[EOKeyValueQualifier alloc]
641                               initWithKey:key
642                               operatorSelector:EOQualifierOperatorGreaterThan
643                               value:value];
644     [quals addObject:q];
645     RELEASE(q);
646   }
647
648   /* construct max qualifiers */
649
650   keys = [qmax keyEnumerator];
651   while ((key = [keys nextObject])) {
652     EOQualifier *q;
653     id value;
654     
655     value = [qmin objectForKey:key];
656
657     q = [[EOKeyValueQualifier alloc]
658                               initWithKey:key
659                               operatorSelector:EOQualifierOperatorLessThan
660                               value:value];
661     [quals addObject:q];
662     RELEASE(q);
663   }
664
665   if ([quals count] == 0)
666     return nil;
667   else if ([quals count] == 1)
668     return [quals objectAtIndex:0];
669   else
670     return AUTORELEASE([[EOAndQualifier alloc] initWithQualifierArray:quals]);
671 }
672
673 - (NSMutableDictionary *)queryBindings {
674   if (self->_queryBindings == nil)
675     self->_queryBindings = [[NSMutableDictionary alloc] init];
676   return self->_queryBindings;
677 }
678 - (NSMutableDictionary *)queryMatch {
679   if (self->_queryMatch == nil)
680     self->_queryMatch = [[NSMutableDictionary alloc] init];
681   return self->_queryMatch;
682 }
683 - (NSMutableDictionary *)queryMin {
684   if (self->_queryMin == nil)
685     self->_queryMin = [[NSMutableDictionary alloc] init];
686   return self->_queryMin;
687 }
688 - (NSMutableDictionary *)queryMax {
689   if (self->_queryMax == nil)
690     self->_queryMax = [[NSMutableDictionary alloc] init];
691   return self->_queryMax;
692 }
693 - (NSMutableDictionary *)queryOperator {
694   if (self->_queryOperator == nil)
695     self->_queryOperator = [[NSMutableDictionary alloc] init];
696   return self->_queryOperator;
697 }
698
699 - (void)setDefaultStringMatchFormat:(NSString *)_tmp {
700   ASSIGN(self->defaultStringMatchFormat, _tmp);
701 }
702 - (NSString *)defaultStringMatchFormat {
703   return self->defaultStringMatchFormat;
704 }
705 - (void)setDefaultStringMatchOperator:(NSString *)_tmp {
706   ASSIGN(self->defaultStringMatchOperator, _tmp);
707 }
708 - (NSString *)defaultStringMatchOperator {
709   return self->defaultStringMatchOperator;
710 }
711 + (NSString *)globalDefaultStringMatchFormat {
712   return @"%@*";
713 }
714 + (NSString *)globalDefaultStringMatchOperator {
715   return @"caseInsensitiveLike";
716 }
717
718
719 /* qualfiers */
720
721 - (void)setQualifier:(EOQualifier *)_q {
722   ASSIGN(self->qualifier, _q);
723 }
724 - (EOQualifier *)qualifier {
725   return self->qualifier;
726 }
727
728 - (NSArray *)allQualifierOperators {
729   static NSArray *quals = nil;
730   if (quals == nil) {
731     quals = [[NSArray alloc] initWithObjects:
732                                @"=", @"!=", @"<", @"<=", @">", @">=",
733                                @"like", @"caseInsensitiveLike", nil];
734   }
735   return quals;
736 }
737 - (NSArray *)stringQualifierOperators {
738   static NSArray *quals = nil;
739   if (quals == nil) {
740     quals = [[NSArray alloc] initWithObjects:
741                                @"starts with",
742                                @"contains",
743                                @"ends with",
744                                @"is",
745                                @"like",
746                                nil];
747   }
748   return quals;
749 }
750 - (NSArray *)relationalQualifierOperators {
751   static NSArray *quals = nil;
752   if (quals == nil) {
753     quals = [[NSArray alloc] initWithObjects:
754                                @"=", @"!=", @"<", @"<=", @">", @">=", nil];
755   }
756   return quals;
757 }
758
759 - (void)qualifyDisplayGroup {
760   EOQualifier *q;
761
762   if ((q = [self qualifierFromQueryValues]))
763     [self setQualifier:q];
764   
765   [self updateDisplayedObjects];
766   
767   if ([self inQueryMode])
768     [self setInQueryMode:NO];
769 }
770
771 - (void)qualifyDataSource {
772   EODataSource *ds;
773   EOQualifier  *q;
774
775   ds = [self dataSource];
776   
777   if ((q = [self qualifierFromQueryValues]))
778     [self setQualifier:q];
779
780   if ([ds respondsToSelector:@selector(setAuxiliaryQualifier:)])
781     [ds setAuxiliaryQualifier:[self qualifier]];
782   else if ([ds respondsToSelector:@selector(setQualifier:)])
783     [ds setQualifier:[self qualifier]];
784   else {
785     /* could not qualify ds */
786   }
787   
788   if ([ds respondsToSelector:@selector(setQualifierBindings:)])
789     [ds setQualifierBindings:[self queryBindings]];
790   
791   [self fetch];
792   
793   if ([self inQueryMode])
794     [self setInQueryMode:NO];
795 }
796
797 /* KVC */
798
799 - (void)takeValue:(id)_value forKeyPath:(NSString *)_keyPath {
800   if([_keyPath hasPrefix:@"queryMatch."]) {
801     [[self queryMatch] takeValue:_value 
802                        forKey:[_keyPath substringFromIndex:11]];
803   }
804   else if([_keyPath hasPrefix:@"queryMax."])
805     [[self queryMax] takeValue:_value forKey:[_keyPath substringFromIndex:9]];
806   else if([_keyPath hasPrefix:@"queryMin."])
807     [[self queryMin] takeValue:_value forKey:[_keyPath substringFromIndex:9]];
808   else if([_keyPath hasPrefix:@"queryOperator."]) {
809     [[self queryOperator] takeValue:_value 
810                           forKey:[_keyPath substringFromIndex:14]];
811   }
812   else
813     [super takeValue:_value forKeyPath:_keyPath];
814 }
815 - (id)valueForKeyPath:(NSString *)_keyPath {
816   if ([_keyPath hasPrefix:@"queryMatch."])
817     return [[self queryMatch] valueForKey:[_keyPath substringFromIndex:11]];
818   if ([_keyPath hasPrefix:@"queryMax."])
819     return [[self queryMax] valueForKey:[_keyPath substringFromIndex:9]];
820   if ([_keyPath hasPrefix:@"queryMin."])
821     return [[self queryMin] valueForKey:[_keyPath substringFromIndex:9]];
822   if ([_keyPath hasPrefix:@"queryOperator."])
823     return [[self queryOperator] valueForKey:[_keyPath substringFromIndex:14]];
824
825   return [super valueForKeyPath:_keyPath];
826 }
827
828 /* NSCoding */
829
830 - (id)initWithCoder:(NSCoder *)_coder {
831   self->dataSource                 = [[_coder decodeObject] retain];
832   self->delegate                   = [_coder decodeObject];
833   self->sortOrderings              = [[_coder decodeObject] copy];
834   self->insertedObjectDefaults     = [[_coder decodeObject] copy];
835   self->qualifier                  = [[_coder decodeObject] copy];
836   self->defaultStringMatchFormat   = [[_coder decodeObject] copy];
837   self->defaultStringMatchOperator = [[_coder decodeObject] copy];
838   self->_queryBindings             = [[_coder decodeObject] copy];
839   self->_queryMatch                = [[_coder decodeObject] copy];
840   self->_queryMin                  = [[_coder decodeObject] copy];
841   self->_queryMax                  = [[_coder decodeObject] copy];
842   self->_queryOperator             = [[_coder decodeObject] copy];
843   
844   return self;
845 }
846
847 - (void)encodeWithCoder:(NSCoder *)_coder {
848   [_coder encodeObject:self->dataSource];
849   [_coder encodeObject:self->delegate];
850   [_coder encodeObject:self->sortOrderings];
851   [_coder encodeObject:self->insertedObjectDefaults];
852   [_coder encodeObject:self->qualifier];
853   [_coder encodeObject:self->defaultStringMatchFormat];
854   [_coder encodeObject:self->defaultStringMatchOperator];
855   [_coder encodeObject:self->_queryBindings];
856   [_coder encodeObject:self->_queryMatch];
857   [_coder encodeObject:self->_queryMin];
858   [_coder encodeObject:self->_queryMax];
859   [_coder encodeObject:self->_queryOperator];
860   
861   [self notImplemented:_cmd];
862 }
863
864 /* description */
865
866 - (NSString *)description {
867   return [NSString stringWithFormat:@"<0x%08X %@: ds=%@>",
868                      self, NSStringFromClass([self class]),
869                      [self dataSource]];
870 }
871
872 @end /* WODisplayGroup */
873
874 @implementation WODisplayGroup(KVCArchiving)
875
876 - (id)initWithKeyValueUnarchiver:(EOKeyValueUnarchiver *)_unarchiver {
877   if ((self = [self init])) {
878     id tmp;
879     
880     if ((tmp = [_unarchiver decodeObjectForKey:@"formatForLikeQualifier"]))
881       [self setDefaultStringMatchFormat:tmp];
882     
883     if ((tmp = [_unarchiver decodeObjectForKey:@"dataSource"]))
884       [self setDataSource:tmp];
885
886     if ((tmp = [_unarchiver decodeObjectForKey:@"numberOfObjectsPerBatch"]))
887       [self setNumberOfObjectsPerBatch:[tmp intValue]];
888     
889     [self setFetchesOnLoad:[_unarchiver decodeBoolForKey:@"fetchesOnLoad"]];
890     [self setSelectsFirstObjectAfterFetch:
891           [_unarchiver decodeBoolForKey:@"selectsFirstObjectAfterFetch"]];
892   }
893   return self;
894 }
895
896 - (void)encodeWithKeyValueArchiver:(EOKeyValueArchiver *)_archiver {
897   [_archiver encodeObject:[self defaultStringMatchFormat]
898              forKey:@"formatForLikeQualifier"];
899   [_archiver encodeObject:[self dataSource]
900              forKey:@"dataSource"];
901   [_archiver encodeObject:
902                [NSNumber numberWithUnsignedInt:[self numberOfObjectsPerBatch]]
903              forKey:@"numberOfObjectsPerBatch"];
904   [_archiver encodeBool:[self fetchesOnLoad]
905              forKey:@"fetchesOnLoad"];
906   [_archiver encodeBool:[self selectsFirstObjectAfterFetch]
907              forKey:@"selectFirstAfterFetch"];
908 }
909
910 @end /* KVCArchiving */
911
912 @implementation WODisplayGroup(EOEditorsImpl)
913
914 #if 0
915
916 - (void)editingContextWillSaveChanges:(id)_ec {
917 }
918 - (BOOL)editorHasChangesForEditingContext:(id)_ec {
919   return NO;
920 }
921
922 #endif
923
924 @end /* WODisplayGroup(EOEditorsImpl) */
925
926 @implementation WODisplayGroup(EOMessageHandlersImpl)
927
928 #if 0
929
930 - (void)editingContext:(id)_ec
931   presentErrorMessage:(NSString *)_msg
932 {
933 }
934
935 - (BOOL)editingContext:(id)_ec
936   shouldContinueFetchingWithCurrentObjectCount:(unsigned)_oc
937   originalLimit:(unsigned)_olimit
938   objectStore:(EOObjectStore *)_store
939 {
940   return NO;
941 }
942
943 #endif
944
945 @end /* WODisplayGroup(EOMessageHandlersImpl) */