]> err.no Git - sope/blobdiff - sope-appserver/NGObjWeb/WODisplayGroup.m
minor improvement to WOHttpAdaptor, bumped framework revisions
[sope] / sope-appserver / NGObjWeb / WODisplayGroup.m
index b2e3c263c13418aef61f4a82ba9b71d913ae204d..ae3e58ef406217d1613da8b395c84f83217298b1 100644 (file)
@@ -1,24 +1,23 @@
 /*
-  Copyright (C) 2000-2004 SKYRIX Software AG
+  Copyright (C) 2000-2005 SKYRIX Software AG
 
-  This file is part of OpenGroupware.org.
+  This file is part of SOPE.
 
-  OGo is free software; you can redistribute it and/or modify it under
+  SOPE is free software; you can redistribute it and/or modify it under
   the terms of the GNU Lesser General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.
 
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+  SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
   License for more details.
 
   You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
+  License along with SOPE; see the file COPYING.  If not, write to the
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
 
 #include <NGObjWeb/WODisplayGroup.h>
 #import <EOControl/EOControl.h>
 @end
 #endif
 
+
+@interface NSObject(EditingContext)
+- (id)editingContext;
+- (void)addEditor:(id)_editor;
+- (void)removeEditor:(id)_editor;
+- (void)setMessageHandler:(id)_handler;
+- (id)messageHandler;
+@end
+
+
 @implementation WODisplayGroup
 
 static NSNumber *uint0 = nil;
@@ -65,19 +74,19 @@ static NSArray  *uint0Array = nil;
   [[NSNotificationCenter defaultCenter] removeObserver:self];
   [self setDataSource:nil];
 
-  RELEASE(self->_queryMatch);
-  RELEASE(self->_queryMin);
-  RELEASE(self->_queryMax);
-  RELEASE(self->_queryOperator);
-  RELEASE(self->_queryBindings);
-  RELEASE(self->defaultStringMatchFormat);
-  RELEASE(self->defaultStringMatchOperator);
-  RELEASE(self->qualifier);
-  RELEASE(self->objects);
-  RELEASE(self->displayObjects);
-  RELEASE(self->selectionIndexes);
-  RELEASE(self->sortOrderings);
-  RELEASE(self->insertedObjectDefaults);
+  [self->_queryMatch                release];
+  [self->_queryMin                  release];
+  [self->_queryMax                  release];
+  [self->_queryOperator             release];
+  [self->_queryBindings             release];
+  [self->defaultStringMatchFormat   release];
+  [self->defaultStringMatchOperator release];
+  [self->qualifier                  release];
+  [self->objects                    release];
+  [self->displayObjects             release];
+  [self->selectionIndexes           release];
+  [self->sortOrderings              release];
+  [self->insertedObjectDefaults     release];
   [super dealloc];
 }
 
@@ -88,9 +97,9 @@ static NSArray  *uint0Array = nil;
   BOOL doRedisplay;
 
   doRedisplay = YES;
-  if ((d = [self delegate])) {
+  if ((d = [self delegate]) != nil) {
     if ([d respondsToSelector:
-           @selector(displayGroup:shouldRedisplayForChangesInEditingContext:)]) {
+       @selector(displayGroup:shouldRedisplayForChangesInEditingContext:)]) {
       doRedisplay = [d displayGroup:self
                        shouldRedisplayForEditingContextChangeNotification:
                          _notification];
@@ -117,27 +126,31 @@ static NSArray  *uint0Array = nil;
 }
 
 - (void)setDataSource:(EODataSource *)_ds {
-  if (_ds != self->dataSource) {
-#if WITH_EC
-    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
-
-    id ec;
-
-    if ((ec = [self->dataSource editingContext])) {
+  NSNotificationCenter *nc = nil;
+  id ec;
+  
+  if (_ds == self->dataSource)
+    return;
+  
+  /* unregister with old editing context */
+  if ([self->dataSource respondsToSelector:@selector(editingContext)]) {
+    if ((ec = [self->dataSource editingContext]) != nil) {
       [ec removeEditor:self];
       if ([ec messageHandler] == self)
         [ec setMessageHandler:nil];
     
-      [nc removeObserver:self
-          name:@"EOObjectsChangedInEditingContext"
-          object:ec];
+      [[NSNotificationCenter defaultCenter]
+       removeObserver:self
+       name:@"EOObjectsChangedInEditingContext"
+       object:ec];
     }
-#endif
-    
-    ASSIGN(self->dataSource, _ds);
-
-#if WITH_EC
-    if ((ec = [_ds editingContext])) {
+  }
+  
+  ASSIGN(self->dataSource, _ds);
+  
+  /* register with new editing context */
+  if ([self->dataSource respondsToSelector:@selector(editingContext)]) {
+    if ((ec = [self->dataSource editingContext]) != nil) {
       [ec addEditor:self];
       if ([ec messageHandler] == nil)
         [ec setMessageHandler:self];
@@ -147,21 +160,18 @@ static NSArray  *uint0Array = nil;
           name:@"EOObjectsChangedInEditingContext"
           object:ec];
     }
-#endif
-    
-    if ([self->delegate respondsToSelector:
-               @selector(displayGroupDidChangeDataSource:)])
-      [self->delegate displayGroupDidChangeDataSource:self];
   }
