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