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