2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "UIxTabView.h"
25 #import <NGObjWeb/NGObjWeb.h>
26 #import <NGExtensions/NGExtensions.h>
27 #import <EOControl/EOControl.h>
28 #include <WEExtensions/WEClientCapabilities.h>
31 // # define DEBUG_TAKEVALUES 1
36 NSString *UIxTabView_HEAD = @"UIxTabView_head";
37 NSString *UIxTabView_BODY = @"UIxTabView_body";
38 NSString *UIxTabView_KEYS = @"UIxTabView_keys";
39 NSString *UIxTabView_SCRIPT = @"UIxTabView_script";
40 NSString *UIxTabView_ACTIVEKEY = @"UIxTabView_activekey";
41 NSString *UIxTabView_COLLECT = @"~tv~";
43 @implementation UIxTabView
45 static NSNumber *YesNumber;
49 YesNumber = [[NSNumber numberWithBool:YES] retain];
53 return [super version] + 0;
56 - (id)initWithName:(NSString *)_name
57 associations:(NSDictionary *)_config
58 template:(WOElement *)_subs
60 if ((self = [super initWithName:_name associations:_config template:_subs])) {
61 self->selection = WOExtGetProperty(_config, @"selection");
63 self->headerStyle = WOExtGetProperty(_config, @"headerStyle");
64 self->bodyStyle = WOExtGetProperty(_config, @"bodyStyle");
65 self->tabStyle = WOExtGetProperty(_config, @"tabStyle");
66 self->selectedTabStyle = WOExtGetProperty(_config, @"selectedTabStyle");
68 self->bgColor = WOExtGetProperty(_config, @"bgColor");
69 self->nonSelectedBgColor = WOExtGetProperty(_config, @"nonSelectedBgColor");
70 self->leftCornerIcon = WOExtGetProperty(_config, @"leftCornerIcon");
71 self->rightCornerIcon = WOExtGetProperty(_config, @"rightCornerIcon");
73 self->tabIcon = WOExtGetProperty(_config, @"tabIcon");
74 self->leftTabIcon = WOExtGetProperty(_config, @"leftTabIcon");
75 self->selectedTabIcon = WOExtGetProperty(_config, @"selectedTabIcon");
77 self->asBackground = WOExtGetProperty(_config, @"asBackground");
78 self->width = WOExtGetProperty(_config, @"width");
79 self->height = WOExtGetProperty(_config, @"height");
80 self->activeBgColor = WOExtGetProperty(_config, @"activeBgColor");
81 self->inactiveBgColor = WOExtGetProperty(_config, @"inactiveBgColor");
83 self->fontColor = WOExtGetProperty(_config, @"fontColor");
84 self->fontSize = WOExtGetProperty(_config, @"fontSize");
85 self->fontFace = WOExtGetProperty(_config, @"fontFace");
87 self->template = RETAIN(_subs);
93 [self->selection release];
95 [self->headerStyle release];
96 [self->bodyStyle release];
97 [self->tabStyle release];
98 [self->selectedTabStyle release];
100 RELEASE(self->bgColor);
101 RELEASE(self->nonSelectedBgColor);
102 RELEASE(self->leftCornerIcon);
103 RELEASE(self->rightCornerIcon);
105 RELEASE(self->leftTabIcon);
106 RELEASE(self->selectedTabIcon);
107 RELEASE(self->tabIcon);
109 RELEASE(self->width);
110 RELEASE(self->height);
112 RELEASE(self->activeBgColor);
113 RELEASE(self->inactiveBgColor);
115 RELEASE(self->fontColor);
116 RELEASE(self->fontSize);
117 RELEASE(self->fontFace);
119 RELEASE(self->template);
125 - (id)saveNestedStateInContext:(WOContext *)_ctx {
128 - (void)restoreNestedState:(id)_state inContext:(WOContext *)_ctx {
129 if (_state == nil) return;
132 - (NSArray *)collectKeysInContext:(WOContext *)_ctx {
133 /* collect mode, collects all keys */
134 [_ctx setObject:UIxTabView_COLLECT forKey:UIxTabView_HEAD];
136 [self->template appendToResponse:nil inContext:_ctx];
138 [_ctx removeObjectForKey:UIxTabView_HEAD];
139 return [_ctx objectForKey:UIxTabView_KEYS];
144 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
146 NSString *activeTabKey;
148 activeTabKey = [self->selection stringValueInComponent:[_ctx component]];
149 NSLog(@"%s activeTabKey:%@", __PRETTY_FUNCTION__, activeTabKey);
150 nestedState = [self saveNestedStateInContext:_ctx];
151 [_ctx appendElementIDComponent:@"b"];
152 [_ctx appendElementIDComponent:activeTabKey];
154 [_ctx setObject:activeTabKey forKey:UIxTabView_BODY];
157 [[_ctx component] debugWithFormat:@"UIxTabView: body takes values, eid='%@'",
161 [self->template takeValuesFromRequest:_req inContext:_ctx];
163 [_ctx removeObjectForKey:UIxTabView_BODY];
164 [_ctx deleteLastElementIDComponent]; // activeKey
165 [_ctx deleteLastElementIDComponent]; /* 'b' */
166 [self restoreNestedState:nestedState inContext:_ctx];
169 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
174 if ((key = [_ctx currentElementID]) == nil)
178 nestedState = [self saveNestedStateInContext:_ctx];
180 if ([key isEqualToString:@"h"]) {
184 [_ctx consumeElementID];
185 [_ctx appendElementIDComponent:@"h"];
187 if ((urlKey = [_ctx currentElementID]) == nil) {
189 debugWithFormat:@"missing active head tab key !"];
192 //NSLog(@"clicked: %@", urlKey);
193 [_ctx consumeElementID];
194 [_ctx appendElementIDComponent:urlKey];
198 [_ctx setObject:self->selection forKey:UIxTabView_HEAD];
199 result = [self->template invokeActionForRequest:_req inContext:_ctx];
200 [_ctx removeObjectForKey:UIxTabView_HEAD];
204 [_ctx deleteLastElementIDComponent]; // active key
206 [_ctx deleteLastElementIDComponent]; // 'h'
208 else if ([key isEqualToString:@"b"]) {
210 NSString *activeTabKey, *urlKey;
212 [_ctx consumeElementID];
213 [_ctx appendElementIDComponent:@"b"];
215 if ((urlKey = [_ctx currentElementID]) == nil) {
217 debugWithFormat:@"missing active body tab key !"];
220 //NSLog(@"clicked: %@", urlKey);
221 [_ctx consumeElementID];
222 [_ctx appendElementIDComponent:urlKey];
225 activeTabKey = [self->selection stringValueInComponent:[_ctx component]];
226 [_ctx setObject:activeTabKey forKey:UIxTabView_BODY];
228 result = [self->template invokeActionForRequest:_req inContext:_ctx];
230 [_ctx removeObjectForKey:UIxTabView_BODY];
233 [_ctx deleteLastElementIDComponent]; // active key
234 [_ctx deleteLastElementIDComponent]; // 'b'
238 debugWithFormat:@"unknown tab container key '%@'", key];
241 [self restoreNestedState:nestedState inContext:_ctx];
245 - (NSString *)_tabViewCountInContext:(WOContext *)_ctx {
247 count = [[_ctx valueForKey:@"UIxTabViewScriptDone"] intValue];
248 return [NSString stringWithFormat:@"%d",count];
251 - (NSString *)scriptHref:(UIxTabItemInfo *)_info
252 inContext:(WOContext *)_ctx
254 keys:(NSArray *)_keys
256 NSMutableString *result = [NSMutableString string];
263 activeKey = [self->selection stringValueInComponent:[_ctx component]];
264 [result appendString:@"JavaScript:showTab("];
265 [result appendString:_info->key];
266 [result appendString:@"Tab);"];
268 [result appendString:@"swapCorners("];
270 ? @"tabCorner%@,tabCornerLeft%@);"
271 : @"tabCornerLeft%@,tabCorner%@);";
272 elID = [self _tabViewCountInContext:_ctx];
273 [result appendString:[NSString stringWithFormat:tstring,elID,elID]];
275 for (i=0, cnt = [_keys count]; i < cnt; i++) {
276 tmp = [_keys objectAtIndex:i];
278 if ((tmp->isScript || [tmp->key isEqualToString:activeKey])
279 && ![tmp->key isEqualToString:_info->key]) {
280 [result appendString:@"hideTab("];
281 [result appendString:tmp->key];
282 [result appendString:@"Tab);"];
288 - (void)appendLink:(UIxTabItemInfo *)_info
289 toResponse:(WOResponse *)_response
290 inContext:(WOContext *)_ctx
291 isActive:(BOOL)_isActive isLeft:(BOOL)_isLeft
292 doScript:(BOOL)_doScript keys:(NSArray *)_keys
294 NSString *headUri = nil;
295 NSString *label = nil;
296 NSString *styleName = nil;
297 WEClientCapabilities *ccaps;
300 ccaps = [[_ctx request] clientCapabilities];
302 comp = [_ctx component];
303 headUri = _info->uri;
305 if ((label = _info->label) == nil)
309 styleName = (_info->selectedTabStyle)
310 ? _info->selectedTabStyle
311 : [self->selectedTabStyle stringValueInComponent:comp];
314 styleName = (_info->tabStyle)
316 : [self->tabStyle stringValueInComponent:comp];
319 [_response appendContentString:@"<td align='center' valign='middle'"];
322 [_response appendContentString:@" class='"];
323 [_response appendContentHTMLAttributeValue:styleName];
324 [_response appendContentCharacter:'\''];
327 // click on td background
328 if ([ccaps isInternetExplorer] && [ccaps isJavaScriptBrowser]) {
329 [_response appendContentString:@" onclick=\"window.location.href='"];
330 [_response appendContentHTMLAttributeValue:headUri];
331 [_response appendContentString:@"'\""];
334 [_response appendContentCharacter:'>'];
336 [_response appendContentString:@"<a href=\""];
338 [_response appendContentHTMLAttributeValue:headUri];
340 [_response appendContentString:@"\" "];
341 [_response appendContentString:
342 [NSString stringWithFormat:@"name='%@TabLink'", _info->key]];
343 [_response appendContentString:@">"];
345 if ([label length] < 1)
347 [_response appendContentString:@"<nobr>"];
348 [_response appendContentHTMLString:label];
349 [_response appendContentString:@"</nobr>"];
351 [_response appendContentString:@"</a>"];
353 [_response appendContentString:@"</td>"];
356 - (void)appendSubmitButton:(UIxTabItemInfo *)_info
357 toResponse:(WOResponse *)_response
358 inContext:(WOContext *)_ctx
359 isActive:(BOOL)_isActive isLeft:(BOOL)_left
360 doScript:(BOOL)_doScript keys:(NSArray *)_keys
362 [self appendLink:_info
365 isActive:_isActive isLeft:_left
366 doScript:NO keys:_keys];
369 - (void)_appendTabViewJSScriptToResponse:(WOResponse *)_response
370 inContext:(WOContext *)_ctx
372 [_response appendContentString:
373 @"<script language=\"JavaScript\">\n<!--\n\n"
374 @"function showTab(obj) {\n"
376 @" if (obj==null) { alert('missing tab obj ..'); return; }\n"
377 @" if (obj['Div']==null) {"
378 @" alert('missing div key in ' + obj); return; }\n"
379 @" if (obj['Div'].style==null) {"
380 @" alert('missing style key in div ' + obj['Div']);return; }\n"
382 @" obj['Div'].style.display = \"\";\n"
383 @" obj['Img'].src = obj[\"Ar\"][1].src;\n"
384 @" obj['link'].href = obj[\"href2\"];\n"
386 @"function hideTab(obj) {\n"
388 @" if (obj==null) { alert('missing tab obj ..'); return; }\n"
389 @" if (obj['Div']==null) {"
390 @" alert('missing div key in ' + obj); return; }\n"
391 @" if (obj['Div'].style==null) {"
392 @" alert('missing style key in div ' + obj['Div']);return; }\n"
394 @" obj['Div'].style.display = \"none\";\n"
395 @" obj['Img'].src = obj[\"Ar\"][0].src;\n"
396 @" obj['link'].href = obj[\"href1\"];\n"
398 @"function swapCorners(obj1,obj2) {\n"
399 @" if (obj1==null) { alert('missing corner 1'); return; }\n"
400 @" if (obj2==null) { alert('missing corner 2'); return; }\n"
401 @" obj1.style.display = \"none\";\n"
402 @" obj2.style.display = \"\";\n"
404 @"//-->\n</script>"];
407 - (void)_appendHeaderRowToResponse:(WOResponse *)_response
408 inContext:(WOContext *)_ctx
409 keys:(NSArray *)keys activeKey:(NSString *)activeKey
410 doScript:(BOOL)doScript
416 doForm = NO; /* generate form controls ? */
418 [_response appendContentString:@"<tr><td colspan='2'>"];
420 styleName = [self->headerStyle stringValueInComponent:[_ctx component]];
422 [_response appendContentString:
423 @"<table border='0' cellpadding='0' cellspacing='0' class='"];
424 [_response appendContentHTMLAttributeValue:styleName];
425 [_response appendContentString:@"'><tr>"];
428 [_response appendContentString:
429 @"<table border='0' cellpadding='0' cellspacing='0'><tr>"];
432 for (i = 0, count = [keys count]; i < count; i++) {
433 UIxTabItemInfo *info;
437 info = [keys objectAtIndex:i];
439 isActive = [key isEqualToString:activeKey];
441 [_ctx appendElementIDComponent:key];
444 /* tab is inside of a FORM, so produce submit buttons */
445 [self appendSubmitButton:info
449 isLeft:(i == 0) ? YES : NO
454 /* tab is not in a FORM, generate hyperlinks for tab */
455 [self appendLink:info
459 isLeft:(i == 0) ? YES : NO
464 [_ctx deleteLastElementIDComponent];
466 // [_response appendContentString:@"<td></td>"];
467 [_response appendContentString:@"</tr></table>"];
468 [_response appendContentString:@"</td></tr>"];
471 - (void)_appendHeaderFootRowToResponse:(WOResponse *)_response
472 inContext:(WOContext *)_ctx
473 bgcolor:(NSString *)bgcolor
474 doScript:(BOOL)doScript
475 isLeftActive:(BOOL)isLeftActive
478 [_response appendContentString:@" <tr"];
480 styleName = [self->bodyStyle stringValueInComponent:[_ctx component]];
482 [_response appendContentString:@" class='"];
483 [_response appendContentHTMLAttributeValue:styleName];
484 [_response appendContentCharacter:'\''];
487 [_response appendContentString:@" bgcolor=\""];
488 [_response appendContentHTMLAttributeValue:bgcolor];
489 [_response appendContentString:@"\""];
491 [_response appendContentString:@">\n"];
494 [_response appendContentString:@" <td align=\"left\" width=\"10\">"];
497 [_response appendContentString:@" "];
502 uri = [self->leftCornerIcon stringValueInComponent:[_ctx component]];
503 if ((uri = WEUriOfResource(uri, _ctx))) {
504 [_response appendContentString:@"<img border=\"0\" alt=\"\" src=\""];
505 [_response appendContentString:uri];
506 [_response appendContentString:@"\" />"];
509 [_response appendContentString:@" "];
512 [_response appendContentString:@"</td>"];
515 [_response appendContentString:@" <td align=\"right\">"];
519 uri = [self->rightCornerIcon stringValueInComponent:[_ctx component]];
520 if ((uri = WEUriOfResource(uri, _ctx))) {
521 [_response appendContentString:@"<img border=\"0\" alt=\"\" src=\""];
522 [_response appendContentString:uri];
523 [_response appendContentString:@"\" />"];
526 [_response appendContentString:@" "];
528 [_response appendContentString:@"</td>\n"];
530 [_response appendContentString:@" </tr>\n"];
533 - (void)_appendBodyRowToResponse:(WOResponse *)_response
534 inContext:(WOContext *)_ctx
535 bgcolor:(NSString *)bgcolor
536 activeKey:(NSString *)activeKey
538 WEClientCapabilities *ccaps;
542 styleName = [self->bodyStyle stringValueInComponent:[_ctx component]];
543 ccaps = [[_ctx request] clientCapabilities];
545 /* put additional padding table into content ??? */
546 indentContent = [ccaps isFastTableBrowser] && ![ccaps isTextModeBrowser];
548 [_response appendContentString:@"<tr"];
550 [_response appendContentString:@" class='"];
551 [_response appendContentHTMLAttributeValue:styleName];
552 [_response appendContentCharacter:'\''];
554 [_response appendContentString:@"><td colspan='2'"];
556 [_response appendContentString:@" bgcolor=\""];
557 [_response appendContentHTMLAttributeValue:bgcolor];
558 [_response appendContentCharacter:'\"'];
560 [_response appendContentCharacter:'>'];
563 /* start padding table */
564 [_response appendContentString:
565 @"<table border='0' width='100%'"
566 @" cellpadding='10' cellspacing='0'>"];
567 [_response appendContentString:@"<tr><td>"];
570 [_ctx appendElementIDComponent:@"b"];
571 [_ctx appendElementIDComponent:activeKey];
573 /* generate currently active body */
575 [_ctx setObject:activeKey forKey:UIxTabView_BODY];
576 [self->template appendToResponse:_response inContext:_ctx];
577 [_ctx removeObjectForKey:UIxTabView_BODY];
580 [_ctx deleteLastElementIDComponent]; // activeKey
581 [_ctx deleteLastElementIDComponent]; // 'b'
584 /* close padding table */
585 [_response appendContentString:@"</td></tr></table>"];
587 [_response appendContentString:@"</td></tr>"];
590 - (BOOL)isLeftActiveInKeys:(NSArray *)keys activeKey:(NSString *)activeKey{
596 for (i = 0, count = [keys count]; i < count; i++) {
597 UIxTabItemInfo *info;
599 info = [keys objectAtIndex:i];
601 if ((i == 0) && [info->key isEqualToString:activeKey])
608 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
615 int tabViewCount; /* used for image id's and writing script once */
617 tabViewCount = [[_ctx valueForKey:@"UIxTabViewScriptDone"] intValue];
618 cmp = [_ctx component];
622 nestedState = [self saveNestedStateInContext:_ctx];
626 activeKey = [self->selection stringValueInComponent:cmp];
628 bgcolor = [self->bgColor stringValueInComponent:cmp];
629 bgcolor = [bgcolor stringValue];
631 [_ctx appendElementIDComponent:@"h"];
633 /* collect & process keys (= available tabs) */
635 keys = [self collectKeysInContext:_ctx];
637 if (![[keys valueForKey:@"key"] containsObject:activeKey])
638 /* selection is not available in keys */
641 if ((activeKey == nil) && ([keys count] > 0)) {
642 /* no or invalid selection, use first key */
643 activeKey = [[keys objectAtIndex:0] key];
644 if ([self->selection isValueSettable])
645 [self->selection setValue:activeKey inComponent:[_ctx component]];
648 /* start appending */
650 /* count up for unique tabCorner/tabCornerLeft images */
651 [_ctx takeValue:[NSNumber numberWithInt:(tabViewCount + 1)]
652 forKey:@"UIxTabViewScriptDone"];
654 [_response appendContentString:
655 @"<table border='0' width='100%'"
656 @" cellpadding='0' cellspacing='0'>"];
658 /* find out whether left is active */
660 isLeftActive = [self isLeftActiveInKeys:keys activeKey:activeKey];
662 /* generate header row */
664 [self _appendHeaderRowToResponse:_response inContext:_ctx
665 keys:keys activeKey:activeKey
668 [_ctx deleteLastElementIDComponent]; // 'h' for head
669 [_ctx removeObjectForKey:UIxTabView_HEAD];
673 [self _appendBodyRowToResponse:_response inContext:_ctx
675 activeKey:activeKey];
679 [_response appendContentString:@"</table>"];
680 [_ctx removeObjectForKey:UIxTabView_ACTIVEKEY];
681 [_ctx removeObjectForKey:UIxTabView_KEYS];
682 [self restoreNestedState:nestedState inContext:_ctx];
685 @end /* UIxTabView */