]> err.no Git - sope/blob - sope-appserver/WEExtensions/WETreeView.m
added strict OSX bundle dependencies
[sope] / sope-appserver / WEExtensions / WETreeView.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE 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   SOPE 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 SOPE; 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
22 /*
23   WETreeView
24   
25   A WETreeView is very similiar to a WETableView (eg it can have arbitary
26   columns), but can also show/manage a tree of objects.
27   
28   TODO: we should support a cookie to store the tree hiearchy for stateless 
29         servers like the Zope tree. This probably needs to be implemented in
30         takeValues (cookie decoding) and appendToResponse (cookie encoding).
31         Zooming (invokeAction) needs to ignore? the component-id in case the
32         cookie is set and somehow work based on cookie data.
33
34   TODO: we need to support CSS.
35   
36   WETreeView associations:
37     list & sublist & (item | index | currentPath)
38     itemIsLeaf
39     showItem
40     zoom
41     string
42
43     noTable
44
45     // config:
46     iconWidth
47     plusIcon 
48     minusIcon
49     leafIcon
50     junctionIcon
51     cornerIcon
52     cornerPlusIcon
53     cornerMinusIcon
54     leafCornerIcon
55     lineIcon
56
57   WETreeHeader associations:
58     isTreeElement
59     icon
60     cornerIcon
61     title
62     string
63
64   WETreeData associations:
65     isTreeElement
66     icon
67     cornerIcon
68     title
69     string
70   
71   Example:
72
73     TestTree.wod:
74       --- snip ---
75       TestTree: WETreeView {
76         list    = rootList;
77         item    = item;
78         index   = index;
79         sublist = item.sublist;
80         zoom    = treeState.isExpanded; // take a look at LSWTreeState !!!
81         // if you leave out *zoom*, the tree is rendered full expanded
82         // and without plus and minus icons
83
84         // if no icons are specified, the tree replaces these icons with
85         // ascii characters (that style is supposed to be ugly :-)
86
87         // icon config
88         iconWidth       = "13"; // every icon's width should be equal to "13"
89         plusIcon        = "plus.gif";
90         minusIcon       = "minus.gif";
91         leafIcon        = "leaf.gif";
92         junctionIcon    = "junction.gif";
93         cornerIcon      = "corner.gif";
94         cornerPlusIcon  = "corner_plus.gif";
95         cornerMinusIcon = "corner_miunus.gif";
96         leafCornerIcon  = "leaf_corner.gif";
97         lineIcon        = "line.gif";
98       }
99       TreeDataCell: WETreeData {
100         isTreeElement = YES; // this is a tree cell (that means, it has plus
101                              // and minus icons and all that stuff)
102       }
103       DataCell: WETreeData {
104         isTreeElement = NO;  // this is NOT a  tree cell, i.e. it does NOT
105                              // have any plus or minus icons. (This is just a
106                              // ordinary <td></td>!!!)
107       }
108
109       TreeHeaderCell: WETreeHeader {
110         isTreeElement = YES;
111       }
112       HeaderCell: WETreeHeader {
113         isTreeElement = NO;
114       }
115
116       --- snap ---
117
118     TestTree.html:
119       --- snip ---
120       <#TestTree>
121         <!--- tree header --->
122           <#TreeHeaderCell>some title</#TreeHeaderCell>
123           <#HeaderCell">some title</#HeaderCell>
124           <#HeaderCell">some title</#HeaderCell>
125
126         <!-- tree content -->
127
128           <#TreeDataCell">some content</#TreeDataCell>
129           <#DataCell">some content</#DataCell>
130           <#DataCell">some content</#DataCell>
131       </#TestTree>
132       --- snap ---
133
134     TestTree.wox
135       ---snip---
136         <var:treeview list="root" sublist="item.sublist" item="item"
137                       currentPath="currentPath" zoom="isZoom"
138                       showItem="showItem"
139         >
140           <var:tree-header const:isTreeElement="YES">treecell</var:tree-header>
141           <var:tree-header const:isTreeElement="NO" const:bgcolor="#FFDAAA">
142             <b>first name</b>
143           </var:tree-header>
144         </var:treeview>
145       ---snap---
146 */
147
148 #include <NGObjWeb/WODynamicElement.h>
149
150 @class NSMutableArray;
151
152 @interface WETreeView : WODynamicElement
153 {
154   // WODynamicElement: extraAttributes
155   // WODynamicElement: otherTagString
156 @protected
157   WOAssociation  *list;        // array of objects to iterate through
158   WOAssociation  *item;        // current item in the array
159   WOAssociation  *sublist;     // sub list of item
160   WOAssociation  *itemIsLeaf;  // hh-optimization
161   WOAssociation  *index;       // current index
162   WOAssociation  *zoom;        // show sub list of item (BOOL)
163   WOAssociation  *currentPath; //
164   WOAssociation  *showItem;    // show current item
165
166   WOAssociation  *noTable;     // render no TABLE (BOOL)
167   
168   // config:
169   WOAssociation  *plusIcon;
170   WOAssociation  *minusIcon;
171   WOAssociation  *leafIcon;
172   WOAssociation  *junctionIcon;
173   WOAssociation  *cornerIcon;
174   WOAssociation  *cornerPlusIcon;
175   WOAssociation  *cornerMinusIcon;
176   WOAssociation  *leafCornerIcon;
177   WOAssociation  *lineIcon;
178   WOAssociation  *spaceIcon;
179   WOAssociation  *iconWidth;
180
181   // private:
182   NSMutableArray *matrix;
183   
184   WOElement      *template;
185 }
186
187 @end /* WETreeView */
188
189 #include "WETreeContextKeys.h"
190 #include "WETreeMatrixElement.h"
191 #include "common.h"
192 #include <NGObjWeb/WEClientCapabilities.h>
193
194 NSString *WETreeView_HEADER_MODE    = @"WETreeView_HEADER_MODE";
195 NSString *WETreeView_ZOOM_ACTION_ID = @"_";
196
197 NSString *WETreeView_TreeElement    = @"WETreeView_TreeElement";
198 NSString *WETreeView_RenderNoTable  = @"WETreeView_RenderNoTable";
199
200 NSString *WETreeView_IconWidth      = @"WETreeView_IconWidth";
201 NSString *WETreeView_Plus           = @"WETreeView_Plus";
202 NSString *WETreeView_Minus          = @"WETreeView_Minus";
203 NSString *WETreeView_Leaf           = @"WETreeView_Leaf";
204 NSString *WETreeView_Line           = @"WETreeView_Line";
205 NSString *WETreeView_Junction       = @"WETreeView_Junction";
206 NSString *WETreeView_Corner         = @"WETreeView_Corner";
207 NSString *WETreeView_CornerPlus     = @"WETreeView_CornerPlus";
208 NSString *WETreeView_CornerMinus    = @"WETreeView_CornerMinus";
209 NSString *WETreeView_CornerLeaf     = @"WETreeView_CornerLeaf";
210 NSString *WETreeView_Space          = @"WETreeView_Space";
211
212 @implementation WETreeView
213
214 - (id)initWithName:(NSString *)_name
215   associations:(NSDictionary *)_config
216   template:(WOElement *)_t
217 {
218   if ((self = [super initWithName:_name associations:_config template:_t])) {
219     self->list        = WOExtGetProperty(_config, @"list");
220     self->item        = WOExtGetProperty(_config, @"item");
221     self->index       = WOExtGetProperty(_config, @"index");
222     self->sublist     = WOExtGetProperty(_config, @"sublist");
223     self->itemIsLeaf  = WOExtGetProperty(_config, @"itemIsLeaf");
224     self->zoom        = WOExtGetProperty(_config, @"zoom");
225     self->currentPath = WOExtGetProperty(_config, @"currentPath");
226     self->showItem    = WOExtGetProperty(_config, @"showItem");
227
228     self->noTable     = WOExtGetProperty(_config, @"noTable");
229     
230     // config
231     self->plusIcon        = WOExtGetProperty(_config, @"plusIcon");
232     self->minusIcon       = WOExtGetProperty(_config, @"minusIcon");
233     self->leafIcon        = WOExtGetProperty(_config, @"leafIcon");
234     self->junctionIcon    = WOExtGetProperty(_config, @"junctionIcon");
235     self->cornerIcon      = WOExtGetProperty(_config, @"cornerIcon");
236     self->cornerPlusIcon  = WOExtGetProperty(_config, @"cornerPlusIcon");
237     self->cornerMinusIcon = WOExtGetProperty(_config, @"cornerMinusIcon");
238     self->leafCornerIcon  = WOExtGetProperty(_config, @"leafCornerIcon");
239     self->lineIcon        = WOExtGetProperty(_config, @"lineIcon");
240     self->spaceIcon       = WOExtGetProperty(_config, @"spaceIcon");
241     self->iconWidth       = WOExtGetProperty(_config, @"iconWidth");
242     
243     self->template = [_t retain];
244   }
245   return self;
246 }
247
248 - (void)dealloc {
249   [self->itemIsLeaf  release];
250   [self->sublist     release];
251   [self->list        release];
252   [self->item        release];
253   [self->index       release];
254   [self->zoom        release];
255   [self->currentPath release];
256   [self->showItem    release];
257
258   [self->noTable     release];
259
260   [self->plusIcon        release];
261   [self->minusIcon       release];
262   [self->leafIcon        release];
263   [self->junctionIcon    release];
264   [self->cornerIcon      release];
265   [self->cornerPlusIcon  release];
266   [self->cornerMinusIcon release];
267   [self->leafCornerIcon  release];
268   [self->lineIcon        release];
269   [self->spaceIcon       release];
270   [self->iconWidth       release];
271
272   [self->template release];
273   [self->matrix   release];
274   
275   [super dealloc];
276 }
277
278 - (void)updateConfigInContext:(WOContext *)_ctx {
279   NSString       *tmp;
280   WOComponent    *cmp;
281
282   cmp = [_ctx component];
283
284   // TODO: replace the macro with methods?
285 #define SetConfigInContext(_a_, _key_)                                  \
286       if (_a_ && (tmp = [_a_ valueInComponent:cmp]))                    \
287         [_ctx setObject:tmp forKey:_key_];                              \
288
289   SetConfigInContext(self->plusIcon,        WETreeView_Plus);
290   SetConfigInContext(self->minusIcon,       WETreeView_Minus);
291   SetConfigInContext(self->leafIcon,        WETreeView_Leaf);
292   SetConfigInContext(self->junctionIcon,    WETreeView_Junction);
293   SetConfigInContext(self->cornerIcon,      WETreeView_Corner);
294   SetConfigInContext(self->cornerPlusIcon,  WETreeView_CornerPlus);
295   SetConfigInContext(self->cornerMinusIcon, WETreeView_CornerMinus);
296   SetConfigInContext(self->leafCornerIcon,  WETreeView_CornerLeaf);
297   SetConfigInContext(self->lineIcon,        WETreeView_Line);
298   SetConfigInContext(self->spaceIcon,       WETreeView_Space);
299   SetConfigInContext(self->iconWidth,       WETreeView_IconWidth);
300   
301 #undef SetConfigInContext
302 }
303
304 - (void)removeConfigInContext:(WOContext *)_ctx {
305   [_ctx removeObjectForKey:WETreeView_Plus];
306   [_ctx removeObjectForKey:WETreeView_Minus];
307   [_ctx removeObjectForKey:WETreeView_Leaf];
308   [_ctx removeObjectForKey:WETreeView_Junction];
309   [_ctx removeObjectForKey:WETreeView_Corner];
310   [_ctx removeObjectForKey:WETreeView_CornerPlus];
311   [_ctx removeObjectForKey:WETreeView_CornerMinus];
312   [_ctx removeObjectForKey:WETreeView_CornerLeaf];
313   [_ctx removeObjectForKey:WETreeView_Line];
314   [_ctx removeObjectForKey:WETreeView_Space];
315   [_ctx removeObjectForKey:WETreeView_IconWidth];
316 }
317
318 - (id)_toggleZoomInContext:(WOContext *)_ctx {
319   WOComponent *component = [_ctx component];
320
321   if ([self->zoom isValueSettable]) {
322     BOOL isZoom;
323     
324     isZoom = [self->zoom boolValueInComponent:component];
325     [self->zoom setBoolValue:!isZoom inComponent:component];
326   }
327   return nil;
328 }
329
330 /* OWResponder */
331
332 - (NSArray *)_sublistInContext:(WOContext *)_ctx {
333   NSArray *a;
334   
335   if (self->sublist == nil)
336     return nil;
337   if (self->itemIsLeaf) {
338     if ([self->itemIsLeaf boolValueInComponent:[_ctx component]])
339       return nil;
340   }
341   
342   if ((a = [self->sublist valueInComponent:[_ctx component]]) == nil)
343     return nil;
344   
345   return ([a count] > 0) ? a : nil;
346 }
347
348 - (void)_takeValuesFromRequest:(WORequest *)_req
349   inContext:(WOContext *)_ctx
350   withArray:(NSArray *)array
351       depth:(int)_depth
352 {
353   WOComponent *cmp;
354   int i, cnt;
355
356 #if DEBUG
357 #if 1
358   if (!(_depth <= MAX_TREE_DEPTH-1)) {
359     NSLog(@"ERROR[%s]: WETreeView takeValuesFromRequest: max."
360           @"recursion depth is %d",
361           __PRETTY_FUNCTION__, MAX_TREE_DEPTH-1);
362     return;
363   }
364 #else  
365   NSAssert1((_depth <= MAX_TREE_DEPTH-1),
366             @"WETreeView takeValuesFromRequest: max. recursion depth is %d",
367             MAX_TREE_DEPTH-1);
368 #endif  
369 #endif
370   
371   cmp = [_ctx component];
372   cnt = [array count];
373   
374   [_ctx appendZeroElementIDComponent]; // append index
375   for (i = 0; i < cnt; i++) {
376     NSArray *subArray;
377     
378     if ([self->index isValueSettable])
379       [self->index setUnsignedIntValue:i inComponent:cmp];
380     if ([self->item isValueSettable])
381       [self->item setValue:[array objectAtIndex:i] inComponent:cmp];
382
383     if (self->showItem && ![self->showItem boolValueInComponent:cmp])
384       continue;
385
386     if (self->zoom == nil || [self->zoom boolValueInComponent:cmp])
387       subArray = [self _sublistInContext:_ctx];
388     else
389       subArray = nil;
390     
391     if (subArray)
392       [self _takeValuesFromRequest:_req
393                          inContext:_ctx
394                          withArray:subArray
395                              depth:_depth+1];
396     else
397       [self->template takeValuesFromRequest:_req inContext:_ctx];
398     
399     [_ctx incrementLastElementIDComponent];
400   }
401   [_ctx deleteLastElementIDComponent]; // delete index
402 }
403
404 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
405   NSArray *array;
406
407   array = [self->list valueInComponent:[_ctx component]];
408   [self _takeValuesFromRequest:_req inContext:_ctx withArray:array depth:0];
409 }
410
411 - (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
412   WOComponent *sComponent;
413   id       result  = nil;
414   id       idxId   = nil;
415   id       object  = nil;
416   NSMutableArray *stack = nil;
417   NSArray  *array;
418   unsigned idCount = 0;
419
420   sComponent = [_ctx component];
421   array      = [self->list valueInComponent:sComponent];
422   if ([array count] < 1) return nil;
423   
424   stack = [NSMutableArray arrayWithCapacity:8];
425   
426   idxId = [_ctx currentElementID]; // top level index
427   idCount = 0;
428   
429   if ([idxId isEqualToString:@"h"]) {
430     [_ctx setObject:@"YES" forKey:WETreeView_HEADER_MODE];
431     [_ctx appendElementIDComponent:@"h"];
432     [_ctx consumeElementID];
433     result = [self->template invokeActionForRequest:_rq inContext:_ctx];
434     [_ctx deleteLastElementIDComponent];
435     [_ctx removeObjectForKey:WETreeView_HEADER_MODE];
436     return result;
437   }
438   
439   while ((![idxId isEqualToString:@"end"]) && (idxId != nil) &&
440          (array != nil)) {
441     unsigned idx = [idxId unsignedIntValue];
442
443     object = [array objectAtIndex:idx];
444     [stack addObject:object];
445     
446     if ([self->index isValueSettable])
447       [self->index setUnsignedIntValue:idx inComponent:sComponent];
448     if ([self->item isValueSettable])
449       [self->item setValue:object inComponent:sComponent];
450     if ([self->currentPath isValueSettable])
451       [self->currentPath setValue:stack inComponent:sComponent];
452
453     array = [self->sublist valueInComponent:sComponent];
454     
455     [_ctx appendElementIDComponent:idxId]; idCount++;
456     idxId = [_ctx consumeElementID]; // sub level index
457   }
458   if ([idxId isEqualToString:@"end"]) {
459     [_ctx appendElementIDComponent:idxId]; idCount++;
460     idxId = [_ctx consumeElementID];
461   }
462   
463   result = ([[_ctx senderID] hasSuffix:WETreeView_ZOOM_ACTION_ID])
464     ? [self _toggleZoomInContext:_ctx]
465     : [self->template invokeActionForRequest:_rq inContext:_ctx];
466   
467   /* remove element-ids */
468   for (; idCount > 0; idCount--)
469     [_ctx deleteLastElementIDComponent];
470   
471   return result;
472 }
473
474 - (void)appendList:(NSArray *)_list
475   treeElement:(_WETreeMatrixElement *)_element
476   inContext:(WOContext *)_ctx
477 {
478   /* TODO: split up this method! */
479   WOComponent    *comp;
480   unsigned       i, cnt;
481
482   comp = [_ctx component];
483   cnt  = [_list count];
484
485 #if DEBUG
486 #if 1  
487   if (!([_element depth] <= MAX_TREE_DEPTH-1)) {
488     NSLog(@"ERROR[%s]: WETreeView takeValuesFromRequest: max."
489           @"recursion depth is %d",
490           __PRETTY_FUNCTION__, MAX_TREE_DEPTH-1);
491     return;
492   }
493 #else  
494   NSAssert1(([_element depth] <= MAX_TREE_DEPTH-1),
495             @"WETreeView appendToResponse: max. recursion depth is %d",
496             MAX_TREE_DEPTH-1);
497 #endif  
498 #endif
499     
500   for (i = 0; i < cnt; i++) {
501     id object = [_list objectAtIndex:i];
502
503     [_element setIndex:i];
504     [_element setItem:object];
505     
506     if ([self->index isValueSettable])
507       [self->index setUnsignedIntValue:i inComponent:comp];
508     if ([self->item isValueSettable])
509       [self->item setValue:object inComponent:comp];
510     if ([self->currentPath isValueSettable])
511       [self->currentPath setValue:[_element currentPath] inComponent:comp];
512
513     if (self->showItem && ![self->showItem boolValueInComponent:comp])
514       continue;
515
516     {
517       BOOL    isLast;
518       BOOL    isLeaf;
519       BOOL    isZoom = YES;
520       NSArray *sl;
521
522       if (self->itemIsLeaf) {
523         isLeaf = [self->itemIsLeaf boolValueInComponent:comp];
524         isZoom = (self->zoom)
525           ? [self->zoom boolValueInComponent:comp]
526           : YES;
527         
528         sl = (!isLeaf && isZoom)
529           ? [self _sublistInContext:_ctx]
530           : nil;
531       }
532       else {
533         sl = [self _sublistInContext:_ctx];
534         isLeaf = !([sl count] > 0);
535       }
536
537       if (self->showItem) {
538         if (i == (cnt-1))
539           isLast = YES;
540         else {
541           id       obj;
542           unsigned k;
543
544           isLast = YES;
545           for (k = (i + 1); k < cnt; k++) {
546             obj = [_list objectAtIndex:k];
547             
548             [_element setIndex:k];
549             [_element setItem:obj];
550             if ([self->index isValueSettable])
551               [self->index setUnsignedIntValue:k inComponent:comp];
552             if ([self->item isValueSettable])
553               [self->item setValue:obj inComponent:comp];
554             if ([self->currentPath isValueSettable])
555               [self->currentPath setValue:[_element currentPath]
556                    inComponent:comp];
557             
558             if ([self->showItem boolValueInComponent:comp]) {
559               isLast = NO;
560               break;
561             }
562           }
563           [_element setIndex:i];
564           [_element setItem:object];
565
566           if ([self->index isValueSettable])
567             [self->index setUnsignedIntValue:i inComponent:comp];
568           if ([self->item isValueSettable])
569             [self->item setValue:object inComponent:comp];
570           if ([self->currentPath isValueSettable])
571             [self->currentPath setValue:[_element currentPath]
572                    inComponent:comp];
573         }
574       }
575       else
576         isLast = (i == (cnt-1));
577         
578       if (!isLeaf) { // not a leaf
579         _WETreeMatrixElement *newElement;
580         
581         if (self->zoom == nil) {
582           [_element setElement:(isLast)
583                     ? WETreeView_Corner
584                     : WETreeView_Junction];
585         }
586         else {
587           isZoom = [self->zoom boolValueInComponent:comp];
588           if (isZoom) {
589             [_element setElement:(isLast)
590                  ? ([sl count]) ? WETreeView_CornerMinus : WETreeView_Corner
591                  : ([sl count]) ? WETreeView_Minus : WETreeView_Junction];
592           }
593           else
594             [_element setElement:(isLast)
595                       ? WETreeView_CornerPlus
596                       : WETreeView_Plus];
597         }
598         [_element setLeaf:(isZoom && [sl count])
599                   ? WETreeView_CornerLeaf
600                   : WETreeView_Leaf];
601
602         newElement = [[_WETreeMatrixElement alloc] initWithElement:_element];
603
604         [self->matrix addObject:newElement];
605         [newElement release]; newElement = nil;
606         
607         if (isZoom) {
608           [_element setElement:(isLast)
609                       ? WETreeView_Space
610                       : WETreeView_Line];
611           
612           newElement = [[_WETreeMatrixElement alloc] initWithElement:_element];
613
614           [self appendList:sl treeElement:newElement inContext:_ctx];
615           [newElement release]; newElement = nil;
616         }
617       }
618       else {
619         _WETreeMatrixElement *newElement;
620
621         [_element setElement: (isLast)
622                   ? WETreeView_Corner
623                   : WETreeView_Junction];
624
625         newElement = [[_WETreeMatrixElement alloc] initWithElement:_element];
626
627         [newElement setLeaf:WETreeView_Leaf];
628
629         [self->matrix addObject:newElement];
630         [newElement release]; newElement = nil;
631       }
632     }
633   }
634 }
635
636 - (void)_calcMatrixInContext:(WOContext *)_ctx depth:(int *)_depth {
637   _WETreeMatrixElement *treeElm = nil;
638   NSArray              *top     = nil;
639   int i, cnt, d, maxDepth = 0;
640
641   top = [self->list valueInComponent:[_ctx component]];
642   
643   [self->matrix release]; self->matrix = nil;
644   self->matrix = [[NSMutableArray allocWithZone:[self zone]]
645                                  initWithCapacity:64];
646
647   treeElm = [[_WETreeMatrixElement alloc] init];
648   
649   [self appendList:top treeElement:treeElm inContext:_ctx];
650   
651   [treeElm release]; treeElm = nil;
652
653   cnt = [self->matrix count];
654
655   /* calc max depth */
656   for (i = 0; i < cnt; i++) {
657     d = [[self->matrix objectAtIndex:i] depth];
658     maxDepth = (maxDepth < d) ? d : maxDepth;
659   }
660
661   /* update depth */
662   for (i = 0; i < cnt; i++) {
663     _WETreeMatrixElement *element;
664
665     element = [self->matrix objectAtIndex:i];
666     [element setColspan:maxDepth-[element depth]+1];
667   }
668   *_depth = maxDepth + 2;
669 }
670
671 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
672   WOComponent *comp;
673   BOOL        doTable;
674   int         i, cnt, depth;
675
676   [self updateConfigInContext:_ctx];
677
678   comp = [_ctx component];
679
680   /* check for browser */
681   if (self->noTable == nil) {
682     WEClientCapabilities *ccaps;
683     
684     ccaps = [[_ctx request] clientCapabilities];
685     doTable = [ccaps isFastTableBrowser];
686   }
687   else
688     doTable = ![self->noTable boolValueInComponent:comp];
689
690   if (!doTable)
691     [_ctx setObject:@"1" forKey:WETreeView_RenderNoTable];
692
693   if (doTable) {
694     [_response appendContentString:
695                @"<table border='0' cellspacing='0' cellpadding='0'"];
696
697     [self appendExtraAttributesToResponse:_response inContext:_ctx];
698     if (self->otherTagString) {
699       [_response appendContentCharacter:' '];
700       [_response appendContentString:
701                  [self->otherTagString stringValueInComponent:
702                       [_ctx component]]];
703     }
704     [_response appendContentCharacter:'>'];
705   }
706
707   [self _calcMatrixInContext:_ctx depth:&depth];
708
709     /* append table title */
710   if (doTable)
711     [_response appendContentString:@"<tr>"];
712
713   [_ctx setObject:[NSNumber numberWithInt:depth]
714         forKey:WETreeView_HEADER_MODE];
715   [_ctx appendElementIDComponent:@"h"];
716   [self->template appendToResponse:_response inContext:_ctx];
717   [_ctx deleteLastElementIDComponent];
718   [_ctx removeObjectForKey:WETreeView_HEADER_MODE];
719   if (doTable)
720     [_response appendContentString:@"</tr>"];
721   else if (_ctx->wcFlags.xmlStyleEmptyElements)
722     [_response appendContentString:@"<br />"];
723   else
724     [_response appendContentString:@"<br>"];
725
726   cnt = [self->matrix count];
727
728   for (i = 0; i < cnt; i++) {
729     _WETreeMatrixElement *element;
730
731     element = [self->matrix objectAtIndex:i];
732       
733     if ([self->index isValueSettable])
734       [self->index setUnsignedIntValue:[element index]
735            inComponent:comp];
736
737     if ([self->item isValueSettable])
738       [self->item setValue:[element item]
739            inComponent:comp];
740
741     if ([self->currentPath isValueSettable])
742       [self->currentPath setValue:[element currentPath]
743            inComponent:comp];
744
745     [_ctx setObject:element forKey:WETreeView_TreeElement];
746
747     [_ctx appendElementIDComponent:[element elementID]];
748       
749     [_ctx appendElementIDComponent:@"end"];
750     if (doTable)
751       [_response appendContentString:@"<tr>"];
752     [self->template appendToResponse:_response inContext:_ctx];
753     if (doTable)
754       [_response appendContentString:@"</tr>"];
755     else if (_ctx->wcFlags.xmlStyleEmptyElements)
756       [_response appendContentString:@"<br />"];
757     else
758       [_response appendContentString:@"<br>"];
759       
760     [_ctx deleteLastElementIDComponent]; // delete "end"
761       
762     [_ctx deleteLastElementIDComponent]; // delete eids
763   }
764   [_ctx removeObjectForKey:WETreeView_TreeElement];
765
766   if (doTable)
767     [_response appendContentString:@"</table>"];
768
769   [self removeConfigInContext:_ctx];
770   [_ctx removeObjectForKey:WETreeView_RenderNoTable];
771   [self->matrix release]; self->matrix = nil;
772 }
773
774 @end /* WETreeView */