/* Copyright (C) 2000-2005 SKYRIX Software AG This file is part of SOPE. 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. 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 SOPE; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "WETabView.h" #include #include "common.h" #if DEBUG // # define DEBUG_TAKEVALUES 1 # define DEBUG_JS 1 #endif /* context keys */ NSString *WETabView_HEAD = @"WETabView_head"; NSString *WETabView_BODY = @"WETabView_body"; NSString *WETabView_KEYS = @"WETabView_keys"; NSString *WETabView_SCRIPT = @"WETabView_script"; NSString *WETabView_ACTIVEKEY = @"WETabView_activekey"; NSString *WETabView_COLLECT = @"~tv~"; @implementation WETabView 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 *)_t { if ((self = [super initWithName:_name associations:_config template:_t])) { self->selection = WOExtGetProperty(_config, @"selection"); 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->disabledTabKeys = WOExtGetProperty(_config, @"disabledTabKeys"); self->template = [_t retain]; } return self; } - (void)dealloc { [self->disabledTabKeys release]; [self->selection 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 release]; [super dealloc]; } /* nesting */ - (id)saveNestedStateInContext:(WOContext *)_ctx { return nil; } - (void)restoreNestedState:(id)_state inContext:(WOContext *)_ctx { if (_state == nil) return; } - (NSArray *)filterKeys:(NSArray *)_keys inContext:(WOContext *)_ctx { NSMutableArray *ma; NSArray *rkeys; unsigned i, count; if (self->disabledTabKeys == nil || _keys == nil) return _keys; rkeys = [self->disabledTabKeys valueInComponent:[_ctx component]]; if (rkeys == nil || (count = [rkeys count]) == 0) return _keys; ma = [[_keys mutableCopy] autorelease]; for (i = 0; i < count; i++) { unsigned j, jc; // TODO: not particulary efficient, but should be OK ... */ for (j = 0, jc = [ma count]; j < jc; j++) { WETabItemInfo *info; info = [ma objectAtIndex:j]; if (![info->key isEqualToString:[rkeys objectAtIndex:i]]) continue; [ma removeObjectAtIndex:j]; } } return ma; } - (NSArray *)collectKeysInContext:(WOContext *)_ctx { NSArray *keys; /* collect mode, collects all keys */ [_ctx setObject:WETabView_COLLECT forKey:WETabView_HEAD]; [self->template appendToResponse:nil inContext:_ctx]; [_ctx removeObjectForKey:WETabView_HEAD]; keys = [_ctx objectForKey:WETabView_KEYS]; /* filter keys */ keys = [self filterKeys:keys inContext:_ctx]; return keys; } /* responder */ - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx { id nestedState; NSString *activeTabKey; activeTabKey = [self->selection stringValueInComponent:[_ctx component]]; nestedState = [self saveNestedStateInContext:_ctx]; [_ctx appendElementIDComponent:@"b"]; [_ctx appendElementIDComponent:activeTabKey]; [_ctx setObject:activeTabKey forKey:WETabView_BODY]; #if DEBUG_TAKEVALUES [[_ctx component] debugWithFormat:@"WETabView: body takes values, eid='%@'", [_ctx elementID]]; #endif [self->template takeValuesFromRequest:_req inContext:_ctx]; [_ctx removeObjectForKey:WETabView_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:WETabView_HEAD]; result = [self->template invokeActionForRequest:_req inContext:_ctx]; [_ctx removeObjectForKey:WETabView_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:WETabView_BODY]; result = [self->template invokeActionForRequest:_req inContext:_ctx]; [_ctx removeObjectForKey:WETabView_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:@"WETabViewScriptDone"] intValue]; return [NSString stringWithFormat:@"%d",count]; } - (NSString *)scriptHref:(WETabItemInfo *)_info inContext:(WOContext *)_ctx isLeft:(BOOL)_isLeft keys:(NSArray *)_keys { NSMutableString *result = [NSMutableString string]; WETabItemInfo *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:(WETabItemInfo *)_info toResponse:(WOResponse *)_response inContext:(WOContext *)_ctx isActive:(BOOL)_isActive isLeft:(BOOL)_isLeft doScript:(BOOL)_doScript keys:(NSArray *)_keys { BOOL doBgIcon; BOOL doImages; NSString *headUri = nil; NSString *imgUri = nil; NSString *label = nil; NSString *bgcolor = nil; NSString *w = nil; NSString *h = nil; NSString *scriptHref = nil; WOComponent *comp; doImages = ![[[_ctx request] clientCapabilities] isTextModeBrowser]; comp = [_ctx component]; headUri = _info->uri; if (_info->asBackground == 0) doBgIcon = [self->asBackground boolValueInComponent:comp]; else doBgIcon = (_info->asBackground == 1) ? YES : NO; if ((label = _info->label) == nil) label = _info->key; if (doImages) { /* lookup image */ NSString *imgName = nil; if (_isActive) { imgName = _info->selIcon; // selectedTabIcon if (imgName == nil) imgName = [self->selectedTabIcon stringValueInComponent:comp]; } else if (_isLeft) { imgName = _info->leftIcon; // leftTabIcon if (imgName == nil) imgName = [self->leftTabIcon stringValueInComponent:comp]; } else { imgName = _info->tabIcon; // tabIcon if (imgName == nil) imgName = [self->tabIcon stringValueInComponent:comp]; } if (imgName == nil) { imgName = _info->icon; } imgUri = WEUriOfResource(imgName, _ctx); if ([imgUri length] < 1) doImages = NO; } if (_isActive) { bgcolor = (_info->activeBg) ? _info->activeBg : [self->activeBgColor stringValueInComponent:comp]; } else { bgcolor = (_info->inactiveBg) ? _info->inactiveBg : [self->inactiveBgColor stringValueInComponent:comp]; } w = (_info->width) ? _info->width : [self->width stringValueInComponent:comp]; h = (_info->height) ? _info->height : [self->height stringValueInComponent:comp]; [_response appendContentString:@"']; if (!doImages) [_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 (!doImages && _isActive) [_response appendContentString:@""]; if (doImages && !doBgIcon) { [_response appendContentString:@""];
    [_response appendContentHTMLAttributeValue:label];
    [_response appendContentString:@"wcFlags.xmlStyleEmptyElements) [_response appendContentString:@" />"]; else [_response appendContentString:@">"]; } else { NSString *fc = [self->fontColor stringValueInComponent:comp]; NSString *fs = [self->fontSize stringValueInComponent:comp]; NSString *ff = [self->fontFace stringValueInComponent:comp]; BOOL hasFont; hasFont = (fc || fs || ff) ? YES : NO; if ([label length] < 1) label = _info->key; [_response appendContentString:@""]; if (hasFont) WEAppendFont(_response, fc, ff, fs); // if (_isActive) [_response appendContentString:@""]; [_response appendContentHTMLString:label]; if (_isActive) [_response appendContentString:@""]; if (hasFont) [_response appendContentString:@""]; // [_response appendContentString:@""]; } if (!doImages && _isActive) [_response appendContentString:@""]; [_response appendContentString:@""]; if (!doImages) [_response appendContentString:@"]"]; [_response appendContentString:@""]; if (_doScript && doImages && (_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 } } - (void)appendSubmitButton:(WETabItemInfo *)_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; doForm = NO; /* generate form controls ? */ [_response appendContentString:@""]; [_response appendContentString: @""]; for (i = 0, count = [keys count]; i < count; i++) { WETabItemInfo *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 { [_response appendContentString:@" "]; /* 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:@""]; /* right corner */ [_response appendContentString:@" "]; { NSString *uri; uri = [self->rightCornerIcon stringValueInComponent:[_ctx component]]; if ((uri = WEUriOfResource(uri, _ctx))) { [_response appendContentString:@"\"\"wcFlags.xmlStyleEmptyElements) [_response appendContentString:@"\" />"]; else [_response appendContentString:@"\">"]; } else [_response appendContentString:@" "]; } [_response appendContentString:@""]; [_response appendContentString:@" "]; } - (void)_appendBodyRowToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx bgcolor:(NSString *)bgcolor activeKey:(NSString *)activeKey { WEClientCapabilities *ccaps; BOOL indentContent; ccaps = [[_ctx request] clientCapabilities]; /* put additional padding table into content ??? */ indentContent = [ccaps isFastTableBrowser] && ![ccaps isTextModeBrowser]; [_response appendContentString:@""]; if (indentContent) { // TODO: we can now replace that with CSS padding? /* start padding table */ [_response appendContentString: @""]; [_response appendContentString:@"
"]; } [_ctx appendElementIDComponent:@"b"]; [_ctx appendElementIDComponent:activeKey]; /* generate currently active body */ { [_ctx setObject:activeKey forKey:WETabView_BODY]; [self->template appendToResponse:_response inContext:_ctx]; [_ctx removeObjectForKey:WETabView_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++) { WETabItemInfo *info; info = [keys objectAtIndex:i]; if ((i == 0) && [info->key isEqualToString:activeKey]) isLeftActive = YES; } return isLeftActive; } - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx { /* generates a table with three rows: - a row with the tabs - a row for round edges - a row for the content */ 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:@"WETabViewScriptDone"] intValue]; cmp = [_ctx component]; /* check for browser */ { WEClientCapabilities *ccaps; //BOOL isJavaScriptBrowser; ccaps = [[_ctx request] clientCapabilities]; doScript = [ccaps isInternetExplorer] && [ccaps isJavaScriptBrowser]; if ([_ctx hasSession]) { WOSession *sn; sn = [cmp session]; doScript = (doScript && [[sn 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:WETabView_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:@"WETabViewScriptDone"]; // TODO: add CSS class for table [_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:WETabView_HEAD]; /* header foot row */ [self _appendHeaderFootRowToResponse:_response inContext:_ctx bgcolor:bgcolor doScript:doScript isLeftActive:isLeftActive]; /* body row */ [self _appendBodyRowToResponse:_response inContext:_ctx bgcolor:bgcolor activeKey:activeKey]; /* close table */ [_response appendContentString:@"
"]; [_ctx removeObjectForKey:WETabView_ACTIVEKEY]; [_ctx removeObjectForKey:WETabView_KEYS]; [self restoreNestedState:nestedState inContext:_ctx]; } @end /* WETabView */