]> err.no Git - scalable-opengroupware.org/blob - SOGo/UI/Common/UIxTabView.m
git-svn-id: http://svn.opengroupware.org/SOGo/trunk@103 d1b88da0-ebda-0310-925b-ed51d...
[scalable-opengroupware.org] / SOGo / UI / Common / UIxTabView.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21 // $Id$
22
23 #include "UIxTabView.h"
24 #include "common.h"
25 #import <NGObjWeb/NGObjWeb.h>
26 #import <NGExtensions/NGExtensions.h>
27 #import <EOControl/EOControl.h>
28 #include <WEExtensions/WEClientCapabilities.h>
29
30 #if DEBUG
31 // #  define DEBUG_TAKEVALUES 1
32 #  define DEBUG_JS 1
33 #endif
34
35 /* context keys */
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~";
42
43 @implementation UIxTabView
44
45 static NSNumber *YesNumber;
46
47 + (void)initialize {
48   if (YesNumber == nil)
49     YesNumber = [[NSNumber numberWithBool:YES] retain];
50 }
51
52 + (int)version {
53   return [super version] + 0;
54 }
55
56 - (id)initWithName:(NSString *)_name
57   associations:(NSDictionary *)_config
58   template:(WOElement *)_subs
59 {
60   if ((self = [super initWithName:_name associations:_config template:_subs])) {
61     self->selection          = WOExtGetProperty(_config, @"selection");
62     
63     self->headerStyle        = WOExtGetProperty(_config, @"headerStyle");
64     self->bodyStyle          = WOExtGetProperty(_config, @"bodyStyle");
65     self->tabStyle           = WOExtGetProperty(_config, @"tabStyle");
66     self->selectedTabStyle   = WOExtGetProperty(_config, @"selectedTabStyle");
67
68     self->bgColor            = WOExtGetProperty(_config, @"bgColor");
69     self->nonSelectedBgColor = WOExtGetProperty(_config, @"nonSelectedBgColor");
70     self->leftCornerIcon     = WOExtGetProperty(_config, @"leftCornerIcon");
71     self->rightCornerIcon    = WOExtGetProperty(_config, @"rightCornerIcon");
72
73     self->tabIcon            = WOExtGetProperty(_config, @"tabIcon");
74     self->leftTabIcon        = WOExtGetProperty(_config, @"leftTabIcon");
75     self->selectedTabIcon    = WOExtGetProperty(_config, @"selectedTabIcon");
76
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");
82
83     self->fontColor          = WOExtGetProperty(_config, @"fontColor");
84     self->fontSize           = WOExtGetProperty(_config, @"fontSize");
85     self->fontFace           = WOExtGetProperty(_config, @"fontFace");
86
87     self->template = RETAIN(_subs);
88   }
89   return self;
90 }
91
92 - (void)dealloc {
93   [self->selection release];
94
95   [self->headerStyle release];
96   [self->bodyStyle release];
97   [self->tabStyle release];
98   [self->selectedTabStyle release];
99
100   RELEASE(self->bgColor);
101   RELEASE(self->nonSelectedBgColor);
102   RELEASE(self->leftCornerIcon);
103   RELEASE(self->rightCornerIcon);
104
105   RELEASE(self->leftTabIcon);
106   RELEASE(self->selectedTabIcon);
107   RELEASE(self->tabIcon);
108
109   RELEASE(self->width);
110   RELEASE(self->height);
111
112   RELEASE(self->activeBgColor);
113   RELEASE(self->inactiveBgColor);
114
115   RELEASE(self->fontColor);
116   RELEASE(self->fontSize);
117   RELEASE(self->fontFace);
118   
119   RELEASE(self->template);
120   [super dealloc];
121 }
122
123 /* nesting */
124
125 - (id)saveNestedStateInContext:(WOContext *)_ctx {
126   return nil;
127 }
128 - (void)restoreNestedState:(id)_state inContext:(WOContext *)_ctx {
129   if (_state == nil) return;
130 }
131
132 - (NSArray *)collectKeysInContext:(WOContext *)_ctx {
133   /* collect mode, collects all keys */
134   [_ctx setObject:UIxTabView_COLLECT forKey:UIxTabView_HEAD];
135   
136   [self->template appendToResponse:nil inContext:_ctx];
137   
138   [_ctx removeObjectForKey:UIxTabView_HEAD];
139   return [_ctx objectForKey:UIxTabView_KEYS];
140 }
141
142 /* responder */
143
144 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
145   id       nestedState;
146   NSString *activeTabKey;
147   
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];
153   
154   [_ctx setObject:activeTabKey forKey:UIxTabView_BODY];
155   
156 #if DEBUG_TAKEVALUES
157   [[_ctx component] debugWithFormat:@"UIxTabView: body takes values, eid='%@'",
158                     [_ctx elementID]];
159 #endif
160   
161   [self->template takeValuesFromRequest:_req inContext:_ctx];
162   
163   [_ctx removeObjectForKey:UIxTabView_BODY];
164   [_ctx deleteLastElementIDComponent]; // activeKey
165   [_ctx deleteLastElementIDComponent]; /* 'b' */
166   [self restoreNestedState:nestedState inContext:_ctx];
167 }
168
169 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
170   NSString *key;
171   id       result;
172   id       nestedState;
173   
174   if ((key = [_ctx currentElementID]) == nil)
175     return nil;
176   
177   result      = nil;
178   nestedState = [self saveNestedStateInContext:_ctx];
179     
180   if ([key isEqualToString:@"h"]) {
181     /* header action */
182     //NSString *urlKey;
183     
184     [_ctx consumeElementID];
185     [_ctx appendElementIDComponent:@"h"];
186 #if 0
187     if ((urlKey = [_ctx currentElementID]) == nil) {
188       [[_ctx application]
189              debugWithFormat:@"missing active head tab key !"];
190     }
191     else {
192       //NSLog(@"clicked: %@", urlKey);
193       [_ctx consumeElementID];
194       [_ctx appendElementIDComponent:urlKey];
195     }
196 #endif
197     
198     [_ctx setObject:self->selection forKey:UIxTabView_HEAD];
199     result = [self->template invokeActionForRequest:_req inContext:_ctx];
200     [_ctx removeObjectForKey:UIxTabView_HEAD];
201
202 #if 0
203     if (urlKey)
204       [_ctx deleteLastElementIDComponent]; // active key
205 #endif
206     [_ctx deleteLastElementIDComponent]; // 'h'
207   }
208   else if ([key isEqualToString:@"b"]) {
209     /* body action */
210     NSString *activeTabKey, *urlKey;
211     
212     [_ctx consumeElementID];
213     [_ctx appendElementIDComponent:@"b"];
214       
215     if ((urlKey = [_ctx currentElementID]) == nil) {
216       [[_ctx application]
217              debugWithFormat:@"missing active body tab key !"];
218     }
219     else {
220       //NSLog(@"clicked: %@", urlKey);
221       [_ctx consumeElementID];
222       [_ctx appendElementIDComponent:urlKey];
223     }
224     
225     activeTabKey = [self->selection stringValueInComponent:[_ctx component]];
226     [_ctx setObject:activeTabKey forKey:UIxTabView_BODY];
227     
228     result = [self->template invokeActionForRequest:_req inContext:_ctx];
229       
230     [_ctx removeObjectForKey:UIxTabView_BODY];
231
232     if (urlKey)
233       [_ctx deleteLastElementIDComponent]; // active key
234     [_ctx deleteLastElementIDComponent]; // 'b'
235   }
236   else {
237     [[_ctx application]
238            debugWithFormat:@"unknown tab container key '%@'", key];
239   }
240     
241   [self restoreNestedState:nestedState inContext:_ctx];
242   return result;
243 }
244
245 - (NSString *)_tabViewCountInContext:(WOContext *)_ctx {
246   int count;
247   count = [[_ctx valueForKey:@"UIxTabViewScriptDone"] intValue];
248   return [NSString stringWithFormat:@"%d",count];
249 }
250
251 - (NSString *)scriptHref:(UIxTabItemInfo *)_info
252   inContext:(WOContext *)_ctx
253   isLeft:(BOOL)_isLeft
254   keys:(NSArray *)_keys
255 {
256   NSMutableString *result = [NSMutableString string];
257   UIxTabItemInfo *tmp;
258   NSString       *activeKey;
259   int            i, cnt;
260   NSString       *elID;
261   NSString       *tstring;
262   
263   activeKey = [self->selection stringValueInComponent:[_ctx component]];
264   [result appendString:@"JavaScript:showTab("];
265   [result appendString:_info->key];
266   [result appendString:@"Tab);"];
267   
268   [result appendString:@"swapCorners("];
269   tstring = (!_isLeft)
270     ? @"tabCorner%@,tabCornerLeft%@);"
271     : @"tabCornerLeft%@,tabCorner%@);";
272   elID = [self _tabViewCountInContext:_ctx];
273   [result appendString:[NSString stringWithFormat:tstring,elID,elID]];
274   
275   for (i=0, cnt = [_keys count]; i < cnt; i++) {
276     tmp = [_keys objectAtIndex:i];
277
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);"];
283     }
284   }
285   return result;
286 }
287
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
293 {
294   NSString    *headUri    = nil;
295   NSString    *label      = nil;
296   NSString    *scriptHref = nil;
297   NSString    *styleName  = nil;
298
299   WEClientCapabilities *ccaps;
300   WOComponent *comp;
301
302   ccaps = [[_ctx request] clientCapabilities];
303
304   comp = [_ctx component];
305   headUri = _info->uri;
306
307   if ((label = _info->label) == nil)
308     label = _info->key;
309   
310   if (_isActive) {
311     styleName = (_info->selectedTabStyle)
312       ? _info->selectedTabStyle
313       : [self->selectedTabStyle stringValueInComponent:comp];
314   }
315   else {
316     styleName = (_info->tabStyle)
317       ? _info->tabStyle
318       : [self->tabStyle stringValueInComponent:comp];
319   }
320   
321   [_response appendContentString:@"<td align='center' valign='middle'"];
322   
323   if (styleName) {
324       [_response appendContentString:@" class='"];
325       [_response appendContentHTMLAttributeValue:styleName];
326       [_response appendContentCharacter:'\''];
327   }
328
329   // click on td background
330   if ([ccaps isInternetExplorer] && [ccaps isJavaScriptBrowser]) {
331       [_response appendContentString:@" onclick=\"window.location.href='"];
332       [_response appendContentHTMLAttributeValue:headUri];
333       [_response appendContentString:@"'\""];
334   }
335   
336   [_response appendContentCharacter:'>'];
337
338   [_response appendContentString:@"<a href=\""];
339   
340   if (_doScript && (_info->isScript || _isActive)) {
341     scriptHref =
342       [self scriptHref:_info inContext:_ctx isLeft:_isLeft keys:_keys];
343     [_response appendContentHTMLAttributeValue:scriptHref];
344   }
345   else {
346     [_response appendContentHTMLAttributeValue:headUri];
347   }
348   
349   [_response appendContentString:@"\" "];
350   [_response appendContentString:
351                [NSString stringWithFormat:@"name='%@TabLink'", _info->key]];
352   [_response appendContentString:@">"];
353   
354   if ([label length] < 1)
355       label = _info->key;
356   [_response appendContentString:@"<nobr>"];
357   [_response appendContentHTMLString:label];
358   [_response appendContentString:@"</nobr>"];
359   
360   [_response appendContentString:@"</a>"];
361
362   [_response appendContentString:@"</td>"];
363
364 #if 0
365   if (_doScript && (_info->isScript || _isActive)) {
366     NSString *k; // key 
367     NSString *s; // selected   tab icon
368     NSString *u; // unselected tab icon
369     //NSString *out;
370
371     k = _info->key;
372     s = _info->selIcon; // selectedTabIcon
373     u = (_isLeft) ? _info->leftIcon : _info->tabIcon;
374     
375     s = WEUriOfResource(s, _ctx);
376     u = WEUriOfResource(u, _ctx);
377
378     s = ([s length] < 1) ? imgUri : s;
379     u = ([u length] < 1) ? imgUri : u;
380     
381 #if 0
382     out = [NSString alloc];
383     out = [out initWithFormat:
384                     @"<script language=\"JavaScript\">\n"
385                     @"<!--\n"
386                     @"var %@Tab = new Array();\n"
387                     @"%@Tab[\"link\"] = %@TabLink;\n"
388                     @"%@Tab[\"href1\"] = \"%@\";\n"
389                     @"%@Tab[\"href2\"] = \"%@\";\n"
390                     @"%@Tab[\"Img\"] = window.document.%@TabImg;\n"
391                     @"%@Tab[\"Ar\"]  = new Array();\n"
392                     @"%@Tab[\"Ar\"][0] = new Image();\n"
393                     @"%@Tab[\"Ar\"][0].src = \"%@\";\n"
394                     @"%@Tab[\"Ar\"][1] = new Image();\n"
395                     @"%@Tab[\"Ar\"][1].src = \"%@\";\n"
396                     @"//-->\n</script>",
397                     k, k, k, k, scriptHref, k, headUri,
398                     k, k, k, k,
399                     k, u, k, k, s
400                     ];
401  
402     [_response appendContentString:out];
403     RELEASE(out);
404 #else
405 #  define _appendStr_(_str_) [_response appendContentString:_str_]
406     _appendStr_(@"<script language=\"JavaScript\">\n<!--\nvar ");
407     _appendStr_(k); _appendStr_(@"Tab = new Array();\n");
408
409     _appendStr_(k); _appendStr_(@"Tab[\"link\"] = ");
410     _appendStr_(k); _appendStr_(@"TabLink;\n");   // linkName
411       
412     _appendStr_(k); _appendStr_(@"Tab[\"href1\"] = \"");
413     _appendStr_(scriptHref); _appendStr_(@"\";\n"); // scriptHref
414
415     _appendStr_(k); _appendStr_(@"Tab[\"href2\"] = \"");
416     _appendStr_(_info->uri); _appendStr_(@"\";\n"); // actionHref
417       
418     _appendStr_(k); _appendStr_(@"Tab[\"Img\"] = window.document.");
419     _appendStr_(k); _appendStr_(@"TabImg;\n");
420     _appendStr_(k); _appendStr_(@"Tab[\"Ar\"]  = new Array();\n");
421     _appendStr_(k); _appendStr_(@"Tab[\"Ar\"][0] = new Image();\n");
422     _appendStr_(k); _appendStr_(@"Tab[\"Ar\"][0].src = \"");
423     _appendStr_(u); _appendStr_(@"\";\n");  // unselected img
424     _appendStr_(k); _appendStr_(@"Tab[\"Ar\"][1] = new Image();\n");
425     _appendStr_(k); _appendStr_(@"Tab[\"Ar\"][1].src = \"");
426     _appendStr_(s); _appendStr_(@"\";\n");  // selected img
427     _appendStr_(@"//-->\n</script>");
428 #undef _appendStr_
429 #endif
430   }
431 #endif
432 }
433
434 - (void)appendSubmitButton:(UIxTabItemInfo *)_info
435   toResponse:(WOResponse *)_response
436   inContext:(WOContext *)_ctx
437   isActive:(BOOL)_isActive isLeft:(BOOL)_left
438   doScript:(BOOL)_doScript   keys:(NSArray *)_keys
439 {
440   [self appendLink:_info
441         toResponse:_response
442         inContext:_ctx
443         isActive:_isActive isLeft:_left
444         doScript:_doScript   keys:_keys];
445 }
446
447 - (void)_appendTabViewJSScriptToResponse:(WOResponse *)_response
448   inContext:(WOContext *)_ctx
449 {
450   [_response appendContentString:
451                @"<script language=\"JavaScript\">\n<!--\n\n"
452                @"function showTab(obj) {\n"
453 #if DEBUG_JS
454                @"  if (obj==null) { alert('missing tab obj ..'); return; }\n"
455                @"  if (obj['Div']==null) {"
456                @"    alert('missing div key in ' + obj); return; }\n"
457                @"  if (obj['Div'].style==null) {"
458                @"    alert('missing style key in div ' + obj['Div']);return; }\n"
459 #endif
460                @"  obj['Div'].style.display = \"\";\n"
461                @"  obj['Img'].src = obj[\"Ar\"][1].src;\n"
462                @"  obj['link'].href = obj[\"href2\"];\n"
463                @"}\n"
464                @"function hideTab(obj) {\n"
465 #if DEBUG_JS
466                @"  if (obj==null) { alert('missing tab obj ..'); return; }\n"
467                @"  if (obj['Div']==null) {"
468                @"    alert('missing div key in ' + obj); return; }\n"
469                @"  if (obj['Div'].style==null) {"
470                @"    alert('missing style key in div ' + obj['Div']);return; }\n"
471 #endif
472                @" obj['Div'].style.display = \"none\";\n"
473                @" obj['Img'].src = obj[\"Ar\"][0].src;\n"
474                @" obj['link'].href = obj[\"href1\"];\n"
475                @"}\n"
476                @"function swapCorners(obj1,obj2) {\n"
477                @"   if (obj1==null) { alert('missing corner 1'); return; }\n"
478                @"   if (obj2==null) { alert('missing corner 2'); return; }\n"
479                @"   obj1.style.display = \"none\";\n"
480                @"   obj2.style.display = \"\";\n"
481                @"}\n"
482                @"//-->\n</script>"];
483 }
484
485 - (void)_appendHeaderRowToResponse:(WOResponse *)_response
486   inContext:(WOContext *)_ctx
487   keys:(NSArray *)keys activeKey:(NSString *)activeKey
488   doScript:(BOOL)doScript
489 {
490   unsigned  i, count;
491   BOOL      doForm;
492   NSString  *styleName;
493   
494   doForm = NO;  /* generate form controls ? */
495   
496   [_response appendContentString:@"<tr><td colspan='2'>"];
497   
498   styleName = [self->headerStyle stringValueInComponent:[_ctx component]];
499   if(styleName) {
500       [_response appendContentString:
501           @"<table border='0' cellpadding='0' cellspacing='0' class='"];
502       [_response appendContentHTMLAttributeValue:styleName];
503       [_response appendContentString:@"'><tr>"];
504   }
505   else {
506       [_response appendContentString:
507           @"<table border='0' cellpadding='0' cellspacing='0'><tr>"];
508   }
509
510   for (i = 0, count = [keys count]; i < count; i++) {
511     UIxTabItemInfo *info;
512     NSString       *key;
513     BOOL           isActive;
514     
515     info     = [keys objectAtIndex:i];
516     key      = info->key;
517     isActive = [key isEqualToString:activeKey];
518     
519     [_ctx appendElementIDComponent:key];
520     
521     if (doForm) {
522       /* tab is inside of a FORM, so produce submit buttons */
523       [self appendSubmitButton:info
524             toResponse:_response
525             inContext:_ctx
526             isActive:isActive
527             isLeft:(i == 0) ? YES : NO
528             doScript:doScript
529             keys:keys];
530     }
531     else {
532       /* tab is not in a FORM, generate hyperlinks for tab */
533       [self appendLink:info
534             toResponse:_response
535             inContext:_ctx
536             isActive:isActive
537             isLeft:(i == 0) ? YES : NO
538             doScript:doScript
539             keys:keys];
540     }
541     
542     [_ctx deleteLastElementIDComponent];
543   }
544   //  [_response appendContentString:@"<td></td>"];
545   [_response appendContentString:@"</tr></table>"];
546   [_response appendContentString:@"</td></tr>"];
547 }
548
549 - (void)_appendHeaderFootRowToResponse:(WOResponse *)_response
550   inContext:(WOContext *)_ctx
551   bgcolor:(NSString *)bgcolor
552   doScript:(BOOL)doScript
553   isLeftActive:(BOOL)isLeftActive
554 {
555   NSString *styleName;
556   [_response appendContentString:@"  <tr"];
557     
558   styleName = [self->bodyStyle stringValueInComponent:[_ctx component]];
559   if(styleName) {
560     [_response appendContentString:@" class='"];
561     [_response appendContentHTMLAttributeValue:styleName];
562     [_response appendContentCharacter:'\''];
563   }
564   if (bgcolor) {
565     [_response appendContentString:@" bgcolor=\""];
566     [_response appendContentHTMLAttributeValue:bgcolor];
567     [_response appendContentString:@"\""];
568   }
569   [_response appendContentString:@">\n"];
570     
571   /* left corner */
572   [_response appendContentString:@"    <td align=\"left\" width=\"10\">"];
573   
574   if (doScript) {
575     [_response appendContentString:@"<div id=\"tabCorner"];
576     [_response appendContentString:[self _tabViewCountInContext:_ctx]];
577     [_response appendContentString:@"\" "];
578     [_response appendContentString:@"style=\"display: "];
579     [_response appendContentString:(isLeftActive) ? @"" : @"none"];
580     [_response appendContentString:@";\">"];
581     [_response appendContentString:@"&nbsp;"];
582     [_response appendContentString:@"</div>"];
583   }
584   else if (isLeftActive)
585     [_response appendContentString:@"&nbsp;"];
586   
587   if (doScript) {
588     [_response appendContentString:@"<div id=\"tabCornerLeft"];
589     [_response appendContentString:[self _tabViewCountInContext:_ctx]];
590     [_response appendContentString:@"\" "];
591     [_response appendContentString:@"style=\"display: "];
592     [_response appendContentString:(!isLeftActive) ? @"visible" : @"none"];
593     [_response appendContentString:@";\">"];
594   }
595   
596   if (!isLeftActive || doScript) {
597     NSString *uri;
598     
599     uri = [self->leftCornerIcon stringValueInComponent:[_ctx component]];
600     if ((uri = WEUriOfResource(uri, _ctx))) {
601       [_response appendContentString:@"<img border=\"0\" alt=\"\" src=\""];
602       [_response appendContentString:uri];
603       [_response appendContentString:@"\">"];
604     }
605     else
606       [_response appendContentString:@"&nbsp;"];
607   }
608   if (doScript)
609     [_response appendContentString:@"</div>"];
610
611   [_response appendContentString:@"</td>\n"];
612
613   /* right corner */
614   [_response appendContentString:@"    <td align=\"right\">"];
615   {
616     NSString *uri;
617       
618     uri = [self->rightCornerIcon stringValueInComponent:[_ctx component]];
619     if ((uri = WEUriOfResource(uri, _ctx))) {
620       [_response appendContentString:@"<img border=\"0\" alt=\"\" src=\""];
621       [_response appendContentString:uri];
622       [_response appendContentString:@"\" />"];
623     }
624     else
625       [_response appendContentString:@"&nbsp;"];
626   }
627   [_response appendContentString:@"</td>\n"];
628     
629   [_response appendContentString:@"  </tr>\n"];
630 }
631
632 - (void)_appendBodyRowToResponse:(WOResponse *)_response
633   inContext:(WOContext *)_ctx
634   bgcolor:(NSString *)bgcolor
635   activeKey:(NSString *)activeKey
636 {
637   WEClientCapabilities *ccaps;
638   BOOL indentContent;
639   NSString *styleName;
640
641   styleName = [self->bodyStyle stringValueInComponent:[_ctx component]];
642   ccaps = [[_ctx request] clientCapabilities];
643
644   /* put additional padding table into content ??? */
645   indentContent = [ccaps isFastTableBrowser] && ![ccaps isTextModeBrowser];
646   
647   [_response appendContentString:@"<tr"];
648   if(styleName) {
649     [_response appendContentString:@" class='"];
650     [_response appendContentHTMLAttributeValue:styleName];
651     [_response appendContentCharacter:'\''];
652   }
653   [_response appendContentString:@"><td colspan='2'"];
654   if (bgcolor) {
655     [_response appendContentString:@" bgcolor=\""];
656     [_response appendContentHTMLAttributeValue:bgcolor];
657     [_response appendContentCharacter:'\"'];
658   }
659   [_response appendContentCharacter:'>'];
660     
661   if (indentContent) {
662     /* start padding table */
663     [_response appendContentString:
664                @"<table border='0' width='100%'"
665                @" cellpadding='10' cellspacing='0'>"];
666     [_response appendContentString:@"<tr><td>"];
667   }
668     
669   [_ctx appendElementIDComponent:@"b"];
670   [_ctx appendElementIDComponent:activeKey];
671   
672   /* generate currently active body */
673   {
674     [_ctx setObject:activeKey forKey:UIxTabView_BODY];
675     [self->template appendToResponse:_response inContext:_ctx];
676     [_ctx removeObjectForKey:UIxTabView_BODY];
677   }
678   
679   [_ctx deleteLastElementIDComponent]; // activeKey
680   [_ctx deleteLastElementIDComponent]; // 'b'
681     
682   if (indentContent)
683     /* close padding table */
684     [_response appendContentString:@"</td></tr></table>"];
685     
686   [_response appendContentString:@"</td></tr>"];
687 }
688
689 - (BOOL)isLeftActiveInKeys:(NSArray *)keys activeKey:(NSString *)activeKey{
690   unsigned i, count;
691   BOOL isLeftActive;
692   
693   isLeftActive = NO;
694   
695   for (i = 0, count = [keys count]; i < count; i++) {
696     UIxTabItemInfo *info;
697     
698     info = [keys objectAtIndex:i];
699     
700     if ((i == 0) && [info->key isEqualToString:activeKey])
701       isLeftActive = YES;
702   }
703   
704   return isLeftActive;
705 }
706
707 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
708   WOComponent  *cmp;
709   NSString     *bgcolor;
710   BOOL         isLeftActive;
711   BOOL         doScript;
712   id           nestedState;
713   NSString     *activeKey;
714   NSArray      *keys;
715   int          tabViewCount; /* used for image id's and writing script once */
716   
717   doScript      = NO;  /* perform tab-clicks on browser (use javascript) */
718   tabViewCount  = [[_ctx valueForKey:@"UIxTabViewScriptDone"] intValue];
719   cmp           = [_ctx component];
720   
721   /* check for browser */
722   {
723     WEClientCapabilities *ccaps;
724     //BOOL isJavaScriptBrowser;
725     
726     ccaps    = [[_ctx request] clientCapabilities];
727     doScript = [ccaps isInternetExplorer] && [ccaps isJavaScriptBrowser]; 
728     doScript = (doScript &&
729                 [[[cmp session]
730                        valueForKey:@"isJavaScriptEnabled"] boolValue]);
731   }
732
733   /* disable javascript */
734   doScript = NO;
735   
736   /* save state */
737   
738   nestedState = [self saveNestedStateInContext:_ctx];
739   
740   /* configure */
741   
742   activeKey = [self->selection stringValueInComponent:cmp];
743   
744   bgcolor = [self->bgColor stringValueInComponent:cmp];
745   bgcolor = [bgcolor stringValue];
746   
747   [_ctx appendElementIDComponent:@"h"];
748   
749   /* collect & process keys (= available tabs) */
750   
751   keys = [self collectKeysInContext:_ctx];
752   
753   if (![[keys valueForKey:@"key"] containsObject:activeKey])
754     /* selection is not available in keys */
755     activeKey = nil;
756   
757   if ((activeKey == nil) && ([keys count] > 0)) {
758     /* no or invalid selection, use first key */
759     activeKey = [[keys objectAtIndex:0] key];
760     if ([self->selection isValueSettable])
761       [self->selection setValue:activeKey inComponent:[_ctx component]];
762   }
763
764   if (doScript) {
765     doScript = [[keys valueForKey:@"isScript"] containsObject:YesNumber];
766     [_ctx setObject:[NSNumber numberWithBool:doScript]
767           forKey:UIxTabView_SCRIPT];
768   }
769
770   /* start appending */
771   
772   if ((doScript) && (tabViewCount == 0))
773     [self _appendTabViewJSScriptToResponse:_response inContext:_ctx];
774   
775   /* count up for unique tabCorner/tabCornerLeft images */
776   [_ctx takeValue:[NSNumber numberWithInt:(tabViewCount + 1)]
777         forKey:@"UIxTabViewScriptDone"];
778   
779   [_response appendContentString:
780                @"<table border='0' width='100%'"
781                @" cellpadding='0' cellspacing='0'>"];
782   
783   /* find out whether left is active */
784   
785   isLeftActive = [self isLeftActiveInKeys:keys activeKey:activeKey];
786   
787   /* generate header row */
788   
789   [self _appendHeaderRowToResponse:_response inContext:_ctx
790         keys:keys activeKey:activeKey
791         doScript:doScript];
792   
793   [_ctx deleteLastElementIDComponent]; // 'h' for head
794   [_ctx removeObjectForKey:UIxTabView_HEAD];
795
796 #if 0
797   /* header foot row */
798
799   [self _appendHeaderFootRowToResponse:_response inContext:_ctx
800         bgcolor:bgcolor
801         doScript:doScript
802         isLeftActive:isLeftActive];
803 #endif
804
805   /* body row */
806   
807   [self _appendBodyRowToResponse:_response inContext:_ctx
808         bgcolor:bgcolor
809         activeKey:activeKey];
810   
811   /* close table */
812   
813   [_response appendContentString:@"</table>"];
814   [_ctx removeObjectForKey:UIxTabView_ACTIVEKEY];
815   [_ctx removeObjectForKey:UIxTabView_KEYS];
816   [self restoreNestedState:nestedState inContext:_ctx];
817 }
818
819 @end /* UIxTabView */