]> err.no Git - sope/blob - sope-appserver/WEExtensions/WETableMatrix.m
milli => micro
[sope] / sope-appserver / WEExtensions / WETableMatrix.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 <NGObjWeb/WODynamicElement.h>
24 #include <NGObjWeb/WOAssociation.h>
25 #include <NGObjWeb/WOContext.h>
26 #include <NGObjWeb/WOResponse.h>
27 #include "WETableCalcMatrix.h"
28 #include "common.h"
29
30 /*
31   for examples of this element, look at SkySchedulerViews chart-views.
32 */
33
34 /* context keys */
35 static NSString *WETableMatrix_Query   = @"WETableMatrix_Query";
36 static NSString *WETableMatrix_Mode    = @"WETableMatrix_Mode";
37 static NSString *WETableMatrix_Index   = @"WETableMatrix_Index";
38 static NSString *WETableMatrix_Count   = @"WETableMatrix_Count";
39 static NSString *WETableMatrix_ColSpan = @"WETableMatrix_ColSpan";
40 static NSString *WETableMatrix_RowSpan = @"WETableMatrix_RowSpan";
41
42 @interface WETableMatrix : WODynamicElement
43 {
44   WOAssociation *list;    /* array of objects */
45   WOAssociation *item;    /* current object   */
46   
47   WOAssociation *rows;    /* array of row objects    (eg days)         */
48   WOAssociation *columns; /* array of column objects (eg times of day) */
49   WOAssociation *row;     /* current row object    */
50   WOAssociation *column;  /* current column object */
51   
52   WOAssociation *itemActive; /* query whether item is active in row/column */
53   WOAssociation *isRowActive;
54   WOAssociation *isColumnActive;
55
56   WOElement     *template;
57
58   /* transient, not reentrant (need to lock) */
59   WOComponent   *component;
60   NSArray       *_rows;
61   NSArray       *_cols;
62   NSArray       *objs;
63   NSSet         *subElems;
64 }
65
66 @end
67
68 @interface WEHSpanTableMatrix : WETableMatrix
69 @end
70
71 @interface WEVSpanTableMatrix : WETableMatrix
72 {
73   WOAssociation *rowHeight;          /* height of TR tag */
74   WOAssociation *noSpanInEmptyCells; /* do empty cells span rows ? */
75
76 @end
77
78 static BOOL genComments = NO;
79
80 @implementation WETableMatrix
81
82 // premature: don't know a "good" count
83 static NSNumber *smap[10] = { nil,nil,nil,nil,nil,nil,nil,nil,nil,nil };
84 static Class StrClass = Nil;
85 static Class NumClass = Nil;
86
87 + (void)initialize {
88   static BOOL didInit = NO;
89   int i;
90   if (didInit) return;
91   didInit = YES;
92   
93   StrClass = [NSString class];
94   NumClass = [NSNumber class];
95   for (i = 0; i < 10; i++)
96     smap[i] = [[NumClass numberWithUnsignedInt:i] retain];
97 }
98
99 static NSNumber *numForUInt(unsigned int i) {
100   // TODO: prof
101   if (i < 10) return smap[i];
102   return [NumClass numberWithUnsignedInt:i];
103 }
104
105 - (id)initWithName:(NSString *)_name
106   associations:(NSDictionary *)_config
107   template:(WOElement *)_subs
108 {
109   if ((self = [super initWithName:_name associations:_config template:_subs])) {
110     self->list           = WOExtGetProperty(_config, @"list");
111     self->item           = WOExtGetProperty(_config, @"item");
112     self->rows           = WOExtGetProperty(_config, @"rows");
113     self->columns        = WOExtGetProperty(_config, @"columns");
114     self->row            = WOExtGetProperty(_config, @"row");
115     self->column         = WOExtGetProperty(_config, @"column");
116     self->itemActive     = WOExtGetProperty(_config, @"itemActive");
117     self->isRowActive    = WOExtGetProperty(_config, @"isRowActive");
118     self->isColumnActive = WOExtGetProperty(_config, @"isColumnActive");
119     
120     self->template = [_subs retain];
121   }
122   return self;
123 }
124
125 - (void)dealloc {
126   [self->isRowActive    release];
127   [self->isColumnActive release];
128   [self->itemActive release];
129   [self->row        release];
130   [self->column   release];
131   [self->rows     release];
132   [self->columns  release];
133   [self->list     release];
134   [self->item     release];
135   [self->template release];
136   [super dealloc];
137 }
138
139 /* matrix delegate */
140
141 - (BOOL)tableCalcMatrix:(WETableCalcMatrix *)_matrix
142   shouldPlaceObject:(id)_object
143   atPosition:(unsigned)_x:(unsigned)_y
144 {
145   id   _row, _col;
146   BOOL doPlace;
147   
148   _col = [self->_cols objectAtIndex:_x];
149   _row = [self->_rows objectAtIndex:_y];
150   
151   /* setup context in component */
152   [self->row    setValue:_row    inComponent:self->component];
153   [self->column setValue:_col    inComponent:self->component];
154   [self->item   setValue:_object inComponent:self->component];
155   
156 #if 0
157   NSLog(@"%i/%i: col %@ row %@", _x,_y, _col, _row);
158 #endif
159   
160   /* check */
161   doPlace = [self->itemActive boolValueInComponent:self->component];
162 #if 0
163   NSLog(@"  %@ placed: %s", self->itemActive, doPlace ? "yes" : "no");
164 #endif
165   
166   return doPlace;
167 }
168
169 - (BOOL)tableCalcMatrix:(WETableCalcMatrix *)_matrix
170   shouldProcessColumn:(unsigned)_x
171   forObject:(id)_object
172 {
173   if (!self->isColumnActive)
174     return YES;
175   
176   [self->column setValue:[self->_cols objectAtIndex:_x]
177                 inComponent:self->component];
178   [self->item setValue:_object inComponent:self->component];
179     
180   return [self->isColumnActive boolValueInComponent:self->component];
181 }
182 - (BOOL)tableCalcMatrix:(WETableCalcMatrix *)_matrix
183   shouldProcessRow:(unsigned)_y
184   forObject:(id)_object
185 {
186   if (!self->isRowActive)
187     return YES;
188   
189   [self->row setValue:[self->_rows objectAtIndex:_y]
190              inComponent:self->component];
191   [self->item setValue:_object inComponent:self->component];
192
193   return [self->isRowActive boolValueInComponent:self->component];
194 }
195
196 /* HTML generation */
197
198 - (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
199   [self logWithFormat:@"ERROR: subclasses should override %@!",
200           NSStringFromSelector(_cmd)];
201   return nil;
202 }
203 - (void)appendSpans:(NSArray *)_spans
204   toResponse:(WOResponse *)_response
205   inContext:(WOContext *)_ctx
206 {
207   [self logWithFormat:@"ERROR: subclasses should override %@!",
208           NSStringFromSelector(_cmd)];
209 }
210
211 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
212   NSLog(@"WARNING(WETableMatrix): unsupported invokeActionForRequest called!");
213   return [super invokeActionForRequest:_req inContext:_ctx];
214 }
215
216
217 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
218   NSAutoreleasePool  *pool;
219   WETableCalcMatrix *matrix;
220   NSArray            *allSpans;
221   unsigned           rowCount, colCount, count;
222   
223   self->component = [_ctx component];
224   self->objs      = 
225     [[[self->list    valueInComponent:self->component] copy] autorelease];
226   self->_rows     = 
227     [[[self->rows    valueInComponent:self->component] copy] autorelease];
228   self->_cols     = 
229     [[[self->columns valueInComponent:self->component] copy] autorelease];
230   
231   count    = [self->objs count];
232   rowCount = [self->_rows count];
233   colCount = [self->_cols count];
234   
235   /* query subelements */
236   {
237     NSMutableSet *qs;
238
239     qs = [[NSMutableSet alloc] init];
240     [_ctx setObject:qs forKey:WETableMatrix_Query];
241     [self->template appendToResponse:_response inContext:_ctx];
242     [_ctx removeObjectForKey:WETableMatrix_Query];
243     self->subElems = [[qs copy] autorelease];
244     [qs release];
245   }
246   
247   if ([self->subElems count] == 0) {
248     /* no content */
249     NSLog(@"WARNING: no sub-elements !");
250     return;
251   }
252
253 #if 0
254   NSLog(@"subelems: %@", [self->subElems allObjects]);
255 #endif
256   
257   /* fill matrix and calculate spans */
258   
259   pool = [[NSAutoreleasePool alloc] init];
260   {
261     matrix = [[WETableCalcMatrix alloc] initWithSize:colCount:rowCount];
262     [matrix setDelegate:self];
263     [matrix placeObjects:objs];
264     allSpans = [[self spansFromMatrix:matrix] copy];
265     [matrix release]; matrix = nil;
266   }
267   [pool release]; pool = nil;
268   [allSpans autorelease];
269
270   /* generate vertical table */
271   
272   pool = [[NSAutoreleasePool alloc] init];
273
274   [_response appendContentString:@"<table"];
275   [self appendExtraAttributesToResponse:_response inContext:_ctx];
276   [_response appendContentString:@">"];
277   
278   [self appendSpans:allSpans
279         toResponse:_response
280         inContext:_ctx];
281
282   [_response appendContentString:@"</table>"];
283
284   /* remove context keys */
285   [_ctx removeObjectForKey:WETableMatrix_Mode];
286   [_ctx removeObjectForKey:WETableMatrix_Index];
287   [_ctx removeObjectForKey:WETableMatrix_Count];
288   [_ctx removeObjectForKey:WETableMatrix_ColSpan];
289   [_ctx removeObjectForKey:WETableMatrix_RowSpan];
290   
291   /* reset transients */
292   self->subElems  = nil;
293   self->_rows     = nil;
294   self->_cols     = nil;
295   self->objs      = nil;
296   self->component = nil;
297   [pool release];
298 }
299
300 @end /* WETableMatrix */
301
302 @implementation WEVSpanTableMatrix
303
304 - (id)initWithName:(NSString *)_name
305   associations:(NSDictionary *)_config
306   template:(WOElement *)_subs
307 {
308   if ((self = [super initWithName:_name associations:_config template:_subs])) {
309     self->rowHeight          = WOExtGetProperty(_config, @"rowHeight");
310     self->noSpanInEmptyCells = WOExtGetProperty(_config, @"noSpanInEmptyCells");
311   }
312   return self;
313 }
314
315 - (void)dealloc {
316   [self->noSpanInEmptyCells release];
317   [self->rowHeight release];
318   [super dealloc];
319 }
320
321 - (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
322   return [_matrix columnSpans];
323 }
324
325 - (void)_genEmptyCellWithRowSpan:(int)_span
326   toResponse:(WOResponse *)_response
327   inContext:(WOContext *)_ctx
328 {
329   NSString *s;
330   
331   if (_span > 1) {
332     char buf[16];
333     sprintf(buf, "%i", _span);
334     s = [[StrClass alloc] initWithCString:buf];
335   }
336   else
337     s = @"1";
338   
339   if ([self->subElems containsObject:@"empty"]) {
340     if (_span > 0)
341       [_ctx setObject:s forKey:WETableMatrix_RowSpan];
342     
343     [_ctx setObject:@"empty" forKey:WETableMatrix_Mode];
344     
345     [self->template appendToResponse:_response inContext:_ctx];
346     
347     if (_span > 0)
348       [_ctx removeObjectForKey:WETableMatrix_RowSpan];
349   }
350   else {
351     [_response appendContentString:@"<td"];
352     if (_span > 1) {
353       [_response appendContentString:@" rowspan=\""];
354       [_response appendContentString:s];
355       [_response appendContentString:@"\""];
356     }
357     [_response appendContentString:@">"];
358
359     [_response appendContentString:@"&nbsp;"];
360     [_response appendContentString:@"</td>\n"];
361   }
362   
363   [s release];
364 }
365
366 - (void)appendSpans:(NSArray *)_spans
367   toResponse:(WOResponse *)_response
368   inContext:(WOContext *)_ctx
369 {
370   WOComponent *sComponent;
371 #if 0 // hh: unused
372   NSString *horizWidth;
373 #endif
374   unsigned columnCount, rowCount;
375   BOOL noEmptySpan; // empty cells do not have ROWSPAN (more cells are written)
376   
377   sComponent  = [_ctx component];
378   columnCount = [self->_cols count];
379   rowCount    = [self->_rows count];
380 #if 0 // hh: unused
381   horizWidth  =
382     [StrClass stringWithFormat:@"%d%%", (unsigned)(100.0/columnCount)];
383 #endif
384
385   noEmptySpan = [self->noSpanInEmptyCells boolValueInComponent:sComponent];
386   
387   [_ctx setObject:numForUInt(columnCount) forKey:WETableMatrix_Count];
388
389   /* head row */
390   if ([self->subElems containsObject:@"top"]) {
391     unsigned x;
392     int numOfLeftLabels;
393
394     numOfLeftLabels = 0;
395     if ([self->subElems containsObject:@"left"])
396       numOfLeftLabels++;
397     
398     [_response appendContentString:@"<tr>\n"];
399
400     /* left/top edge */
401     if (numOfLeftLabels > 0) {
402       char buf[8];
403       NSString *s;
404       
405       sprintf(buf, "%i", numOfLeftLabels);
406       s = [[StrClass alloc] initWithCString:buf];
407       
408       if ([self->subElems containsObject:@"topleft"]) {
409         [_ctx setObject:s          forKey:WETableMatrix_ColSpan];
410         [_ctx setObject:@"topleft" forKey:WETableMatrix_Mode];
411         
412         [self->template appendToResponse:_response inContext:_ctx];
413         
414         [_ctx removeObjectForKey:WETableMatrix_ColSpan];
415       }
416       else {
417         [_response appendContentString:@"  <td"];
418         if (numOfLeftLabels > 1) {
419           [_response appendContentString:@" colspan=\""];
420           [_response appendContentString:s];
421           [_response appendContentString:@"\""];
422         }
423         [_response appendContentString:@">&nbsp;</td>\n"];
424       }
425       [s release];
426     }
427     
428     /* header */
429     [_ctx setObject:@"top" forKey:WETableMatrix_Mode];
430     
431     for (x = 0; x < columnCount; x++) {
432       NSArray  *spans;
433       char     buf[64];
434       NSString *s;
435       
436       spans = [_spans objectAtIndex:x];
437       
438       sprintf(buf, "%d", [spans count]);
439       s = [[StrClass alloc] initWithCString:buf];
440       [_ctx setObject:s forKey:WETableMatrix_ColSpan];
441       
442       [self->column setValue:[self->_cols objectAtIndex:x]
443                     inComponent:sComponent];
444       [_ctx setObject:numForUInt(x) forKey:WETableMatrix_Index];
445       
446       [self->template appendToResponse:_response inContext:_ctx];
447       
448       [s release]; s = nil;
449     }
450     
451     [_response appendContentString:@"</tr>"];
452   }
453
454   [_ctx removeObjectForKey:WETableMatrix_ColSpan];
455   [_ctx removeObjectForKey:WETableMatrix_RowSpan];
456   [_ctx removeObjectForKey:WETableMatrix_Index];
457   
458   /* body rows */
459   {
460     unsigned y;
461     
462     /* foreach row */
463     for (y = 0; y < rowCount; y++) {
464       unsigned x;
465
466       [self->row setValue:[self->_rows objectAtIndex:y]
467                  inComponent:[_ctx component]];
468
469       if (self->rowHeight) {
470         [_response appendContentString:@"<tr height=\""];
471         [_response appendContentString:
472                      [self->rowHeight stringValueInComponent:sComponent]];
473         [_response appendContentString:@"\">"];
474       }
475       else {
476         [_response appendContentString:@"<tr>"];
477       }
478       if (genComments) {
479         NSString *s;
480         s = [[StrClass alloc] initWithFormat:@"<!-- row %i -->\n", y];
481         [_response appendContentString:s];
482         [s release];
483       }
484       
485       /* left edge */
486       {
487         char     buf[64];
488         NSString *s;
489         
490         [_ctx setObject:@"left" forKey:WETableMatrix_Mode];
491
492         sprintf(buf, "%i", y);
493         s = [[StrClass alloc] initWithCString:buf];
494         
495         [_ctx setObject:numForUInt(y) forKey:WETableMatrix_Index];
496         
497         [self->template appendToResponse:_response inContext:_ctx];
498         [s release];
499       }
500       
501       /* foreach column */
502       for (x = 0; x < columnCount; x++) {
503         NSArray  *spans;
504         unsigned i;
505
506         spans = [_spans objectAtIndex:x];
507         
508         if ([spans count] == 0) { /* no content cells */
509           if ((y == 0) || noEmptySpan) {
510             /* max rowspan, only encode in first row */
511             
512             [self _genEmptyCellWithRowSpan:(noEmptySpan ? 1 : rowCount)
513                   toResponse:_response
514                   inContext:_ctx];
515           }
516           continue;
517         }
518         
519         /* foreach sub-columns (title-COLSPAN=subcell-count) */
520
521         for (i = 0; i < [spans count]; i++) {
522           NSArray *thread; /* sub-column top-down */
523           unsigned j;
524             
525           thread = [spans objectAtIndex:i];
526           NSCAssert([thread count] > 0, @"no contents in thread");
527             
528           for (j = 0; j < [thread count]; j++) {
529             id span;
530               
531             span = [thread objectAtIndex:j];
532             if (![span occupiesIndex:y])
533               continue;
534             
535             if ([span startsAtIndex:y]) {
536               char buf[64];
537               NSString *s;
538               
539               sprintf(buf, "%i", [span size]);
540               s = [[StrClass alloc] initWithCString:buf];
541               [_ctx setObject:s forKey:WETableMatrix_RowSpan];
542               
543               [self->column setValue:[self->_cols objectAtIndex:x]
544                             inComponent:self->component];
545               
546               if ([span object]) {
547                 /* setup context */
548                 [self->item setValue:[span object] inComponent:self->component];
549                 
550                 /* generate body */
551                 [_ctx setObject:@"content" forKey:WETableMatrix_Mode];
552               
553                 [self->template appendToResponse:_response inContext:_ctx];
554               }
555               else {
556                 [self _genEmptyCellWithRowSpan:(noEmptySpan ? 1 : [span size])
557                       toResponse:_response
558                       inContext:_ctx];
559               }
560
561               [_ctx removeObjectForKey:WETableMatrix_RowSpan];
562               [s release]; s = nil;
563             }
564             else if (noEmptySpan) {
565               if ([span object] == nil) {
566                 [self _genEmptyCellWithRowSpan:1
567                       toResponse:_response
568                       inContext:_ctx];
569               }
570             }
571           }
572         } /* end of 'foreach sub-columns (title-COLSPAN=subcell-count)' */
573       } /* end of 'foreach column' */
574       [_response appendContentString:@"</tr>"];
575     }
576   }
577   
578   /* footer row */
579   
580   if ([self->subElems containsObject:@"bottom"]) {
581   }
582 }
583
584 @end /* WEVSpanTableMatrix */
585
586 @implementation WEHSpanTableMatrix
587
588 - (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
589   return [_matrix rowSpans];
590 }
591
592 - (void)appendVerticalSpan:(NSArray *)_threads
593   toResponse:(WOResponse *)_response
594   inContext:(WOContext *)_ctx
595 {
596   unsigned x, width;
597   
598   width = [self->_cols count];
599
600   for (x = 0; x < width; x++) { /* foreach table column */
601     unsigned j, tc;
602           
603     for (j = 0, tc = [_threads count]; j < tc; j++) {
604       id span;
605             
606       span = [_threads objectAtIndex:j];
607       if (![span occupiesIndex:x])
608         continue;
609
610       if ([span startsAtIndex:x]) {
611         NSString *s;
612         char buf[64];
613
614         sprintf(buf, "%d", [span size]);
615         s = [[StrClass alloc] initWithCString:buf];
616         [_ctx setObject:s forKey:WETableMatrix_ColSpan];
617         
618         if ([span object]) {
619           /* setup context */
620           [self->column setValue:[self->_cols objectAtIndex:x]
621                         inComponent:self->component];
622           [self->item setValue:[span object] inComponent:self->component];
623
624           /* generate body */
625           [_ctx setObject:@"content" forKey:WETableMatrix_Mode];
626           [self->template appendToResponse:_response inContext:_ctx];
627         }
628         else {
629           /* generate empty body */
630           if ([self->subElems containsObject:@"empty"]) {
631             [_ctx setObject:@"empty" forKey:WETableMatrix_Mode];
632             [self->template appendToResponse:_response inContext:_ctx];
633           }
634           else {
635             [_response appendContentString:@"<td colspan=\""];
636             [_response appendContentString:s];
637             [_response appendContentString:@"\">"];
638             [_response appendContentString:@"&nbsp;"];
639             [_response appendContentString:@"</td>\n"];
640           }
641         }
642         [s release]; s = nil;
643       }
644     }
645   }
646 }
647
648 - (void)appendSpans:(NSArray *)_spans
649   toResponse:(WOResponse *)_response
650   inContext:(WOContext *)_ctx
651 {
652   WOComponent *sComponent;
653 #if 0 // hh: unused
654   NSString *horizWidth;
655 #endif
656   unsigned columnCount;
657
658   sComponent  = [_ctx component];
659   columnCount = [self->_cols count];
660 #if 0 // hh: unused
661   horizWidth  =
662     [StrClass stringWithFormat:@"%d%%", (unsigned)(100.0/columnCount)];
663 #endif
664   
665   [_ctx setObject:numForUInt(columnCount) forKey:WETableMatrix_Count];
666   
667   /* head row */
668
669   if ([self->subElems containsObject:@"top"]) {
670     unsigned i, count;
671     
672     [_response appendContentString:@"<tr>"];
673
674     /* edge */
675     if ([self->subElems containsObject:@"left"])
676       [_response appendContentString:@"<td>&nbsp;</td>"];
677     
678     /* header */
679     [_ctx setObject:@"top" forKey:WETableMatrix_Mode];
680     count = [self->_cols count];
681     
682     for (i = 0; i < count; i++) {
683       [_ctx setObject:numForUInt(i) forKey:WETableMatrix_Index];
684       
685       [self->column setValue:[self->_cols objectAtIndex:i]
686                     inComponent:sComponent];
687       
688       [self->template appendToResponse:_response inContext:_ctx];
689     }
690     [_response appendContentString:@"</tr>"];
691   }
692   
693   /* body rows */
694   {
695     unsigned y, width, height;
696
697     width  = [self->_cols count];
698     height = [self->_rows count];
699     
700     for (y = 0; y < height; y++) { /* foreach row */
701       NSArray  *rowSpans;
702       unsigned i, count;
703       
704       /* apply context */
705       [self->row setValue:[self->_rows objectAtIndex:y]
706                  inComponent:self->component];
707
708       /* get span */
709       rowSpans = [_spans objectAtIndex:y];
710       count    = [rowSpans count];
711
712       /* begin first row */
713       [_response appendContentString:@"<tr>"];
714
715       /* left edge */
716       {
717         char     buf[64];
718         NSString *s;
719       
720         sprintf(buf, "%d", count);
721         s = [[StrClass alloc] initWithCString:buf];
722         [_ctx setObject:s forKey:WETableMatrix_RowSpan];
723         
724         [_ctx setObject:@"left" forKey:WETableMatrix_Mode];
725         
726         [_ctx setObject:numForUInt(y) forKey:WETableMatrix_Index];        
727         [self->template appendToResponse:_response inContext:_ctx];
728         [s release]; s = nil;
729         [_ctx removeObjectForKey:WETableMatrix_RowSpan];
730       }
731 #if 0 /* hh: why is this commented out ? */
732       /* left label ? */
733       [_response appendContentString:@"<td"];
734       if (count > 1) {
735         NSString *s;
736
737         [_response appendContentString:@" rowspan=\""];
738         s = [[StrClass alloc] initWithFormat:@"%d", count];
739         [_response appendContentString:s];
740         [s release];
741         [_response appendContentString:@"\""];
742       }
743       [_response appendContentString:@">"];
744       [genLabel];
745       [_response appendContentString:@"</td>"];
746 #endif
747       
748       /* check for empty span */
749       if (count == 0) {
750         NSString *s;
751
752         s = [[StrClass alloc] initWithFormat:@"%d", width];
753         
754         /* max rowspan, only encode in first row */
755         [_ctx setObject:@"empty" forKey:WETableMatrix_Mode];
756         [_ctx setObject:s forKey:WETableMatrix_ColSpan];
757         [self->template appendToResponse:_response inContext:_ctx];
758         [_ctx removeObjectForKey:WETableMatrix_ColSpan];
759 #if 0 /* hh: why is this commented out ? */
760         
761         [_response appendContentString:@"<td colspan=\""];
762         [_response appendContentString:s];
763         [_response appendContentString:@"\">&nbsp;</td>"];
764         
765         /* close completly filled row */
766         [_response appendContentString:@"</tr>"];
767 #endif
768         [s release];
769         continue;
770       }
771       
772       /* first span (same row like vertical label) */
773       if (count > 0) {
774         NSArray *thread;
775         
776         thread = [rowSpans objectAtIndex:0];
777
778         [self appendVerticalSpan:thread
779               toResponse:_response
780               inContext:_ctx];
781       }
782       /* close first row */
783       [_response appendContentString:@"</tr>"];
784
785       for (i = 1; i < count; i++) { /* foreach additional span */
786         NSArray *thread;
787         
788         thread = [rowSpans objectAtIndex:i];
789
790         [_response appendContentString:@"<tr>"];
791         
792         [self appendVerticalSpan:thread
793               toResponse:_response
794               inContext:_ctx];
795         
796         [_response appendContentString:@"</tr>"];
797       }
798       [_ctx removeObjectForKey:WETableMatrix_ColSpan];
799     }
800   }
801   
802   /* footer row */
803   
804   if ([self->subElems containsObject:@"bottom"]) {
805     unsigned i, count;
806     
807     [_response appendContentString:@"<tr>"];
808     
809     /* edge */
810     if ([self->subElems containsObject:@"left"])
811       [_response appendContentString:@"<td>&nbsp;</td>"];
812     
813     /* footer */
814     
815     [_ctx setObject:@"bottom" forKey:WETableMatrix_Mode];
816     
817     count = [self->_cols count];
818     
819     for (i = 0; i < count; i++) {
820       [_ctx setObject:numForUInt(i) forKey:WETableMatrix_Index];
821       
822       [self->column setValue:[self->_cols objectAtIndex:i]
823                     inComponent:sComponent];
824       
825       [self->template appendToResponse:_response inContext:_ctx];
826     }
827     [_response appendContentString:@"</tr>"];
828   }
829 }
830
831 @end /* WEVSpanTableMatrix */