@interface UIxMailTree : UIxComponent
{
+ NSString *rootClassName;
id rootNodes;
id item;
}
@implementation UIxMailTree
+static BOOL debugBlocks = NO;
+
++ (void)initialize {
+ [UIxMailTreeBlock class]; // ensure that globals are initialized
+}
+
- (void)dealloc {
+ [self->rootClassName release];
[self->rootNodes release];
[self->item release];
[super dealloc];
/* accessors */
+- (void)setRootClassName:(id)_rootClassName {
+ ASSIGNCOPY(self->rootClassName, _rootClassName);
+}
+- (id)rootClassName {
+ return self->rootClassName;
+}
+
- (void)setItem:(id)_item {
ASSIGN(self->item, _item);
}
NSArray *names;
unsigned i, count;
- if ((names = [_object toManyRelationshipKeys]) == nil)
+ if ((names = [_object toManyRelationshipKeys]) == nil) {
+ [self logWithFormat:@"no to-many: %@", _object];
return nil;
+ }
+
+ if (debugBlocks) {
+ [self logWithFormat:@"to-many: %@ %@", _object,
+ [names componentsJoinedByString:@","]];
+ }
count = [names count];
ma = [NSMutableArray arrayWithCapacity:(count + 1)];
return YES;
}
- // TODO: make this a parameter to make UIxMailTree reusable
- return [_object isKindOfClass:NSClassFromString(@"SOGoMailAccounts")];
+ return [_object isKindOfClass:NSClassFromString([self rootClassName])];
}
- (NSString *)treeNavigationLinkForObject:(id)_object atDepth:(int)_depth {
we do not generate blocks for folders which are not displayed anyway.
*/
blocks = [[_o toManyRelationshipKeys] count] > 0
- ? [[NSArray alloc] initWithObjects:@"FAKE", nil]
+ ? UIxMailTreeHasChildrenMarker
: nil;
-
+
[self getTitle:&n andIcon:&i forObject:_o];
md = [UIxMailTreeBlock blockWithName:nil
NSArray *folders;
NSString *title, *icon;
unsigned i, count;
+
+ if (debugBlocks) {
+ [self logWithFormat:@"block for root node 0x%08X<%@>",
+ _object, NSStringFromClass([_object class])];
+ }
/* process child folders */
NSArray *folders;
NSString *title, *icon;
unsigned i, count;
-
+
// TODO: maybe we can join the two implementations, this might not be
// necessary
if ([self isRootObject:_object]) /* we are at the top */
return [self treeNavigationBlockForRootNode:_object];
+ if (debugBlocks) {
+ [self logWithFormat:@"block for active node 0x%08X<%@> - %@",
+ _object, NSStringFromClass([_object class]),
+ [_object davDisplayName]];
+ }
+
/* process child folders */
folders = [self fetchSubfoldersOfObject:_object];
withActiveChildBlock:(UIxMailTreeBlock *)_activeChildBlock
depth:(int)_depth
{
- UIxMailTreeBlock *md;
+ /*
+ Note: 'activeChildBlock' here doesn't mean that the block is the selected
+ folder in the tree. Its just the element which is active in the
+ list of subfolders.
+ */
+ UIxMailTreeBlock *resultBlock;
NSMutableArray *blocks;
NSString *activeName;
NSArray *folders;
NSString *title, *icon;
unsigned i, count;
- if ([self isRootObject:_object]) /* we are at the top */
- return _activeChildBlock;
-
- /* the following is not run on the OGoMailAccounts (root) object */
-
activeName = [_activeChildBlock valueForKey:@"name"];
/* process child folders */
/* build block */
[self getTitle:&title andIcon:&icon forObject:_object];
- md = [UIxMailTreeBlock blockWithName:[_object nameInContainer]
- title:title iconName:icon
- link:[self treeNavigationLinkForObject:_object
- atDepth:(_depth + 1)]
- isPathNode:YES isActiveNode:NO
- childBlocks:blocks];
+ resultBlock = [UIxMailTreeBlock blockWithName:[_object nameInContainer]
+ title:title iconName:icon
+ link:
+ [self treeNavigationLinkForObject:_object
+ atDepth:(_depth + 1)]
+ isPathNode:YES isActiveNode:NO
+ childBlocks:blocks];
- /* recurse up */
+ /* recurse up unless we are at the root */
+
+ if ([self isRootObject:_object]) /* we are at the top */
+ return resultBlock;
return [self treeNavigationBlockForObject:[_object container]
- withActiveChildBlock:md
+ withActiveChildBlock:resultBlock
depth:(_depth + 1)];
}
-- (id)buildNavigationNodesForObject:(id)_object {
- id block;
+- (UIxMailTreeBlock *)buildNavigationNodesForObject:(id)_object {
+ /*
+ This is the top-level 'flattening' method. The _object is the active
+ object in the tree, that is, usually a "current folder".
+
+ The tree will show:
+ all subfolders of the current folder,
+ all parent folders of the current folder up to some root,
+ all siblings along the parent chain.
+ */
+ UIxMailTreeBlock *block;
+ /*
+ This is the cursor, we create nodes below that for direct subfolders
+ */
+ if (debugBlocks) [self logWithFormat:@"ACTIVE block ..."];
block = [self treeNavigationBlockForActiveNode:_object];
+ if (debugBlocks) [self logWithFormat:@" ACTIVE block: %@", block];
- if ([self isRootObject:_object])
+ if ([self isRootObject:_object]) {
+ if (debugBlocks) [self logWithFormat:@" active block is root."];
return block;
+ }
- /* the following returns the root block! */
+ /*
+ The following returns the root block. It calculates the chain up to the
+ root folder starting with the parent of the current object.
+ */
+ if (debugBlocks) [self logWithFormat:@"ACTIVE parent block ..."];
block = [self treeNavigationBlockForObject:[_object container]
withActiveChildBlock:block
depth:1];
+ if (debugBlocks) [self logWithFormat:@"done: %@", block];
return block;
}
/* tree */
- (NSArray *)rootNodes {
- id navNode;
+ UIxMailTreeBlock *navNode;
if (self->rootNodes != nil)
return self->rootNodes;
navNode = [self buildNavigationNodesForObject:[self clientObject]];
- self->rootNodes = [[NSArray alloc] initWithObjects:&navNode count:1];
+
+ if ([navNode hasChildren] && [navNode areChildrenLoaded])
+ self->rootNodes = [[navNode children] retain];
+ else if (navNode)
+ self->rootNodes = [[NSArray alloc] initWithObjects:&navNode count:1];
+
return self->rootNodes;
}
@implementation UIxMailTreeBlock
+id UIxMailTreeHasChildrenMarker = nil;
+
++ (void)initialize {
+ // TODO: needs to be an array because the WETreeView requires a
+ // children array
+ UIxMailTreeHasChildrenMarker =
+ [[NSArray alloc] initWithObjects:@"FAKE", nil];
+}
+
+ (id)blockWithName:(NSString *)_name title:(NSString *)_title
iconName:(NSString *)_icon
link:(NSString *)_link isPathNode:(BOOL)_isPath isActiveNode:(BOOL)_isActive
return self->iconName;
}
+- (BOOL)hasChildren {
+ if (self->blocks == UIxMailTreeHasChildrenMarker)
+ return YES;
+ return [self->blocks count] > 0 ? YES : NO;
+}
+
+- (BOOL)areChildrenLoaded {
+ return self->blocks != UIxMailTreeHasChildrenMarker ? YES : NO;
+}
+
- (NSArray *)children {
+ if (self->blocks == UIxMailTreeHasChildrenMarker)
+ // TODO: print a warning
+ return self->blocks;
+
return self->blocks;
}
return self->flags.isActive ? YES : NO;
}
+/* description */
+
+- (void)appendAttributesToDescription:(NSMutableString *)_ms {
+ if (self->name != nil) [_ms appendFormat:@" name='%@'", self->name];
+ if (self->title != nil) [_ms appendFormat:@" title='%@'", self->title];
+
+ if ([self isPathNode]) [_ms appendString:@" path"];
+ if ([self isActiveNode]) [_ms appendString:@" active"];
+
+ if (self->blocks == UIxMailTreeHasChildrenMarker)
+ [_ms appendString:@" has-children"];
+ else if ([self->blocks count] > 0)
+ [_ms appendFormat:@" children=%@", self->blocks];
+}
+
+- (NSString *)description {
+ NSMutableString *ms;
+
+ ms = [NSMutableString stringWithCapacity:64];
+ [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
+ [self appendAttributesToDescription:ms];
+ [ms appendString:@">"];
+ return ms;
+}
+
@end /* UIxMailTreeBlock */