+  
+  if ([self->delegate respondsToSelector:
+               @selector(displayGroupDidChangeDataSource:)])
+    [self->delegate displayGroupDidChangeDataSource:self];
 }
 - (EODataSource *)dataSource {
   return self->dataSource;
 }
 
 - (void)setSortOrderings:(NSArray *)_orderings {
-  id tmp = self->sortOrderings;
-  self->sortOrderings = [_orderings copy];
-  RELEASE(tmp);
+  ASSIGNCOPY(self->sortOrderings, _orderings);
 }
 - (NSArray *)sortOrderings {
   return self->sortOrderings;
@@ -175,9 +185,7 @@ static NSArray  *uint0Array = nil;
 }
 
 - (void)setInsertedObjectDefaultValues:(NSDictionary *)_values {
-  id tmp = self->insertedObjectDefaults;
-  self->insertedObjectDefaults = [_values copy];
-  RELEASE(tmp);
+  ASSIGNCOPY(self->insertedObjectDefaults, [_values copy]);
 }
 - (NSDictionary *)insertedObjectDefaultValues {
   return self->insertedObjectDefaults;
@@ -268,7 +276,7 @@ static NSArray  *uint0Array = nil;
   return nil;
 }
 - (id)displayBatchContainingSelectedObject {
-  NSLog(@"WARNING: %s not implemenented", __PRETTY_FUNCTION__);
+  [self warnWithFormat:@"%s not implemenented", __PRETTY_FUNCTION__];
   [self updateDisplayedObjects];
   return nil;
 }
