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
22 #include "UIxTabView.h"
24 #include <NGObjWeb/NGObjWeb.h>
25 #include <NGExtensions/NGExtensions.h>
26 #include <EOControl/EOControl.h>
27 #include <NGObjWeb/WEClientCapabilities.h>
30 // # define DEBUG_TAKEVALUES 1
35 NSString *UIxTabView_HEAD = @"UIxTabView_head";
36 NSString *UIxTabView_BODY = @"UIxTabView_body";
37 NSString *UIxTabView_KEYS = @"UIxTabView_keys";
38 NSString *UIxTabView_SCRIPT = @"UIxTabView_script";
39 NSString *UIxTabView_ACTIVEKEY = @"UIxTabView_activekey";
40 NSString *UIxTabView_COLLECT = @"~tv~";
42 @implementation UIxTabView
44 static NSNumber *YesNumber;
48 YesNumber = [[NSNumber numberWithBool:YES] retain];
52 return [super version] + 0;
55 - (id)initWithName:(NSString *)_name
56 associations:(NSDictionary *)_config
57 template:(WOElement *)_subs
59 if ((self = [super initWithName:_name associations:_config template:_subs])) {
60 self->selection = WOExtGetProperty(_config, @"selection");
62 self->headerStyle = WOExtGetProperty(_config, @"headerStyle");
63 self->bodyStyle = WOExtGetProperty(_config, @"bodyStyle");
64 self->tabStyle = WOExtGetProperty(_config, @"tabStyle");
65 self->selectedTabStyle = WOExtGetProperty(_config, @"selectedTabStyle");
67 self->bgColor = WOExtGetProperty(_config, @"bgColor");
68 self->nonSelectedBgColor = WOExtGetProperty(_config, @"nonSelectedBgColor");
69 self->leftCornerIcon = WOExtGetProperty(_config, @"leftCornerIcon");
70 self->rightCornerIcon = WOExtGetProperty(_config, @"rightCornerIcon");
72 self->tabIcon = WOExtGetProperty(_config, @"tabIcon");
73 self->leftTabIcon = WOExtGetProperty(_config, @"leftTabIcon");
74 self->selectedTabIcon = WOExtGetProperty(_config, @"selectedTabIcon");
76 self->asBackground = WOExtGetProperty(_config, @"asBackground");
77 self->width = WOExtGetProperty(_config, @"width");
78 self->height = WOExtGetProperty(_config, @"height");
79 self->activeBgColor = WOExtGetProperty(_config, @"activeBgColor");
80 self->inactiveBgColor = WOExtGetProperty(_config, @"inactiveBgColor");
82 self->fontColor = WOExtGetProperty(_config, @"fontColor");
83 self->fontSize = WOExtGetProperty(_config, @"fontSize");
84 self->fontFace = WOExtGetProperty(_config, @"fontFace");
86 self->template = RETAIN(_subs);
92 [self->selection release];
94 [self->headerStyle release];
95 [self->bodyStyle release];
96 [self->tabStyle release];
97 [self->selectedTabStyle release];
99 RELEASE(self->bgColor);
100 RELEASE(self->nonSelectedBgColor);
101 RELEASE(self->leftCornerIcon);
102 RELEASE(self->rightCornerIcon);
104 RELEASE(self->leftTabIcon);
105 RELEASE(self->selectedTabIcon);
106 RELEASE(self->tabIcon);
108 RELEASE(self->width);
109 RELEASE(self->height);
111 RELEASE(self->activeBgColor);
112 RELEASE(self->inactiveBgColor);
114 RELEASE(self->fontColor);
115 RELEASE(self->fontSize);
116 RELEASE(self->fontFace);
118 RELEASE(self->template);
124 - (id)saveNestedStateInContext:(WOContext *)_ctx {
127 - (void)restoreNestedState:(id)_state inContext:(WOContext *)_ctx {
128 if (_state == nil) return;
131 - (NSArray *)collectKeysInContext:(WOContext *)_ctx {
132 /* collect mode, collects all keys */
133 [_ctx setObject:UIxTabView_COLLECT forKey:UIxTabView_HEAD];
135 [self->template appendToResponse:nil inContext:_ctx];
137 [_ctx removeObjectForKey:UIxTabView_HEAD];
138 return [_ctx objectForKey:UIxTabView_KEYS];
143 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
145 NSString *activeTabKey;
147 activeTabKey = [self->selection stringValueInComponent:[_ctx component]];
148 NSLog(@"%s activeTabKey:%@", __PRETTY_FUNCTION__, activeTabKey);
149 nestedState = [self saveNestedStateInContext:_ctx];
150 [_ctx appendElementIDComponent:@"b"];
151 [_ctx appendElementIDComponent:activeTabKey];
153 [_ctx setObject:activeTabKey forKey:UIxTabView_BODY];
156 [[_ctx component] debugWithFormat:@"UIxTabView: body takes values, eid='%@'",
160 [self->template takeValuesFromRequest:_req inContext:_ctx];
162 [_ctx removeObjectForKey:UIxTabView_BODY];
163 [_ctx deleteLastElementIDComponent]; // activeKey
164 [_ctx deleteLastElementIDComponent]; /* 'b' */
165 [self restoreNestedState:nestedState inContext:_ctx];
168 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
173 if ((key = [_ctx currentElementID]) == nil)
177 nestedState = [self saveNestedStateInContext:_ctx];
179 if ([key isEqualToString:@"h"]) {
183 [_ctx consumeElementID];
184 [_ctx appendElementIDComponent:@"h"];
186 if ((urlKey = [_ctx currentElementID]) == nil) {
188 debugWithFormat:@"missing active head tab key !"];
191 //NSLog(@"clicked: %@", urlKey);
192 [_ctx consumeElementID];
193 [_ctx appendElementIDComponent:urlKey];
197 [_ctx setObject:self->selection forKey:UIxTabView_HEAD];
198 result = [self->template invokeActionForRequest:_req inContext:_ctx];
199 [_ctx removeObjectForKey:UIxTabView_HEAD];
203 [_ctx deleteLastElementIDComponent]; // active key
205 [_ctx deleteLastElementIDComponent]; // 'h'
207 else if ([key isEqualToString:@"b"]) {
209 NSString *activeTabKey, *urlKey;
211 [_ctx consumeElementID];
212 [_ctx appendElementIDComponent:@"b"];
214 if ((urlKey = [_ctx currentElementID]) == nil) {
216 debugWithFormat:@"missing active body tab key !"];
219 //NSLog(@"clicked: %@", urlKey);
220 [_ctx consumeElementID];
221 [_ctx appendElementIDComponent:urlKey];
224 activeTabKey = [self->selection stringValueInComponent:[_ctx component]];
225 [_ctx setObject:activeTabKey forKey:UIxTabView_BODY];
227 result = [self->template invokeActionForRequest:_req inContext:_ctx];
229 [_ctx removeObjectForKey:UIxTabView_BODY];
232 [_ctx deleteLastElementIDComponent]; // active key
233 [_ctx deleteLastElementIDComponent]; // 'b'
237 debugWithFormat:@"unknown tab container key '%@'", key];
240 [self restoreNestedState:nestedState inContext:_ctx];
244 - (NSString *)_tabViewCountInContext:(WOContext *)_ctx {
246 count = [[_ctx valueForKey:@"UIxTabViewScriptDone"] intValue];
247 return [NSString stringWithFormat:@"%d",count];
250 - (NSString *)scriptHref:(UIxTabItemInfo *)_info
251 inContext:(WOContext *)_ctx
253 keys:(NSArray *)_keys
255 NSMutableString *result = [NSMutableString string];
262 activeKey = [self->selection stringValueInComponent:[_ctx component]];
263 [result appendString:@"JavaScript:showTab("];
264 [result appendString:_info->key];
265 [result appendString:@"Tab);"];
267 [result appendString:@"swapCorners("];
269 ? @"tabCorner%@,tabCornerLeft%@);"
270 : @"tabCornerLeft%@,tabCorner%@);";
271 elID = [self _tabViewCountInContext:_ctx];
272 [result appendString:[NSString stringWithFormat:tstring,elID,elID]];
274 for (i=0, cnt = [_keys count]; i < cnt; i++) {
275 tmp = [_keys objectAtIndex:i];
277 if ((tmp->isScript || [tmp->key isEqualToString:activeKey])
278 && ![tmp->key isEqualToString:_info->key]) {
279 [result appendString:@"hideTab("];
280 [result appendString:tmp->key];
281 [result appendString:@"Tab);"];
287 - (void)appendLink:(UIxTabItemInfo *)_info
288 toResponse:(WOResponse *)_response
289 inContext:(WOContext *)_ctx
290 isActive:(BOOL)_isActive isLeft:(BOOL)_isLeft
291 doScript:(BOOL)_doScript keys:(NSArray *)_keys
293 NSString *headUri = nil;
294 NSString *label = nil;
295 NSString *styleName = nil;
296 WEClientCapabilities *ccaps;
299 ccaps = [[_ctx request] clientCapabilities];
301 comp = [_ctx component];
302 headUri = _info->uri;
304 if ((label = _info->label) == nil)
308 styleName = (_info->selectedTabStyle)
309 ? _info->selectedTabStyle
310 : [self->selectedTabStyle stringValueInComponent:comp];
313 styleName = (_info->tabStyle)
315 : [self->tabStyle stringValueInComponent:comp];
318 [_response appendContentString:@"<td align='center' valign='middle'"];
321 [_response appendContentString:@" class='"];
322 [_response appendContentHTMLAttributeValue:styleName];
323 [_response appendContentCharacter:'\''];
326 // click on td background
327 if ([ccaps isInternetExplorer] && [ccaps isJavaScriptBrowser]) {
328 [_response appendContentString:@" onclick=\"window.location.href='"];
329 [_response appendContentHTMLAttributeValue:headUri];
330 [_response appendContentString:@"'\""];
333 [_response appendContentCharacter:'>'];
335 [_response appendContentString:@"<a href=\""];
337 [_response appendContentHTMLAttributeValue:headUri];
339 [_response appendContentString:@"\" "];
340 [_response appendContentString:
341 [NSString stringWithFormat:@"name='%@TabLink'", _info->key]];
342 [_response appendContentString:@">"];
344 if ([label length] < 1)
346 [_response appendContentString:@"<nobr>"];
347 [_response appendContentHTMLString:label];
348 [_response appendContentString:@"</nobr>"];
350 [_response appendContentString:@"</a>"];
352 [_response appendContentString:@"</td>"];
355 - (void)appendSubmitButton:(UIxTabItemInfo *)_info
356 toResponse:(WOResponse *)_response
357 inContext:(WOContext *)_ctx
358 isActive:(BOOL)_isActive isLeft:(BOOL)_left
359 doScript:(BOOL)_doScript keys:(NSArray *)_keys
361 [self appendLink:_info
364 isActive:_isActive isLeft:_left
365 doScript:NO keys:_keys];
368 - (void)_appendTabViewJSScriptToResponse:(WOResponse *)_response
369 inContext:(WOContext *)_ctx
371 [_response appendContentString:
372 @"<script language=\"JavaScript\">\n<!--\n\n"
373 @"function showTab(obj) {\n"
375 @" if (obj==null) { alert('missing tab obj ..'); return; }\n"
376 @" if (obj['Div']==null) {"
377 @" alert('missing div key in ' + obj); return; }\n"
378 @" if (obj['Div'].style==null) {"
379 @" alert('missing style key in div ' + obj['Div']);return; }\n"
381 @" obj['Div'].style.display = \"\";\n"
382 @" obj['Img'].src = obj[\"Ar\"][1].src;\n"
383 @" obj['link'].href = obj[\"href2\"];\n"
385 @"function hideTab(obj) {\n"
387 @" if (obj==null) { alert('missing tab obj ..'); return; }\n"
388 @" if (obj['Div']==null) {"
389 @" alert('missing div key in ' + obj); return; }\n"
390 @" if (obj['Div'].style==null) {"
391 @" alert('missing style key in div ' + obj['Div']);return; }\n"
393 @" obj['Div'].style.display = \"none\";\n"
394 @" obj['Img'].src = obj[\"Ar\"][0].src;\n"
395 @" obj['link'].href = obj[\"href1\"];\n"
397 @"function swapCorners(obj1,obj2) {\n"
398 @" if (obj1==null) { alert('missing corner 1'); return; }\n"
399 @" if (obj2==null) { alert('missing corner 2'); return; }\n"
400 @" obj1.style.display = \"none\";\n"
401 @" obj2.style.display = \"\";\n"
403 @"//-->\n</script>"];
406 - (void)_appendHeaderRowToResponse:(WOResponse *)_response
407 inContext:(WOContext *)_ctx
408 keys:(NSArray *)keys activeKey:(NSString *)activeKey
409 doScript:(BOOL)doScript
415 doForm = NO; /* generate form controls ? */
417 [_response appendContentString:@"<tr><td colspan='2'>"];
419 styleName = [self->headerStyle stringValueInComponent:[_ctx component]];
421 [_response appendContentString:
422 @"<table border='0' cellpadding='0' cellspacing='0' class='"];
423 [_response appendContentHTMLAttributeValue:styleName];
424 [_response appendContentString:@"'><tr>"];
427 [_response appendContentString:
428 @"<table border='0' cellpadding='0' cellspacing='0'><tr>"];
431 for (i = 0, count = [keys count]; i < count; i++) {
432 UIxTabItemInfo *info;
436 info = [keys objectAtIndex:i];
438 isActive = [key isEqualToString:activeKey];
440 [_ctx appendElementIDComponent:key];
443 /* tab is inside of a FORM, so produce submit buttons */
444 [self appendSubmitButton:info
448 isLeft:(i == 0) ? YES : NO
453 /* tab is not in a FORM, generate hyperlinks for tab */
454 [self appendLink:info
458 isLeft:(i == 0) ? YES : NO
463 [_ctx deleteLastElementIDComponent];
465 // [_response appendContentString:@"<td></td>"];
466 [_response appendContentString:@"</tr></table>"];
467 [_response appendContentString:@"</td></tr>"];
470 - (void)_appendHeaderFootRowToResponse:(WOResponse *)_response
471 inContext:(WOContext *)_ctx
472 bgcolor:(NSString *)bgcolor
473 doScript:(BOOL)doScript
474 isLeftActive:(BOOL)isLeftActive
477 [_response appendContentString:@" <tr"];
479 styleName = [self->bodyStyle stringValueInComponent:[_ctx component]];
481 [_response appendContentString:@" class='"];
482 [_response appendContentHTMLAttributeValue:styleName];
483 [_response appendContentCharacter:'\''];
486 [_response appendContentString:@" bgcolor=\""];
487 [_response appendContentHTMLAttributeValue:bgcolor];
488 [_response appendContentString:@"\""];
490 [_response appendContentString:@">\n"];
493 [_response appendContentString:@" <td align=\"left\" width=\"10\">"];
496 [_response appendContentString:@" "];
501 uri = [self->leftCornerIcon stringValueInComponent:[_ctx component]];
502 if ((uri = WEUriOfResource(uri, _ctx))) {
503 [_response appendContentString:@"<img border=\"0\" alt=\"\" src=\""];
504 [_response appendContentString:uri];
505 [_response appendContentString:@"\" />"];
508 [_response appendContentString:@" "];
511 [_response appendContentString:@"</td>"];
514 [_response appendContentString:@" <td align=\"right\">"];
518 uri = [self->rightCornerIcon stringValueInComponent:[_ctx component]];
519 if ((uri = WEUriOfResource(uri, _ctx))) {
520 [_response appendContentString:@"<img border=\"0\" alt=\"\" src=\""];
521 [_response appendContentString:uri];
522 [_response appendContentString:@"\" />"];
525 [_response appendContentString:@" "];
527 [_response appendContentString:@"</td>\n"];
529 [_response appendContentString:@" </tr>\n"];
532 - (void)_appendBodyRowToResponse:(WOResponse *)_response
533 inContext:(WOContext *)_ctx
534 bgcolor:(NSString *)bgcolor
535 activeKey:(NSString *)activeKey
537 WEClientCapabilities *ccaps;
541 styleName = [self->bodyStyle stringValueInComponent:[_ctx component]];
542 ccaps = [[_ctx request] clientCapabilities];
544 /* put additional padding table into content ??? */
545 indentContent = [ccaps isFastTableBrowser] && ![ccaps isTextModeBrowser];
547 [_response appendContentString:@"<tr"];
549 [_response appendContentString:@" class='"];
550 [_response appendContentHTMLAttributeValue:styleName];
551 [_response appendContentCharacter:'\''];
553 [_response appendContentString:@"><td colspan='2'"];
555 [_response appendContentString:@" bgcolor=\""];
556 [_response appendContentHTMLAttributeValue:bgcolor];
557 [_response appendContentCharacter:'\"'];
559 [_response appendContentCharacter:'>'];
562 /* start padding table */
563 [_response appendContentString:
564 @"<table border='0' width='100%'"
565 @" cellpadding='10' cellspacing='0'>"];
566 [_response appendContentString:@"<tr><td>"];
569 [_ctx appendElementIDComponent:@"b"];
570 [_ctx appendElementIDComponent:activeKey];
572 /* generate currently active body */
574 [_ctx setObject:activeKey forKey:UIxTabView_BODY];
575 [self->template appendToResponse:_response inContext:_ctx];
576 [_ctx removeObjectForKey:UIxTabView_BODY];
579 [_ctx deleteLastElementIDComponent]; // activeKey
580 [_ctx deleteLastElementIDComponent]; // 'b'
583 /* close padding table */
584 [_response appendContentString:@"</td></tr></table>"];
586 [_response appendContentString:@"</td></tr>"];
589 - (BOOL)isLeftActiveInKeys:(NSArray *)keys activeKey:(NSString *)activeKey{
595 for (i = 0, count = [keys count]; i < count; i++) {
596 UIxTabItemInfo *info;
598 info = [keys objectAtIndex:i];
600 if ((i == 0) && [info->key isEqualToString:activeKey])
607 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
614 int tabViewCount; /* used for image id's and writing script once */
616 tabViewCount = [[_ctx valueForKey:@"UIxTabViewScriptDone"] intValue];
617 cmp = [_ctx component];
621 nestedState = [self saveNestedStateInContext:_ctx];
625 activeKey = [self->selection stringValueInComponent:cmp];
627 bgcolor = [self->bgColor stringValueInComponent:cmp];
628 bgcolor = [bgcolor stringValue];
630 [_ctx appendElementIDComponent:@"h"];
632 /* collect & process keys (= available tabs) */
634 keys = [self collectKeysInContext:_ctx];
636 if (![[keys valueForKey:@"key"] containsObject:activeKey])
637 /* selection is not available in keys */
640 if ((activeKey == nil) && ([keys count] > 0)) {
641 /* no or invalid selection, use first key */
642 activeKey = [[keys objectAtIndex:0] key];
643 if ([self->selection isValueSettable])
644 [self->selection setValue:activeKey inComponent:[_ctx component]];
647 /* start appending */
649 /* count up for unique tabCorner/tabCornerLeft images */
650 [_ctx takeValue:[NSNumber numberWithInt:(tabViewCount + 1)]
651 forKey:@"UIxTabViewScriptDone"];
653 [_response appendContentString:
654 @"<table border='0' width='100%'"
655 @" cellpadding='0' cellspacing='0'>"];
657 /* find out whether left is active */
659 isLeftActive = [self isLeftActiveInKeys:keys activeKey:activeKey];
661 /* generate header row */
663 [self _appendHeaderRowToResponse:_response inContext:_ctx
664 keys:keys activeKey:activeKey
667 [_ctx deleteLastElementIDComponent]; // 'h' for head
668 [_ctx removeObjectForKey:UIxTabView_HEAD];
672 [self _appendBodyRowToResponse:_response inContext:_ctx
674 activeKey:activeKey];
678 [_response appendContentString:@"</table>"];
679 [_ctx removeObjectForKey:UIxTabView_ACTIVEKEY];
680 [_ctx removeObjectForKey:UIxTabView_KEYS];
681 [self restoreNestedState:nestedState inContext:_ctx];
684 @end /* UIxTabView */