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