@@ -316,10 +324,10 @@ static NSArray  *uint0Array = nil;
 - (id)selectNext {
   unsigned int idx;
   
-  if ([self->displayObjects count] == 0)
+  if (![self->displayObjects isNotEmpty])
     return nil;
   
-  if ([self->selectionIndexes count] == 0) {
+  if (![self->selectionIndexes isNotEmpty]) {
     [self setSelectionIndexes:uint0Array];
     return nil;
   }
@@ -340,10 +348,10 @@ static NSArray  *uint0Array = nil;
 - (id)selectPrevious {
   unsigned int idx;
   
-  if ([self->displayObjects count] == 0)
+  if (![self->displayObjects isNotEmpty])
     return nil;
   
-  if ([self->selectionIndexes count] == 0) {
+  if (![self->selectionIndexes isNotEmpty]) {
     [self setSelectionIndexes:uint0Array];
     return nil;
   }
@@ -363,15 +371,36 @@ static NSArray  *uint0Array = nil;
 }
 
 - (void)setSelectedObject:(id)_obj {
-  NSLog(@"WARNING: %s not implemented.", __PRETTY_FUNCTION__);
+  unsigned idx;
+  NSNumber *idxNumber;
+  
+  // TODO: maybe we need to retain the selection array and just swap the first
+  
+  idx = [self->objects indexOfObject:_obj];
+  idxNumber = (idx != NSNotFound) ? [NSNumber numberWithUnsignedInt:idx] : nil;
+
+  if (idxNumber != nil)
+    [self setSelectionIndexes:[NSArray arrayWithObjects:&idxNumber count:1]];
+  else
+    [self setSelectionIndexes:nil];
 }
 - (id)selectedObject {
-  return nil;
+  unsigned int i, sCount;
+  
+  if ((sCount = [self->selectionIndexes count]) == 0)
+    return nil;
+  
+  i = [[self->selectionIndexes objectAtIndex:0] unsignedIntValue];
+  if (i >= [self->objects count])
+    return nil;
+  
+  // TODO: need to ensure selection is in displayedObjects?
+  return [self->objects objectAtIndex:i];
 }
 
 - (void)setSelectedObjects:(NSArray *)_objs {
   [self selectObjectsIdenticalTo:_objs];
-  //  NSLog(@"WARNING: %s not implemented.", __PRETTY_FUNCTION__);
+  //  [self warnWithFormat:@"%s not implemented.", __PRETTY_FUNCTION__];
 }
 - (NSArray *)selectedObjects {
   NSMutableArray *result;
@@ -381,7 +410,7 @@ static NSArray  *uint0Array = nil;
   oCount = [self->objects count];
   result = [NSMutableArray arrayWithCapacity:sCount];
   
-  for (i=0; i<sCount; i++) {
+  for (i = 0; i < sCount; i++) {
     unsigned int idx;
 
     idx = [[self->selectionIndexes objectAtIndex:i] unsignedIntValue];
@@ -391,27 +420,35 @@ static NSArray  *uint0Array = nil;
   return result;
 }
 
-/* returns YES if displayedObjects contains _obj otherwise NO */
 - (BOOL)selectObject:(id)_obj {
+  /* returns YES if displayedObjects contains _obj otherwise NO */
   NSNumber *idxNumber;
   unsigned idx;
   
   if (![self->displayObjects containsObject:_obj])
     return NO;
+  
+  idx = [self->objects indexOfObject:_obj];
+  idxNumber = (idx != NSNotFound) ? [NSNumber numberWithUnsignedInt:idx] : nil;
 
-  idx = [self->displayObjects indexOfObject:_obj];
-  idxNumber = [NSNumber numberWithUnsignedInt:idx];
+  // TODO: should we just exchange the first item and/or call
+  //       -setSelectedObject: ?
   
+#if 0 /* this was wrong? */
   if ([self->selectionIndexes containsObject:idxNumber])
+    /* already selected => could be many => move to top? */
     return YES;
-  else {
-    NSMutableArray *tmp;
-
-    tmp = [NSMutableArray arrayWithObjects:self->selectionIndexes];
-    [tmp addObject:idxNumber];
-    [self setSelectionIndexes:tmp];
-    return YES;
-  }
+  
+  tmp = [NSMutableArray arrayWithObjects:self->selectionIndexes];
+  [tmp addObject:idxNumber];
+  [self setSelectionIndexes:tmp];
+#else
+  if (idxNumber != nil)
+    [self setSelectionIndexes:[NSArray arrayWithObjects:&idxNumber count:1]];
+  else
+    [self setSelectionIndexes:nil];
+#endif
+  return YES;
 }
 
 
@@ -456,7 +493,7 @@ static NSArray  *uint0Array = nil;
 }
 
 - (BOOL)selectObjectsIdenticalTo:(NSArray *)_objs
-            selectFirstOnNoMatch:(BOOL)_flag
+  selectFirstOnNoMatch:(BOOL)_flag
 {
   if ([self selectObjectsIdenticalTo:_objs])
     return YES;
@@ -474,9 +511,8 @@ static NSArray  *uint0Array = nil;
   
   /* should try to restore selection */
   [self clearSelection];
-  if ([_objects count] > 0 && [self selectsFirstObjectAfterFetch]) {
+  if ([_objects isNotEmpty] && [self selectsFirstObjectAfterFetch])
     [self setSelectionIndexes:uint0Array];
-  }
 }
  
 - (NSArray *)allObjects {
@@ -490,7 +526,7 @@ static NSArray  *uint0Array = nil;
 - (id)fetch {
   NSArray *objs;
   
-  if ([self->delegate respondsToSelector:@selector(displayGroupShouldFetch:)]) {
+  if ([self->delegate respondsToSelector:@selector(displayGroupShouldFetch:)]){
     if (![self->delegate displayGroupShouldFetch:self])
       /* delegate rejected fetch-request */
       return nil;
@@ -506,15 +542,17 @@ static NSArray  *uint0Array = nil;
   }
 
   [self updateDisplayedObjects];
-
+  
   if ([self selectsFirstObjectAfterFetch]) {
     [self clearSelection];
     
-    if ([objs count] > 0)
+    if ([objs isNotEmpty])
       [self setSelectedObject:[objs objectAtIndex:0]];
-  }  
-  return nil;
+  }
+  
+  return nil /* stay on page */;
 }
+
 - (void)updateDisplayedObjects {
   NSArray *darray; // display  objects
   NSArray *sarray; // selected objects
@@ -526,13 +564,11 @@ static NSArray  *uint0Array = nil;
     darray = [self->delegate displayGroup:self
                              displayArrayForObjects:[self allObjects]];
 
-    darray = [darray copy];
-    RELEASE(self->displayObjects);
-    self->displayObjects = darray;
-
+    ASSIGNCOPY(self->displayObjects, darray);
     return;
   }
-  else {
+  
+  {
 //    EOQualifier *q;
     NSArray     *so, *ao;
     
@@ -594,7 +630,7 @@ static NSArray  *uint0Array = nil;
   /* construct qualifier for all query-match entries */
   
   keys = [qm keyEnumerator];
-  while ((key = [keys nextObject])) {
+  while ((key = [keys nextObject]) != nil) {
     NSString *op;
     SEL      ops;
     id       value;
@@ -626,13 +662,13 @@ static NSArray  *uint0Array = nil;
                               operatorSelector:ops
                               value:value];
     [quals addObject:q];
-    RELEASE(q); q = nil;
+    [q release]; q = nil;
   }
   
   /* construct min qualifiers */
 
   keys = [qmin keyEnumerator];
-  while ((key = [keys nextObject])) {
+  while ((key = [keys nextObject]) != nil) {
     EOQualifier *q;
     id value;
     
@@ -643,68 +679,68 @@ static NSArray  *uint0Array = nil;
                               operatorSelector:EOQualifierOperatorGreaterThan
                               value:value];
     [quals addObject:q];
-    RELEASE(q);
+    [q release];
   }
 
   /* construct max qualifiers */
-
+  
   keys = [qmax keyEnumerator];
-  while ((key = [keys nextObject])) {
+  while ((key = [keys nextObject]) != nil) {
     EOQualifier *q;
     id value;
     
-    value = [qmin objectForKey:key];
+    value = [qmax objectForKey:key];
 
     q = [[EOKeyValueQualifier alloc]
                               initWithKey:key
                               operatorSelector:EOQualifierOperatorLessThan
                               value:value];
     [quals addObject:q];
-    RELEASE(q);
+    [q release];
   }
 
-  if ([quals count] == 0)
+  if (![quals isNotEmpty])
     return nil;
-  else if ([quals count] == 1)
+  if ([quals count] == 1)
     return [quals objectAtIndex:0];
-  else
-    return AUTORELEASE([[EOAndQualifier alloc] initWithQualifierArray:quals]);
+  
+  return [[[EOAndQualifier alloc] initWithQualifierArray:quals] autorelease];
 }
 
 - (NSMutableDictionary *)queryBindings {
   if (self->_queryBindings == nil)
-    self->_queryBindings = [[NSMutableDictionary alloc] init];
+    self->_queryBindings = [[NSMutableDictionary alloc] initWithCapacity:8];
   return self->_queryBindings;
 }
 - (NSMutableDictionary *)queryMatch {
   if (self->_queryMatch == nil)
-    self->_queryMatch = [[NSMutableDictionary alloc] init];
+    self->_queryMatch = [[NSMutableDictionary alloc] initWithCapacity:8];
   return self->_queryMatch;
 }
 - (NSMutableDictionary *)queryMin {
   if (self->_queryMin == nil)
-    self->_queryMin = [[NSMutableDictionary alloc] init];
+    self->_queryMin = [[NSMutableDictionary alloc] initWithCapacity:8];
   return self->_queryMin;
 }
 - (NSMutableDictionary *)queryMax {
   if (self->_queryMax == nil)
-    self->_queryMax = [[NSMutableDictionary alloc] init];
+    self->_queryMax = [[NSMutableDictionary alloc] initWithCapacity:8];
   return self->_queryMax;
 }
 - (NSMutableDictionary *)queryOperator {
   if (self->_queryOperator == nil)
-    self->_queryOperator = [[NSMutableDictionary alloc] init];
+    self->_queryOperator = [[NSMutableDictionary alloc] initWithCapacity:8];
   return self->_queryOperator;
 }
 
 - (void)setDefaultStringMatchFormat:(NSString *)_tmp {
-  ASSIGN(self->defaultStringMatchFormat, _tmp);
+  ASSIGNCOPY(self->defaultStringMatchFormat, _tmp);
 }
 - (NSString *)defaultStringMatchFormat {
   return self->defaultStringMatchFormat;
 }
 - (void)setDefaultStringMatchOperator:(NSString *)_tmp {
-  ASSIGN(self->defaultStringMatchOperator, _tmp);
+  ASSIGNCOPY(self->defaultStringMatchOperator, _tmp);
 }
 - (NSString *)defaultStringMatchOperator {
   return self->defaultStringMatchOperator;
@@ -760,7 +796,7 @@ static NSArray  *uint0Array = nil;
 - (void)qualifyDisplayGroup {
   EOQualifier *q;
 
-  if ((q = [self qualifierFromQueryValues]))
+  if ((q = [self qualifierFromQueryValues]) != nil)
     [self setQualifier:q];
   
   [self updateDisplayedObjects];
@@ -772,29 +808,248 @@ static NSArray  *uint0Array = nil;
 - (void)qualifyDataSource {
   EODataSource *ds;
   EOQualifier  *q;
+  NSDictionary *bindings;
+
+  if ((ds = [self dataSource]) == nil)
+    [self warnWithFormat:@"no datasource set: %@", NSStringFromSelector(_cmd)];
 
-  ds = [self dataSource];
+  /* build qualifier */
   
-  if ((q = [self qualifierFromQueryValues]))
+  if ((q = [self qualifierFromQueryValues]) != nil)
     [self setQualifier:q];
-
-  if ([ds respondsToSelector:@selector(setAuxiliaryQualifier:)])
+  
+  /* set qualifier in datasource */
+  
+  if ([ds respondsToSelector:@selector(setAuxiliaryQualifier:)]) {
     [ds setAuxiliaryQualifier:[self qualifier]];
+    //[self logWithFormat:@"set aux qualifier in %@: %@", ds,[self qualifier]];
+  }
   else if ([ds respondsToSelector:@selector(setQualifier:)])
     [ds setQualifier:[self qualifier]];
   else {
     /* could not qualify ds */
+    [self warnWithFormat:@"could not qualify datasource: %@", ds];
+  }
+  
+  /* set bindings in datasource */
+
+  if ([(bindings = [self queryBindings]) isNotEmpty]) {
+    if ([ds respondsToSelector:@selector(setQualifierBindings:)])
+      [ds setQualifierBindings:bindings];
+    else {
+      [self warnWithFormat:@"could not set bindings in datasource %@: %@", 
+             ds, bindings];
+    }
   }
   
-  if ([ds respondsToSelector:@selector(setQualifierBindings:)])
-    [ds setQualifierBindings:[self queryBindings]];
+  /* perform fetch */
   
+  /* action method, returns 'nil' to stay on page */
   [self fetch];
   
   if ([self inQueryMode])
     [self setInQueryMode:NO];
 }
 
+- (id)qualifyDataSourceAndReturnDisplayCount {
+  /* 
+     This is a 'hack' created because we can't bind (and therefore 'call')
+     'void' methods in .wod files.
+  */
+  [self qualifyDataSource];
+  return [NSNumber numberWithUnsignedInt:[[self displayedObjects] count]];
+}
+
+/* object creation */
+
+- (id)insert {
+  unsigned idx;
+
+  idx = [self->selectionIndexes isNotEmpty]
+    ? ([[self->selectionIndexes objectAtIndex:0] unsignedIntValue] + 1)
+    : [self->objects count];
+  
+  return [self insertObjectAtIndex:idx]; /* returns 'nil' */
+}
+
+- (id)insertObjectAtIndex:(unsigned)_idx {
+  id newObject;
+  
+  if ((newObject = [[self dataSource] createObject]) == nil) {
+    [self errorWithFormat:@"Failed to create new object in datasource: %@",
+           [self dataSource]];
+    
+    if ([self->delegate respondsToSelector:
+              @selector(displayGroup:createObjectFailedForDataSource:)]) {
+      [self->delegate displayGroup:self 
+                     createObjectFailedForDataSource:[self dataSource]];
+    }
+    return nil /* refresh page */;
+  }
+
+  /* apply default values */
+  
+  [newObject takeValuesFromDictionary:[self insertedObjectDefaultValues]];
+  
+  /* insert */
+
+  [self insertObject:newObject atIndex:_idx];
+  
+  return nil /* refresh page */;
+}
+
+- (void)insertObject:(id)_o atIndex:(unsigned)_idx {
+  NSMutableArray *ma;
+  
+  /* ask delegate whether we should insert */
+  if ([self->delegate respondsToSelector:
+            @selector(displayGroup:shouldInsertObject:atIndex:)]) {
+    if (![self->delegate displayGroup:self shouldInsertObject:_o atIndex:_idx])
+      return;
+  }
+  
+  /* insert in datasource */
+  
+  [[self dataSource] insertObject:_o];
+  
+  /* update object-array (ignores qualifier for new objects!) */
+  
+  ma = [self->objects mutableCopy];
+  if (_idx <= [ma count])
+    [ma insertObject:_o atIndex:_idx];
+  else
+    [ma addObject:_o];
+  
+  [self setObjectArray:ma];
+  [ma release]; ma = nil;
+  [self updateDisplayedObjects];
+
+  /* select object */
+  
+  [self selectObject:_o]; // TODO: or use setSelectedObject:?
+  
+  /* let delegate know */
+  if ([self->delegate respondsToSelector:
+            @selector(displayGroup:didInsertObject:)])
+    [self->delegate displayGroup:self didInsertObject:_o];
+}
+
+
+/* object deletion */
+
+- (id)delete {
+  [self deleteSelection];
+  return nil;
+}
+
+- (BOOL)deleteSelection {
+  NSArray  *objsToDelete;
+  unsigned i, count;
+  
+  objsToDelete = [[[self selectedObjects] shallowCopy] autorelease];
+
+  for (i = 0, count = [objsToDelete count]; i < count; i++) {
+    unsigned idx;
+    
+    idx = [self->objects indexOfObject:[objsToDelete objectAtIndex:i]];
+    if (idx == NSNotFound) {
+      [self errorWithFormat:@"Did not find object in selection: %@",
+             objsToDelete];
+      return NO;
+    }
+    
+    if (![self deleteObjectAtIndex:idx])
+      return NO;
+  }
+  return YES;
+}
+
+- (BOOL)deleteObjectAtIndex:(unsigned)_idx {
+  NSMutableArray *ma;
+  id   object;
+  BOOL ok;
+
+  /* find object */
+  
+  object = (_idx < [self->objects count])
+    ? [[[self->objects objectAtIndex:_idx] retain] autorelease]
+    : nil;
+  // TODO: check for nil?
+  
+  /* ask delegate */
+  
+  if ([self->delegate respondsToSelector:
+            @selector(displayGroup:shouldDeleteObject:)]) {
+    if (![self->delegate displayGroup:self shouldDeleteObject:object])
+      return NO;
+  }
+  
+  /* delete in datasource */
+  
+  ok = YES;
+  NS_DURING
+    [[self dataSource] deleteObject:object];
+  NS_HANDLER
+    *(&ok) = NO;
+  NS_ENDHANDLER;
+
+  if (!ok)
+    return NO;
+  
+  /* update array */
+  
+  ma = [self->objects mutableCopy];
+  [ma removeObject:object];
+  [self setObjectArray:ma];
+  [ma release]; ma = nil;
+  [self updateDisplayedObjects];
+  
+  /* notify delegate */
+
+  if ([self->delegate respondsToSelector:
+            @selector(displayGroup:didDeleteObject:)])
+    [self->delegate displayGroup:self didDeleteObject:object];
+  return YES;
+}
+
+
+/* master / detail */
+
+- (BOOL)hasDetailDataSource {
+  return [[self dataSource] isKindOfClass:[EODetailDataSource class]];
+}
+
+- (void)setDetailKey:(NSString *)_key {
+  // TODO: fix me, probably we want to store the key for later
+#if 0
+  EODataSource *ds;
+  
+  if ([(ds = [self dataSource]) respondsToSelector:_cmd])
+    [(EODetailDataSource *)ds setDetailKey:_key];
+#endif
+}
+- (NSString *)detailKey {
+  EODataSource *ds;
+  
+  return ([(ds = [self dataSource]) respondsToSelector:_cmd])
+    ? [(EODetailDataSource *)ds detailKey] : nil;
+}
+
+- (void)setMasterObject:(id)_object {
+  [[self dataSource] qualifyWithRelationshipKey:[self detailKey]
+                    ofObject:_object];
+  
+  if ([self fetchesOnLoad])
+    [self fetch];
+}
+- (id)masterObject {
+  EODataSource *ds;
+  
+  return ([(ds = [self dataSource]) respondsToSelector:_cmd])
+    ? [(EODetailDataSource *)ds masterObject] : nil;
+}
+
+
 /* KVC */
 
 - (void)takeValue:(id)_value forKeyPath:(NSString *)_keyPath {
@@ -862,20 +1117,10 @@ static NSArray  *uint0Array = nil;
   [self notImplemented:_cmd];
 }
 
-/* description */
-
-- (NSString *)description {
-  return [NSString stringWithFormat:@"<0x%08X %@: ds=%@>",
-                     self, NSStringFromClass([self class]),
-                     [self dataSource]];
-}
-
-@end /* WODisplayGroup */
-
-@implementation WODisplayGroup(KVCArchiving)
+/* KVCArchiving */
 
 - (id)initWithKeyValueUnarchiver:(EOKeyValueUnarchiver *)_unarchiver {
-  if ((self = [self init])) {
+  if ((self = [self init]) != nil) {
     id tmp;
     
     if ((tmp = [_unarchiver decodeObjectForKey:@"formatForLikeQualifier"]))
@@ -908,11 +1153,12 @@ static NSArray  *uint0Array = nil;
              forKey:@"selectFirstAfterFetch"];
 }
 
-@end /* KVCArchiving */
-
-@implementation WODisplayGroup(EOEditorsImpl)
+- (void)awakeFromKeyValueUnarchiver:(EOKeyValueUnarchiver *)_unarchiver {
+  if ([self fetchesOnLoad])
+    [self fetch];
+}
 
-#if 0
+/* EOEditorsImpl */
 
 - (void)editingContextWillSaveChanges:(id)_ec {
 }
@@ -920,13 +1166,7 @@ static NSArray  *uint0Array = nil;
   return NO;
 }
 
-#endif
-
-@end /* WODisplayGroup(EOEditorsImpl) */
-
-@implementation WODisplayGroup(EOMessageHandlersImpl)
-
-#if 0
+/* EOMessageHandlersImpl */
 
 - (void)editingContext:(id)_ec
   presentErrorMessage:(NSString *)_msg
@@ -936,11 +1176,17 @@ static NSArray  *uint0Array = nil;
 - (BOOL)editingContext:(id)_ec
   shouldContinueFetchingWithCurrentObjectCount:(unsigned)_oc
   originalLimit:(unsigned)_olimit
-  objectStore:(EOObjectStore *)_store
+  objectStore:(id)_store
 {
   return NO;
 }
 
-#endif
+/* description */
+
+- (NSString *)description {
+  return [NSString stringWithFormat:@"<0x%08X %@: ds=%@>",
+                     self, NSStringFromClass([self class]),
+                     [self dataSource]];
+}
 
-@end /* WODisplayGroup(EOMessageHandlersImpl) */
+@end /* WODisplayGroup */