2006-06-15 21:34:10 +02:00
|
|
|
/*
|
|
|
|
Copyright (C) 2004 SKYRIX Software AG
|
|
|
|
|
|
|
|
This file is part of OpenGroupware.org.
|
|
|
|
|
|
|
|
OGo 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
|
|
|
|
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
|
|
|
|
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
#import "common.h"
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
#import <SoObjects/Mailer/SOGoMailBaseObject.h>
|
|
|
|
#import <SoObjects/Mailer/SOGoMailAccount.h>
|
|
|
|
#import <SoObjects/Mailer/SOGoMailFolder.h>
|
|
|
|
#import <NGObjWeb/SoComponent.h>
|
|
|
|
#import <NGObjWeb/SoObject+SoDAV.h>
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
#import "UIxMailTree.h"
|
|
|
|
#import "UIxMailTreeBlock.h"
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Support special icons:
|
|
|
|
tbtv_leaf_corner_17x17.gif
|
|
|
|
tbtv_inbox_17x17.gif
|
|
|
|
tbtv_drafts_17x17.gif
|
|
|
|
tbtv_sent_17x17.gif
|
|
|
|
tbtv_trash_17x17.gif
|
|
|
|
*/
|
|
|
|
|
|
|
|
@interface NSString(DotCutting)
|
|
|
|
|
|
|
|
- (NSString *)stringByCuttingOffAtDotsWhenExceedingLength:(int)_maxLength;
|
|
|
|
|
|
|
|
- (NSString *)titleForSOGoIMAP4String;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation UIxMailTree
|
|
|
|
|
|
|
|
static BOOL debugBlocks = NO;
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
+ (void)initialize
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
[UIxMailTreeBlock class]; // ensure that globals are initialized
|
|
|
|
}
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]))
|
|
|
|
{
|
|
|
|
flattenedNodes = [NSMutableDictionary new];
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
[self->treeFolderAction release];
|
|
|
|
[self->rootClassName release];
|
|
|
|
[self->rootNodes release];
|
|
|
|
[self->item release];
|
2006-07-29 00:59:11 +02:00
|
|
|
[flattenedNodes release];
|
2006-06-15 21:34:10 +02:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* icons */
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
- (NSString *) defaultIconName
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
return @"tbtv_leaf_corner_17x17.gif";
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)iconNameForType:(NSString *)_type {
|
|
|
|
if (![_type isNotNull])
|
|
|
|
return [self defaultIconName];
|
|
|
|
|
|
|
|
//return @"tbtv_drafts_17x17.gif";
|
|
|
|
|
|
|
|
return [self defaultIconName];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* accessors */
|
|
|
|
|
|
|
|
- (void)setRootClassName:(id)_rootClassName {
|
|
|
|
ASSIGNCOPY(self->rootClassName, _rootClassName);
|
|
|
|
}
|
|
|
|
- (id)rootClassName {
|
|
|
|
return self->rootClassName;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setItem:(id)_item {
|
|
|
|
ASSIGN(self->item, _item);
|
|
|
|
}
|
|
|
|
- (id)item {
|
|
|
|
return self->item;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setTreeFolderAction:(NSString *)_action {
|
|
|
|
ASSIGNCOPY(self->treeFolderAction, _action);
|
|
|
|
}
|
|
|
|
- (NSString *)treeFolderAction {
|
|
|
|
return self->treeFolderAction;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)itemIconName {
|
|
|
|
// TODO: only called once!
|
|
|
|
NSString *ftype;
|
|
|
|
|
|
|
|
ftype = [[self item] valueForKey:@"outlookFolderClass"];
|
|
|
|
return [self iconNameForType:ftype];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fetching subfolders */
|
|
|
|
|
|
|
|
- (NSArray *)fetchSubfoldersOfObject:(id)_object {
|
|
|
|
/* Walk over toManyRelationshipKeys and lookup the controllers for them. */
|
|
|
|
NSMutableArray *ma;
|
|
|
|
NSArray *names;
|
|
|
|
unsigned i, count;
|
|
|
|
|
|
|
|
if ((names = [_object toManyRelationshipKeys]) == nil) {
|
|
|
|
if (debugBlocks) [self logWithFormat:@"no to-many: %@", _object];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debugBlocks) {
|
|
|
|
[self logWithFormat:@"to-many: %@ %@", _object,
|
|
|
|
[names componentsJoinedByString:@","]];
|
|
|
|
}
|
2006-07-29 00:59:11 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
count = [names count];
|
|
|
|
ma = [NSMutableArray arrayWithCapacity:(count + 1)];
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
id folder;
|
|
|
|
|
|
|
|
// TODO: use some context or reuse the main context?
|
|
|
|
folder = [_object lookupName:[names objectAtIndex:i] inContext:nil
|
|
|
|
acquire:NO];
|
|
|
|
if (folder == nil) {
|
|
|
|
if (debugBlocks) {
|
|
|
|
[self logWithFormat:@" DID NOT FIND FOLDER %@: %@",
|
|
|
|
_object,
|
|
|
|
[names objectAtIndex:i]];
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ([folder isKindOfClass:[NSException class]]) {
|
|
|
|
if (debugBlocks) {
|
|
|
|
[self logWithFormat:@" FOLDER LOOKUP EXCEPTION %@: %@",
|
|
|
|
[names objectAtIndex:i], folder];
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
[ma addObject:folder];
|
|
|
|
}
|
|
|
|
if (debugBlocks)
|
|
|
|
[self logWithFormat:@" returning: %@ %@", _object, ma];
|
|
|
|
return ma;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* navigation nodes */
|
|
|
|
|
|
|
|
- (BOOL)isRootObject:(id)_object {
|
|
|
|
if (![_object isNotNull]) {
|
|
|
|
[self warnWithFormat:@"(%s): got to root by nil lookup ...",
|
|
|
|
__PRETTY_FUNCTION__];
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([_object isKindOfClass:NSClassFromString(@"SOGoUserFolder")])
|
|
|
|
return YES;
|
|
|
|
|
|
|
|
return [_object isKindOfClass:NSClassFromString([self rootClassName])];
|
|
|
|
}
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
- (NSString *)treeNavigationLinkForObject:(id)_object
|
|
|
|
atDepth:(int)_depth
|
|
|
|
{
|
|
|
|
NSMutableString *link;
|
|
|
|
int i;
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
link = [NSMutableString new];
|
|
|
|
[link autorelease];
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
for (i = 0; i < _depth; i++)
|
2006-07-29 00:59:11 +02:00
|
|
|
[link appendString: @"../"];
|
|
|
|
|
|
|
|
[link appendFormat: @"%@/%@",
|
|
|
|
[_object nameInContainer],
|
|
|
|
[self treeFolderAction]];
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
return link;
|
|
|
|
}
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
- (void) getTitle: (NSString **)_t
|
|
|
|
folderType: (NSString **)_ft
|
|
|
|
andIcon: (NSString **)_icon
|
|
|
|
forObject: (id)_object
|
2006-06-15 21:34:10 +02:00
|
|
|
{
|
|
|
|
// TODO: need to refactor for reuse!
|
|
|
|
NSString *ftype;
|
|
|
|
unsigned len;
|
2006-07-29 00:59:11 +02:00
|
|
|
|
|
|
|
// if ([_object respondsToSelector: @selector (outlookFolderClass)])
|
|
|
|
// ftype = [_object outlookFolderClass];
|
|
|
|
// else
|
|
|
|
ftype = [_object valueForKey:@"outlookFolderClass"];
|
|
|
|
len = [ftype length];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
*_ft = nil;
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
switch (len) {
|
|
|
|
case 8:
|
|
|
|
if ([ftype isEqualToString:@"IPF.Sent"]) {
|
|
|
|
*_t = [self labelForKey:@"SentFolderName"];
|
|
|
|
*_icon = @"tbtv_sent_17x17.gif";
|
2006-07-29 00:59:11 +02:00
|
|
|
*_ft = @"sent";
|
2006-06-15 21:34:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
if ([ftype isEqualToString:@"IPF.Inbox"]) {
|
|
|
|
*_t = [self labelForKey:@"InboxFolderName"];
|
|
|
|
*_icon = @"tbtv_inbox_17x17.gif";
|
2006-07-29 00:59:11 +02:00
|
|
|
*_ft = @"inbox";
|
2006-06-15 21:34:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ([ftype isEqualToString:@"IPF.Trash"]) {
|
|
|
|
*_t = [self labelForKey:@"TrashFolderName"];
|
|
|
|
*_icon = @"tbtv_trash_17x17.gif";
|
2006-07-29 00:59:11 +02:00
|
|
|
*_ft = @"trash";
|
2006-06-15 21:34:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
if ([ftype isEqualToString:@"IPF.Drafts"]) {
|
|
|
|
*_t = [self labelForKey:@"DraftsFolderName"];
|
|
|
|
*_icon = @"tbtv_drafts_17x17.gif";
|
2006-07-29 00:59:11 +02:00
|
|
|
*_ft = @"drafts";
|
2006-06-15 21:34:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ([ftype isEqualToString:@"IPF.Filter"]) {
|
|
|
|
*_t = [self labelForKey:@"SieveFolderName"];
|
|
|
|
*_icon = nil;
|
2006-07-29 00:59:11 +02:00
|
|
|
*_ft = @"sieve";
|
2006-06-15 21:34:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2006-07-04 22:13:47 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
*_t = [_object davDisplayName];
|
|
|
|
*_icon = nil;
|
|
|
|
|
|
|
|
if ([_object isKindOfClass:NSClassFromString(@"SOGoMailFolder")])
|
|
|
|
*_icon = nil;
|
|
|
|
else if ([_object isKindOfClass:NSClassFromString(@"SOGoMailAccount")]) {
|
2006-07-29 00:59:11 +02:00
|
|
|
*_icon = @"tbtv_account_17x17.gif";
|
|
|
|
|
|
|
|
*_ft = @"account";
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
/* title processing is somehow Agenor specific and should be done in UI */
|
|
|
|
*_t = [[_object nameInContainer] titleForSOGoIMAP4String];
|
|
|
|
}
|
|
|
|
else if ([_object isKindOfClass:NSClassFromString(@"SOGoMailAccounts")])
|
2006-07-29 00:59:11 +02:00
|
|
|
*_icon = @"tbtv_account_17x17.gif";
|
2006-06-15 21:34:10 +02:00
|
|
|
else if ([_object isKindOfClass:NSClassFromString(@"SOGoUserFolder")])
|
|
|
|
*_icon = @"tbtv_inbox_17x17.gif";
|
|
|
|
else {
|
|
|
|
// TODO: use drafts icon for other SOGo folders
|
|
|
|
*_icon = @"tbtv_drafts_17x17.gif";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
- (UIxMailTreeBlock *) treeNavigationBlockForLeafNode: (id) _o
|
|
|
|
atDepth: (int) _d
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
UIxMailTreeBlock *md;
|
2006-07-29 00:59:11 +02:00
|
|
|
NSString *n, *i, *ft;
|
2006-06-15 21:34:10 +02:00
|
|
|
id blocks;
|
2006-07-29 00:59:11 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/*
|
|
|
|
Trigger plus in treeview if it has subfolders. It is an optimization that
|
|
|
|
we do not generate blocks for folders which are not displayed anyway.
|
|
|
|
*/
|
|
|
|
blocks = [[_o toManyRelationshipKeys] count] > 0
|
|
|
|
? UIxMailTreeHasChildrenMarker
|
|
|
|
: nil;
|
2006-07-04 22:13:47 +02:00
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
[self getTitle: &n folderType: &ft andIcon: &i forObject:_o];
|
|
|
|
|
|
|
|
md = [UIxMailTreeBlock blockWithName: nil
|
|
|
|
title: n
|
|
|
|
iconName: i
|
|
|
|
link: [self treeNavigationLinkForObject:_o atDepth:_d]
|
|
|
|
isPathNode:NO
|
|
|
|
isActiveNode:NO
|
|
|
|
childBlocks: blocks];
|
2006-06-15 21:34:10 +02:00
|
|
|
return md;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (UIxMailTreeBlock *)treeNavigationBlockForRootNode:(id)_object {
|
|
|
|
/*
|
|
|
|
This generates the block for the root object (root of the tree, we get
|
|
|
|
there by walking up the chain starting with the client object).
|
|
|
|
*/
|
|
|
|
UIxMailTreeBlock *md;
|
|
|
|
NSMutableArray *blocks;
|
|
|
|
NSArray *folders;
|
2006-07-29 00:59:11 +02:00
|
|
|
NSString *title, *icon, *ft;
|
2006-06-15 21:34:10 +02:00
|
|
|
unsigned i, count;
|
|
|
|
|
|
|
|
if (debugBlocks) {
|
|
|
|
[self logWithFormat:@"block for root node 0x%08X<%@>",
|
|
|
|
_object, NSStringFromClass([_object class])];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* process child folders */
|
|
|
|
|
|
|
|
folders = [self fetchSubfoldersOfObject:_object];
|
|
|
|
count = [folders count];
|
|
|
|
blocks = [NSMutableArray arrayWithCapacity:count];
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
id block;
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
block = [self treeNavigationBlockForLeafNode: [folders objectAtIndex:i]
|
2006-06-15 21:34:10 +02:00
|
|
|
atDepth:0];
|
|
|
|
if ([block isNotNull]) [blocks addObject:block];
|
|
|
|
}
|
|
|
|
if ([blocks count] == 0)
|
|
|
|
blocks = nil;
|
|
|
|
|
|
|
|
/* build block */
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
[self getTitle:&title folderType: &ft andIcon:&icon forObject:_object];
|
2006-07-04 22:13:47 +02:00
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
md = [UIxMailTreeBlock blockWithName: [_object nameInContainer]
|
|
|
|
title: title
|
|
|
|
iconName: icon
|
|
|
|
link: [@"../" stringByAppendingString:
|
|
|
|
[_object nameInContainer]]
|
|
|
|
isPathNode: YES
|
|
|
|
isActiveNode: YES
|
|
|
|
childBlocks: blocks];
|
2006-06-15 21:34:10 +02:00
|
|
|
return md;
|
|
|
|
}
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
- (UIxMailTreeBlock *) fullTreeNavigationBlockForNode: (id)_object
|
|
|
|
{
|
|
|
|
UIxMailTreeBlock *md;
|
|
|
|
NSMutableArray *blocks;
|
|
|
|
NSArray *folders;
|
|
|
|
NSString *title, *icon, *ft;
|
|
|
|
unsigned i, count;
|
|
|
|
|
|
|
|
if (debugBlocks)
|
|
|
|
[self logWithFormat:@"block for root node 0x%08X<%@>",
|
|
|
|
_object, NSStringFromClass([_object class])];
|
|
|
|
|
|
|
|
folders = [self fetchSubfoldersOfObject: _object];
|
|
|
|
count = [folders count];
|
|
|
|
blocks = [NSMutableArray arrayWithCapacity: count];
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
id block;
|
|
|
|
|
|
|
|
block = [self fullTreeNavigationBlockForNode: [folders objectAtIndex:i]];
|
|
|
|
if ([block isNotNull]) [blocks addObject:block];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (![blocks count])
|
|
|
|
blocks = nil;
|
|
|
|
|
|
|
|
[self getTitle: &title folderType: &ft andIcon: &icon forObject: _object];
|
|
|
|
// NSLog (@"*********** title = '%@'/icon = '%@'", title, icon);
|
|
|
|
|
|
|
|
md = [UIxMailTreeBlock blockWithName: [_object nameInContainer]
|
|
|
|
title: title
|
|
|
|
iconName: icon
|
|
|
|
link: [@"../" stringByAppendingString:
|
|
|
|
[_object nameInContainer]]
|
|
|
|
isPathNode: YES
|
|
|
|
isActiveNode: YES
|
|
|
|
childBlocks: blocks];
|
|
|
|
[md setFolderType: ft];
|
|
|
|
|
|
|
|
return md;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (UIxMailTreeBlock *) treeNavigationBlockForActiveNode: (id) _object
|
|
|
|
{
|
2006-06-15 21:34:10 +02:00
|
|
|
/*
|
|
|
|
This generates the block for the clientObject (the object which has the
|
|
|
|
focus)
|
|
|
|
*/
|
|
|
|
UIxMailTreeBlock *md;
|
|
|
|
NSMutableArray *blocks;
|
|
|
|
NSArray *folders;
|
2006-07-29 00:59:11 +02:00
|
|
|
NSString *title, *icon, *ft;
|
2006-06-15 21:34:10 +02:00
|
|
|
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];
|
|
|
|
count = [folders count];
|
|
|
|
blocks = [NSMutableArray arrayWithCapacity:count];
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
UIxMailTreeBlock *block;
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
block = [self treeNavigationBlockForLeafNode: [folders objectAtIndex:i]
|
|
|
|
atDepth: 0];
|
2006-06-15 21:34:10 +02:00
|
|
|
if ([block isNotNull]) [blocks addObject:block];
|
|
|
|
}
|
|
|
|
if ([blocks count] == 0) blocks = nil;
|
2006-07-29 00:59:11 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* build block */
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
[self getTitle:&title folderType: &ft andIcon:&icon forObject:_object];
|
|
|
|
md = [UIxMailTreeBlock blockWithName: [_object nameInContainer]
|
|
|
|
title: title
|
|
|
|
iconName: icon
|
|
|
|
link: @"."
|
|
|
|
isPathNode: YES
|
|
|
|
isActiveNode: YES
|
|
|
|
childBlocks: blocks];
|
2006-06-15 21:34:10 +02:00
|
|
|
return md;
|
|
|
|
}
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
- (UIxMailTreeBlock *)
|
|
|
|
treeNavigationBlockForObject: (id) _object
|
|
|
|
withActiveChildBlock: (UIxMailTreeBlock *) _activeChildBlock
|
|
|
|
depth: (int) _depth
|
2006-06-15 21:34:10 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
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;
|
2006-07-29 00:59:11 +02:00
|
|
|
NSString *title, *icon, *ft;
|
2006-06-15 21:34:10 +02:00
|
|
|
unsigned i, count;
|
|
|
|
|
|
|
|
activeName = [_activeChildBlock valueForKey:@"name"];
|
|
|
|
|
|
|
|
/* process child folders */
|
|
|
|
|
|
|
|
folders = [self fetchSubfoldersOfObject:_object];
|
|
|
|
count = [folders count];
|
|
|
|
blocks = [NSMutableArray arrayWithCapacity:count == 0 ? 1 : count];
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
UIxMailTreeBlock *block;
|
|
|
|
id folder;
|
|
|
|
|
|
|
|
folder = [folders objectAtIndex:i];
|
|
|
|
block = [activeName isEqualToString:[folder nameInContainer]]
|
|
|
|
? _activeChildBlock
|
2006-07-29 00:59:11 +02:00
|
|
|
: [self treeNavigationBlockForLeafNode: folder
|
|
|
|
atDepth:_depth];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
if ([block isNotNull]) [blocks addObject:block];
|
|
|
|
}
|
|
|
|
if ([blocks count] == 0) {
|
|
|
|
if (_activeChildBlock != nil) // if the parent has no proper fetchmethod!
|
|
|
|
[blocks addObject:_activeChildBlock];
|
|
|
|
else
|
|
|
|
blocks = nil;
|
|
|
|
}
|
2006-07-29 00:59:11 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* build block */
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
[self getTitle:&title folderType: &ft andIcon:&icon forObject:_object];
|
|
|
|
resultBlock
|
|
|
|
= [UIxMailTreeBlock blockWithName: [_object nameInContainer]
|
|
|
|
title: title
|
|
|
|
iconName: icon
|
|
|
|
link:
|
|
|
|
[self treeNavigationLinkForObject: _object
|
|
|
|
atDepth: (_depth + 1)]
|
|
|
|
isPathNode:YES isActiveNode:NO
|
|
|
|
childBlocks:blocks];
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
/* 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:resultBlock
|
|
|
|
depth:(_depth + 1)];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (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 (debugBlocks) [self logWithFormat:@" active block is root."];
|
|
|
|
return 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
|
2006-07-29 00:59:11 +02:00
|
|
|
depth: 1];
|
2006-06-15 21:34:10 +02:00
|
|
|
if (debugBlocks) [self logWithFormat:@"done: %@", block];
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* tree */
|
|
|
|
|
|
|
|
- (NSArray *)rootNodes {
|
|
|
|
UIxMailTreeBlock *navNode;
|
|
|
|
|
|
|
|
if (self->rootNodes != nil)
|
|
|
|
return self->rootNodes;
|
|
|
|
|
|
|
|
navNode = [self buildNavigationNodesForObject:[self clientObject]];
|
|
|
|
|
|
|
|
if ([navNode hasChildren] && [navNode areChildrenLoaded])
|
|
|
|
self->rootNodes = [[navNode children] retain];
|
|
|
|
else if (navNode)
|
|
|
|
self->rootNodes = [[NSArray alloc] initWithObjects:&navNode count:1];
|
|
|
|
|
|
|
|
return self->rootNodes;
|
|
|
|
}
|
|
|
|
|
2006-07-29 00:59:11 +02:00
|
|
|
- (int) addNodes: (NSArray *) nodes
|
|
|
|
atSerial: (int) startSerial
|
|
|
|
forParent: (int) parent
|
|
|
|
withRootName: (NSString *) rootName
|
|
|
|
toArray: (NSMutableArray *) array
|
|
|
|
{
|
|
|
|
unsigned int count, max, currentSerial;
|
|
|
|
UIxMailTreeBlock *curNode;
|
|
|
|
NSArray *children;
|
|
|
|
NSString *fullName;
|
|
|
|
|
|
|
|
max = [nodes count];
|
|
|
|
currentSerial = startSerial;
|
|
|
|
for (count = 0; count < max; count++)
|
|
|
|
{
|
|
|
|
curNode = [nodes objectAtIndex: count];
|
|
|
|
fullName = [rootName stringByAppendingFormat: @"/%@", [curNode name]];
|
|
|
|
[curNode setName: fullName];
|
|
|
|
[curNode setSerial: currentSerial];
|
|
|
|
[curNode setParent: parent];
|
|
|
|
[array addObject: curNode];
|
|
|
|
if ([curNode hasChildren])
|
|
|
|
currentSerial = [self addNodes: [curNode children]
|
|
|
|
atSerial: currentSerial + 1
|
|
|
|
forParent: currentSerial
|
|
|
|
withRootName: fullName
|
|
|
|
toArray: array];
|
|
|
|
else
|
|
|
|
currentSerial++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return currentSerial;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSArray *) flattenedNodes
|
|
|
|
{
|
|
|
|
NSMutableArray *flattenedBlocks = nil;
|
|
|
|
NSString *userKey;
|
|
|
|
UIxMailTreeBlock *rootNode; // , *curNode;
|
|
|
|
id mailAccounts;
|
|
|
|
// unsigned int count, max;
|
|
|
|
|
|
|
|
userKey = [[self user] login];
|
|
|
|
flattenedBlocks = [flattenedNodes objectForKey: userKey];
|
|
|
|
if (!flattenedBlocks)
|
|
|
|
{
|
|
|
|
flattenedBlocks = [NSMutableArray new];
|
|
|
|
|
|
|
|
if (![[self clientObject] isKindOfClass: NSClassFromString(@"SOGoMailAccounts")])
|
|
|
|
mailAccounts = [[self clientObject] mailAccountsFolder];
|
|
|
|
else
|
|
|
|
mailAccounts = [self clientObject];
|
|
|
|
|
|
|
|
rootNode = [self fullTreeNavigationBlockForNode: mailAccounts];
|
|
|
|
[self addNodes: [rootNode children]
|
|
|
|
atSerial: 1
|
|
|
|
forParent: 0
|
|
|
|
withRootName: @""
|
|
|
|
toArray: flattenedBlocks];
|
|
|
|
|
|
|
|
[flattenedNodes setObject: flattenedBlocks forKey: userKey];
|
|
|
|
// max = [flattenedBlocks count];
|
|
|
|
// for (count = 0; count < max; count++)
|
|
|
|
// {
|
|
|
|
// curNode = [flattenedBlocks objectAtIndex: count];
|
|
|
|
// NSLog (@"%d: %@/%@", count, [curNode title], [curNode iconName]);
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
return flattenedBlocks;
|
|
|
|
}
|
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
/* notifications */
|
|
|
|
|
|
|
|
- (void)sleep {
|
|
|
|
[self->item release]; self->item = nil;
|
|
|
|
[self->rootNodes release]; self->rootNodes = nil;
|
|
|
|
[super sleep];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end /* UIxMailTree */
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSString(DotCutting)
|
|
|
|
|
|
|
|
- (NSString *)stringByCuttingOffAtDotsWhenExceedingLength:(int)_maxLength {
|
|
|
|
NSRange r, r2;
|
|
|
|
NSString *s;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ([self length] <= _maxLength) /* if length is small, return as is */
|
|
|
|
return self;
|
|
|
|
|
|
|
|
if ((r = [self rangeOfString:@"."]).length == 0)
|
|
|
|
/* no dots in share, return even if longer than boundary */
|
|
|
|
return self;
|
|
|
|
|
|
|
|
s = self;
|
|
|
|
i = r.location + r.length;
|
|
|
|
r2 = [s rangeOfString:@"." options:NSLiteralSearch
|
|
|
|
range:NSMakeRange(i, [s length] - i)];
|
|
|
|
|
|
|
|
if (r2.length > 0) {
|
|
|
|
s = [s substringToIndex:r2.location];
|
|
|
|
if ([s length] <= _maxLength) /* if length is small, return as is */
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no second dot, and the whole was too long => cut off after first */
|
|
|
|
return [s substringToIndex:r.location];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)titleForSOGoIMAP4String {
|
|
|
|
/*
|
|
|
|
eg:
|
|
|
|
guizmo.g.-.baluh.hommes.tests-montee-en-charge-ogo@\
|
|
|
|
amelie-01.ac.melanie2.i2
|
|
|
|
*/
|
|
|
|
static int CutOffLength = 16;
|
|
|
|
NSString *s;
|
|
|
|
NSRange r;
|
|
|
|
|
|
|
|
s = self;
|
|
|
|
|
|
|
|
/* check for connect strings without hostnames */
|
|
|
|
|
|
|
|
r = [s rangeOfString:@"@"];
|
|
|
|
if (r.length == 0) {
|
|
|
|
/* no login provide, just use the hostname (without domain) */
|
|
|
|
r = [s rangeOfString:@"."];
|
|
|
|
return r.length > 0 ? [s substringToIndex:r.location] : s;
|
|
|
|
}
|
|
|
|
|
|
|
|
s = [s substringToIndex:r.location];
|
|
|
|
|
|
|
|
/* check for shares */
|
|
|
|
|
|
|
|
r = [s rangeOfString:@".-."];
|
|
|
|
if (r.length > 0) {
|
|
|
|
/* eg: 'baluh.hommes.tests-montee-en-charge-ogo' */
|
|
|
|
s = [s substringFromIndex:(r.location + r.length)];
|
|
|
|
|
|
|
|
return [s stringByCuttingOffAtDotsWhenExceedingLength:CutOffLength];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* just the login name, possibly long (test.et.di.cete-lyon) */
|
|
|
|
return [s stringByCuttingOffAtDotsWhenExceedingLength:CutOffLength];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end /* NSString(DotCutting) */
|