/* Copyright (C) 2000-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. */ // $Id$ #include "UIxTabView.h" #include "common.h" #import #import #import #include #if DEBUG // # define DEBUG_TAKEVALUES 1 # define DEBUG_JS 1 #endif /* context keys */ NSString *UIxTabView_HEAD = @"UIxTabView_head"; NSString *UIxTabView_BODY = @"UIxTabView_body"; NSString *UIxTabView_KEYS = @"UIxTabView_keys"; NSString *UIxTabView_SCRIPT = @"UIxTabView_script"; NSString *UIxTabView_ACTIVEKEY = @"UIxTabView_activekey"; NSString *UIxTabView_COLLECT = @"~tv~"; @implementation UIxTabView static NSNumber *YesNumber; + (void)initialize { if (YesNumber == nil) YesNumber = [[NSNumber numberWithBool:YES] retain]; } + (int)version { return [super version] + 0; } - (id)initWithName:(NSString *)_name associations:(NSDictionary *)_config template:(WOElement *)_subs { if ((self = [super initWithName:_name associations:_config template:_subs])) { self->selection = WOExtGetProperty(_config, @"selection"); self->headerStyle = WOExtGetProperty(_config, @"headerStyle"); self->bodyStyle = WOExtGetProperty(_config, @"bodyStyle"); self->tabStyle = WOExtGetProperty(_config, @"tabStyle"); self->selectedTabStyle = WOExtGetProperty(_config, @"selectedTabStyle"); self->bgColor = WOExtGetProperty(_config, @"bgColor"); self->nonSelectedBgColor = WOExtGetProperty(_config, @"nonSelectedBgColor"); self->leftCornerIcon = WOExtGetProperty(_config, @"leftCornerIcon"); self->rightCornerIcon = WOExtGetProperty(_config, @"rightCornerIcon"); self->tabIcon = WOExtGetProperty(_config, @"tabIcon"); self->leftTabIcon = WOExtGetProperty(_config, @"leftTabIcon"); self->selectedTabIcon = WOExtGetProperty(_config, @"selectedTabIcon"); self->asBackground = WOExtGetProperty(_config, @"asBackground"); self->width = WOExtGetProperty(_config, @"width"); self->height = WOExtGetProperty(_config, @"height"); self->activeBgColor = WOExtGetProperty(_config, @"activeBgColor"); self->inactiveBgColor = WOExtGetProperty(_config, @"inactiveBgColor"); self->fontColor = WOExtGetProperty(_config, @"fontColor"); self->fontSize = WOExtGetProperty(_config, @"fontSize"); self->fontFace = WOExtGetProperty(_config, @"fontFace"); self->template = RETAIN(_subs); } return self; } - (void)dealloc { [self->selection release]; [self->headerStyle release]; [self->bodyStyle release]; [self->tabStyle release]; [self->selectedTabStyle release]; RELEASE(self->bgColor); RELEASE(self->nonSelectedBgColor); RELEASE(self->leftCornerIcon); RELEASE(self->rightCornerIcon); RELEASE(self->leftTabIcon); RELEASE(self->selectedTabIcon); RELEASE(self->tabIcon); RELEASE(self->width); RELEASE(self->height); RELEASE(self->activeBgColor); RELEASE(self->inactiveBgColor); RELEASE(self->fontColor); RELEASE(self->fontSize); RELEASE(self->fontFace); RELEASE(self->template); [super dealloc]; } /* nesting */ - (id)saveNestedStateInContext:(WOContext *)_ctx { return nil; } - (void)restoreNestedState:(id)_state inContext:(WOContext *)_ctx { if (_state == nil) return; } - (NSArray *)collectKeysInContext:(WOContext *)_ctx { /* collect mode, collects all keys */ [_ctx setObject:UIxTabView_COLLECT forKey:UIxTabView_HEAD]; [self->template appendToResponse:nil inContext:_ctx]; [_ctx removeObjectForKey:UIxTabView_HEAD]; return [_ctx objectForKey:UIxTabView_KEYS]; } /* responder */ - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx { id nestedState; NSString *activeTabKey; activeTabKey = [self->selection stringValueInComponent:[_ctx component]]; NSLog(@"%s activeTabKey:%@", __PRETTY_FUNCTION__, activeTabKey); nestedState = [self saveNestedStateInContext:_ctx]; [_ctx appendElementIDComponent:@"b"]; [_ctx appendElementIDComponent:activeTabKey]; [_ctx setObject:activeTabKey forKey:UIxTabView_BODY]; #if DEBUG_TAKEVALUES [[_ctx component] debugWithFormat:@"UIxTabView: body takes values, eid='%@'", [_ctx elementID]]; #endif [self->template takeValuesFromRequest:_req inContext:_ctx]; [_ctx removeObjectForKey:UIxTabView_BODY]; [_ctx deleteLastElementIDComponent]; // activeKey [_ctx deleteLastElementIDComponent]; /* 'b' */ [self restoreNestedState:nestedState inContext:_ctx]; } - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx { NSString *key; id result; id nestedState; if ((key = [_ctx currentElementID]) == nil) return nil; result = nil; nestedState = [self saveNestedStateInContext:_ctx]; if ([key isEqualToString:@"h"]) { /* header action */ //NSString *urlKey; [_ctx consumeElementID]; [_ctx appendElementIDComponent:@"h"]; #if 0 if ((urlKey = [_ctx currentElementID]) == nil) { [[_ctx application] debugWithFormat:@"missing active head tab key !"]; } else { //NSLog(@"clicked: %@", urlKey); [_ctx consumeElementID]; [_ctx appendElementIDComponent:urlKey]; } #endif [_ctx setObject:self->selection forKey:UIxTabView_HEAD]; result = [self->template invokeActionForRequest:_req inContext:_ctx]; [_ctx removeObjectForKey:UIxTabView_HEAD]; #if 0 if (urlKey) [_ctx deleteLastElementIDComponent]; // active key #endif [_ctx deleteLastElementIDComponent]; // 'h' } else if ([key isEqualToString:@"b"]) { /* body action */ NSString *activeTabKey, *urlKey; [_ctx consumeElementID]; [_ctx appendElementIDComponent:@"b"]; if ((urlKey = [_ctx currentElementID]) == nil) { [[_ctx application] debugWithFormat:@"missing active body tab key !"]; } else { //NSLog(@"clicked: %@", urlKey); [_ctx consumeElementID]; [_ctx appendElementIDComponent:urlKey]; } activeTabKey = [self->selection stringValueInComponent:[_ctx component]]; [_ctx setObject:activeTabKey forKey:UIxTabView_BODY]; result = [self->template invokeActionForRequest:_req inContext:_ctx]; [_ctx removeObjectForKey:UIxTabView_BODY]; if (urlKey) [_ctx deleteLastElementIDComponent]; // active key [_ctx deleteLastElementIDComponent]; // 'b' } else { [[_ctx application] debugWithFormat:@"unknown tab container key '%@'", key]; } [self restoreNestedState:nestedState inContext:_ctx]; return result; } - (NSString *)_tabViewCountInContext:(WOContext *)_ctx { int count; count = [[_ctx valueForKey:@"UIxTabViewScriptDone"] intValue]; return [NSString stringWithFormat:@"%d",count]; } - (NSString *)scriptHref:(UIxTabItemInfo *)_info inContext:(WOContext *)_ctx isLeft:(BOOL)_isLeft keys:(NSArray *)_keys { NSMutableString *result = [NSMutableString string]; UIxTabItemInfo *tmp; NSString *activeKey; int i, cnt; NSString *elID; NSString *tstring; activeKey = [self->selection stringValueInComponent:[_ctx component]]; [result appendString:@"JavaScript:showTab("]; [result appendString:_info->key]; [result appendString:@"Tab);"]; [result appendString:@"swapCorners("]; tstring = (!_isLeft) ? @"tabCorner%@,tabCornerLeft%@);" : @"tabCornerLeft%@,tabCorner%@);"; elID = [self _tabViewCountInContext:_ctx]; [result appendString:[NSString stringWithFormat:tstring,elID,elID]]; for (i=0, cnt = [_keys count]; i < cnt; i++) { tmp = [_keys objectAtIndex:i]; if ((tmp->isScript || [tmp->key isEqualToString:activeKey]) && ![tmp->key isEqualToString:_info->key]) { [result appendString:@"hideTab("]; [result appendString:tmp->key]; [result appendString:@"Tab);"]; } } return result; } - (void)appendLink:(UIxTabItemInfo *)_info toResponse:(WOResponse *)_response inContext:(WOContext *)_ctx isActive:(BOOL)_isActive isLeft:(BOOL)_isLeft doScript:(BOOL)_doScript keys:(NSArray *)_keys { NSString *headUri = nil; NSString *label = nil; NSString *scriptHref = nil; NSString *styleName = nil; WEClientCapabilities *ccaps; WOComponent *comp; ccaps = [[_ctx request] clientCapabilities]; comp = [_ctx component]; headUri = _info->uri; if ((label = _info->label) == nil) label = _info->key; if (_isActive) { styleName = (_info->selectedTabStyle) ? _info->selectedTabStyle : [self->selectedTabStyle stringValueInComponent:comp]; } else { styleName = (_info->tabStyle) ? _info->tabStyle : [self->tabStyle stringValueInComponent:comp]; } [_response appendContentString:@"']; [_response appendContentString:@"isScript || _isActive)) { scriptHref = [self scriptHref:_info inContext:_ctx isLeft:_isLeft keys:_keys]; [_response appendContentHTMLAttributeValue:scriptHref]; } else { [_response appendContentHTMLAttributeValue:headUri]; } [_response appendContentString:@"\" "]; [_response appendContentString: [NSString stringWithFormat:@"name='%@TabLink'", _info->key]]; [_response appendContentString:@">"]; if ([label length] < 1) label = _info->key; [_response appendContentString:@""]; [_response appendContentHTMLString:label]; [_response appendContentString:@""]; [_response appendContentString:@""]; [_response appendContentString:@""]; #if 0 if (_doScript && (_info->isScript || _isActive)) { NSString *k; // key NSString *s; // selected tab icon NSString *u; // unselected tab icon //NSString *out; k = _info->key; s = _info->selIcon; // selectedTabIcon u = (_isLeft) ? _info->leftIcon : _info->tabIcon; s = WEUriOfResource(s, _ctx); u = WEUriOfResource(u, _ctx); s = ([s length] < 1) ? imgUri : s; u = ([u length] < 1) ? imgUri : u; #if 0 out = [NSString alloc]; out = [out initWithFormat: @"", k, k, k, k, scriptHref, k, headUri, k, k, k, k, k, u, k, k, s ]; [_response appendContentString:out]; RELEASE(out); #else # define _appendStr_(_str_) [_response appendContentString:_str_] _appendStr_(@""); #undef _appendStr_ #endif } #endif } - (void)appendSubmitButton:(UIxTabItemInfo *)_info toResponse:(WOResponse *)_response inContext:(WOContext *)_ctx isActive:(BOOL)_isActive isLeft:(BOOL)_left doScript:(BOOL)_doScript keys:(NSArray *)_keys { [self appendLink:_info toResponse:_response inContext:_ctx isActive:_isActive isLeft:_left doScript:_doScript keys:_keys]; } - (void)_appendTabViewJSScriptToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx { [_response appendContentString: @""]; } - (void)_appendHeaderRowToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx keys:(NSArray *)keys activeKey:(NSString *)activeKey doScript:(BOOL)doScript { unsigned i, count; BOOL doForm; NSString *styleName; doForm = NO; /* generate form controls ? */ [_response appendContentString:@""]; styleName = [self->headerStyle stringValueInComponent:[_ctx component]]; if(styleName) { [_response appendContentString: @""]; } else { [_response appendContentString: @"
"]; } for (i = 0, count = [keys count]; i < count; i++) { UIxTabItemInfo *info; NSString *key; BOOL isActive; info = [keys objectAtIndex:i]; key = info->key; isActive = [key isEqualToString:activeKey]; [_ctx appendElementIDComponent:key]; if (doForm) { /* tab is inside of a FORM, so produce submit buttons */ [self appendSubmitButton:info toResponse:_response inContext:_ctx isActive:isActive isLeft:(i == 0) ? YES : NO doScript:doScript keys:keys]; } else { /* tab is not in a FORM, generate hyperlinks for tab */ [self appendLink:info toResponse:_response inContext:_ctx isActive:isActive isLeft:(i == 0) ? YES : NO doScript:doScript keys:keys]; } [_ctx deleteLastElementIDComponent]; } // [_response appendContentString:@""]; [_response appendContentString:@"
"]; [_response appendContentString:@""]; } - (void)_appendHeaderFootRowToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx bgcolor:(NSString *)bgcolor doScript:(BOOL)doScript isLeftActive:(BOOL)isLeftActive { NSString *styleName; [_response appendContentString:@" bodyStyle stringValueInComponent:[_ctx component]]; if(styleName) { [_response appendContentString:@" class='"]; [_response appendContentHTMLAttributeValue:styleName]; [_response appendContentCharacter:'\'']; } if (bgcolor) { [_response appendContentString:@" bgcolor=\""]; [_response appendContentHTMLAttributeValue:bgcolor]; [_response appendContentString:@"\""]; } [_response appendContentString:@">\n"]; /* left corner */ [_response appendContentString:@" "]; if (doScript) { [_response appendContentString:@"
"]; [_response appendContentString:@" "]; [_response appendContentString:@"
"]; } else if (isLeftActive) [_response appendContentString:@" "]; if (doScript) { [_response appendContentString:@"
"]; } if (!isLeftActive || doScript) { NSString *uri; uri = [self->leftCornerIcon stringValueInComponent:[_ctx component]]; if ((uri = WEUriOfResource(uri, _ctx))) { [_response appendContentString:@"\"\""]; } else [_response appendContentString:@" "]; } if (doScript) [_response appendContentString:@"
"]; [_response appendContentString:@"\n"]; /* right corner */ [_response appendContentString:@" "]; { NSString *uri; uri = [self->rightCornerIcon stringValueInComponent:[_ctx component]]; if ((uri = WEUriOfResource(uri, _ctx))) { [_response appendContentString:@"\"\""]; } else [_response appendContentString:@" "]; } [_response appendContentString:@"\n"]; [_response appendContentString:@" \n"]; } - (void)_appendBodyRowToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx bgcolor:(NSString *)bgcolor activeKey:(NSString *)activeKey { WEClientCapabilities *ccaps; BOOL indentContent; NSString *styleName; styleName = [self->bodyStyle stringValueInComponent:[_ctx component]]; ccaps = [[_ctx request] clientCapabilities]; /* put additional padding table into content ??? */ indentContent = [ccaps isFastTableBrowser] && ![ccaps isTextModeBrowser]; [_response appendContentString:@"']; if (indentContent) { /* start padding table */ [_response appendContentString: @""]; [_response appendContentString:@"
"]; } [_ctx appendElementIDComponent:@"b"]; [_ctx appendElementIDComponent:activeKey]; /* generate currently active body */ { [_ctx setObject:activeKey forKey:UIxTabView_BODY]; [self->template appendToResponse:_response inContext:_ctx]; [_ctx removeObjectForKey:UIxTabView_BODY]; } [_ctx deleteLastElementIDComponent]; // activeKey [_ctx deleteLastElementIDComponent]; // 'b' if (indentContent) /* close padding table */ [_response appendContentString:@"
"]; [_response appendContentString:@""]; } - (BOOL)isLeftActiveInKeys:(NSArray *)keys activeKey:(NSString *)activeKey{ unsigned i, count; BOOL isLeftActive; isLeftActive = NO; for (i = 0, count = [keys count]; i < count; i++) { UIxTabItemInfo *info; info = [keys objectAtIndex:i]; if ((i == 0) && [info->key isEqualToString:activeKey]) isLeftActive = YES; } return isLeftActive; } - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx { WOComponent *cmp; NSString *bgcolor; BOOL isLeftActive; BOOL doScript; id nestedState; NSString *activeKey; NSArray *keys; int tabViewCount; /* used for image id's and writing script once */ doScript = NO; /* perform tab-clicks on browser (use javascript) */ tabViewCount = [[_ctx valueForKey:@"UIxTabViewScriptDone"] intValue]; cmp = [_ctx component]; /* check for browser */ { WEClientCapabilities *ccaps; //BOOL isJavaScriptBrowser; ccaps = [[_ctx request] clientCapabilities]; doScript = [ccaps isInternetExplorer] && [ccaps isJavaScriptBrowser]; doScript = (doScript && [[[cmp session] valueForKey:@"isJavaScriptEnabled"] boolValue]); } /* disable javascript */ doScript = NO; /* save state */ nestedState = [self saveNestedStateInContext:_ctx]; /* configure */ activeKey = [self->selection stringValueInComponent:cmp]; bgcolor = [self->bgColor stringValueInComponent:cmp]; bgcolor = [bgcolor stringValue]; [_ctx appendElementIDComponent:@"h"]; /* collect & process keys (= available tabs) */ keys = [self collectKeysInContext:_ctx]; if (![[keys valueForKey:@"key"] containsObject:activeKey]) /* selection is not available in keys */ activeKey = nil; if ((activeKey == nil) && ([keys count] > 0)) { /* no or invalid selection, use first key */ activeKey = [[keys objectAtIndex:0] key]; if ([self->selection isValueSettable]) [self->selection setValue:activeKey inComponent:[_ctx component]]; } if (doScript) { doScript = [[keys valueForKey:@"isScript"] containsObject:YesNumber]; [_ctx setObject:[NSNumber numberWithBool:doScript] forKey:UIxTabView_SCRIPT]; } /* start appending */ if ((doScript) && (tabViewCount == 0)) [self _appendTabViewJSScriptToResponse:_response inContext:_ctx]; /* count up for unique tabCorner/tabCornerLeft images */ [_ctx takeValue:[NSNumber numberWithInt:(tabViewCount + 1)] forKey:@"UIxTabViewScriptDone"]; [_response appendContentString: @""]; /* find out whether left is active */ isLeftActive = [self isLeftActiveInKeys:keys activeKey:activeKey]; /* generate header row */ [self _appendHeaderRowToResponse:_response inContext:_ctx keys:keys activeKey:activeKey doScript:doScript]; [_ctx deleteLastElementIDComponent]; // 'h' for head [_ctx removeObjectForKey:UIxTabView_HEAD]; #if 0 /* header foot row */ [self _appendHeaderFootRowToResponse:_response inContext:_ctx bgcolor:bgcolor doScript:doScript isLeftActive:isLeftActive]; #endif /* body row */ [self _appendBodyRowToResponse:_response inContext:_ctx bgcolor:bgcolor activeKey:activeKey]; /* close table */ [_response appendContentString:@"
"]; [_ctx removeObjectForKey:UIxTabView_ACTIVEKEY]; [_ctx removeObjectForKey:UIxTabView_KEYS]; [self restoreNestedState:nestedState inContext:_ctx]; } @end /* UIxTabView */