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 */
34static NSString *WETableMatrix_Query   = @"WETableMatrix_Query";
35static NSString *WETableMatrix_Mode    = @"WETableMatrix_Mode";
36static NSString *WETableMatrix_Index   = @"WETableMatrix_Index";
37static NSString *WETableMatrix_Count   = @"WETableMatrix_Count";
38static NSString *WETableMatrix_ColSpan = @"WETableMatrix_ColSpan";
39static 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
77static BOOL genComments = NO;
78
79@implementation WETableMatrix
80
81// premature: don't know a "good" count
82static NSNumber *smap[10] = { nil,nil,nil,nil,nil,nil,nil,nil,nil,nil };
83static Class StrClass = Nil;
84static 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
98static 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;
221
222  if ([_ctx isRenderingDisabled]) {
223    [self->template appendToResponse:_response inContext:_ctx];
224    return;
225  }
226
227  self->component = [_ctx component];
228  self->objs      =
229    [[[self->list    valueInComponent:self->component] copy] autorelease];
230  self->_rows     =
231    [[[self->rows    valueInComponent:self->component] copy] autorelease];
232  self->_cols     =
233    [[[self->columns valueInComponent:self->component] copy] autorelease];
234
235  rowCount = [self->_rows count];
236  colCount = [self->_cols count];
237
238  /* query subelements */
239  {
240    NSMutableSet *qs;
241
242    qs = [[NSMutableSet alloc] init];
243    [_ctx setObject:qs forKey:WETableMatrix_Query];
244    [self->template appendToResponse:_response inContext:_ctx];
245    [_ctx removeObjectForKey:WETableMatrix_Query];
246    self->subElems = [[qs copy] autorelease];
247    [qs release];
248  }
249
250  if ([self->subElems count] == 0) {
251    /* no content */
252    [self warnWithFormat:@"no sub-elements !"];
253    return;
254  }
255
256#if 0
257  NSLog(@"subelems: %@", [self->subElems allObjects]);
258#endif
259
260  /* fill matrix and calculate spans */
261
262  pool = [[NSAutoreleasePool alloc] init];
263  {
264    matrix = [[WETableCalcMatrix alloc] initWithSize:colCount:rowCount];
265    [matrix setDelegate:self];
266    [matrix placeObjects:objs];
267    allSpans = [[self spansFromMatrix:matrix] copy];
268    [matrix release]; matrix = nil;
269  }
270  [pool release]; pool = nil;
271  [allSpans autorelease];
272
273  /* generate vertical table */
274
275  pool = [[NSAutoreleasePool alloc] init];
276
277  [_response appendContentString:@"<table"];
278  [self appendExtraAttributesToResponse:_response inContext:_ctx];
279  [_response appendContentString:@">"];
280
281  [self appendSpans:allSpans
282        toResponse:_response
283        inContext:_ctx];
284
285  [_response appendContentString:@"</table>"];
286
287  /* remove context keys */
288  [_ctx removeObjectForKey:WETableMatrix_Mode];
289  [_ctx removeObjectForKey:WETableMatrix_Index];
290  [_ctx removeObjectForKey:WETableMatrix_Count];
291  [_ctx removeObjectForKey:WETableMatrix_ColSpan];
292  [_ctx removeObjectForKey:WETableMatrix_RowSpan];
293
294  /* reset transients */
295  self->subElems  = nil;
296  self->_rows     = nil;
297  self->_cols     = nil;
298  self->objs      = nil;
299  self->component = nil;
300  [pool release];
301}
302
303@end /* WETableMatrix */
304
305@implementation WEVSpanTableMatrix
306
307- (id)initWithName:(NSString *)_name
308  associations:(NSDictionary *)_config
309  template:(WOElement *)_subs
310{
311  if ((self = [super initWithName:_name associations:_config template:_subs])) {
312    self->rowHeight          = WOExtGetProperty(_config, @"rowHeight");
313    self->noSpanInEmptyCells = WOExtGetProperty(_config, @"noSpanInEmptyCells");
314  }
315  return self;
316}
317
318- (void)dealloc {
319  [self->noSpanInEmptyCells release];
320  [self->rowHeight release];
321  [super dealloc];
322}
323
324- (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
325  return [_matrix columnSpans];
326}
327
328- (void)_genEmptyCellWithRowSpan:(int)_span
329  toResponse:(WOResponse *)_response
330  inContext:(WOContext *)_ctx
331{
332  NSString *s;
333
334  if (_span > 1) {
335    char buf[16];
336    sprintf(buf, "%i", _span);
337    s = [[StrClass alloc] initWithCString:buf];
338  }
339  else
340    s = @"1";
341
342  if ([self->subElems containsObject:@"empty"]) {
343    if (_span > 0)
344      [_ctx setObject:s forKey:WETableMatrix_RowSpan];
345
346    [_ctx setObject:@"empty" forKey:WETableMatrix_Mode];
347
348    [self->template appendToResponse:_response inContext:_ctx];
349
350    if (_span > 0)
351      [_ctx removeObjectForKey:WETableMatrix_RowSpan];
352  }
353  else {
354    [_response appendContentString:@"<td"];
355    if (_span > 1) {
356      [_response appendContentString:@" rowspan=\""];
357      [_response appendContentString:s];
358      [_response appendContentString:@"\""];
359    }
360    [_response appendContentString:@">"];
361
362    [_response appendContentString:@"&nbsp;"];
363    [_response appendContentString:@"</td>\n"];
364  }
365
366  [s release];
367}
368
369- (void)appendSpans:(NSArray *)_spans
370  toResponse:(WOResponse *)_response
371  inContext:(WOContext *)_ctx
372{
373  WOComponent *sComponent;
374#if 0 // hh: unused
375  NSString *horizWidth;
376#endif
377  unsigned columnCount, rowCount;
378  BOOL noEmptySpan; // empty cells do not have ROWSPAN (more cells are written)
379
380  sComponent  = [_ctx component];
381  columnCount = [self->_cols count];
382  rowCount    = [self->_rows count];
383#if 0 // hh: unused
384  horizWidth  =
385    [StrClass stringWithFormat:@"%d%%", (unsigned)(100.0/columnCount)];
386#endif
387
388  noEmptySpan = [self->noSpanInEmptyCells boolValueInComponent:sComponent];
389
390  [_ctx setObject:numForUInt(columnCount) forKey:WETableMatrix_Count];
391
392  /* head row */
393  if ([self->subElems containsObject:@"top"]) {
394    unsigned x;
395    int numOfLeftLabels;
396
397    numOfLeftLabels = 0;
398    if ([self->subElems containsObject:@"left"])
399      numOfLeftLabels++;
400
401    [_response appendContentString:@"<tr>\n"];
402
403    /* left/top edge */
404    if (numOfLeftLabels > 0) {
405      char buf[8];
406      NSString *s;
407
408      sprintf(buf, "%i", numOfLeftLabels);
409      s = [[StrClass alloc] initWithCString:buf];
410
411      if ([self->subElems containsObject:@"topleft"]) {
412        [_ctx setObject:s          forKey:WETableMatrix_ColSpan];
413        [_ctx setObject:@"topleft" forKey:WETableMatrix_Mode];
414
415        [self->template appendToResponse:_response inContext:_ctx];
416
417        [_ctx removeObjectForKey:WETableMatrix_ColSpan];
418      }
419      else {
420        [_response appendContentString:@"  <td"];
421        if (numOfLeftLabels > 1) {
422          [_response appendContentString:@" colspan=\""];
423          [_response appendContentString:s];
424          [_response appendContentString:@"\""];
425        }
426        [_response appendContentString:@">&nbsp;</td>\n"];
427      }
428      [s release];
429    }
430
431    /* header */
432    [_ctx setObject:@"top" forKey:WETableMatrix_Mode];
433
434    for (x = 0; x < columnCount; x++) {
435      NSArray  *spans;
436      char     buf[64];
437      NSString *s;
438
439      spans = [_spans objectAtIndex:x];
440
441#if GS_64BIT_OLD
442      sprintf(buf, "%ld", [spans count]);
443#else
444      sprintf(buf, "%ld", [spans count]);
445#endif
446      s = [[StrClass alloc] initWithCString:buf];
447      [_ctx setObject:s forKey:WETableMatrix_ColSpan];
448
449      [self->column setValue:[self->_cols objectAtIndex:x]
450                    inComponent:sComponent];
451      [_ctx setObject:numForUInt(x) forKey:WETableMatrix_Index];
452
453      [self->template appendToResponse:_response inContext:_ctx];
454
455      [s release]; s = nil;
456    }
457
458    [_response appendContentString:@"</tr>"];
459  }
460
461  [_ctx removeObjectForKey:WETableMatrix_ColSpan];
462  [_ctx removeObjectForKey:WETableMatrix_RowSpan];
463  [_ctx removeObjectForKey:WETableMatrix_Index];
464
465  /* body rows */
466  {
467    unsigned y;
468
469    /* foreach row */
470    for (y = 0; y < rowCount; y++) {
471      unsigned x;
472
473      [self->row setValue:[self->_rows objectAtIndex:y]
474                 inComponent:[_ctx component]];
475
476      if (self->rowHeight) {
477        [_response appendContentString:@"<tr height=\""];
478        [_response appendContentString:
479                     [self->rowHeight stringValueInComponent:sComponent]];
480        [_response appendContentString:@"\">"];
481      }
482      else {
483        [_response appendContentString:@"<tr>"];
484      }
485      if (genComments) {
486        NSString *s;
487        s = [[StrClass alloc] initWithFormat:@"<!-- row %i -->\n", y];
488        [_response appendContentString:s];
489        [s release];
490      }
491
492      /* left edge */
493      {
494        char     buf[64];
495        NSString *s;
496
497        [_ctx setObject:@"left" forKey:WETableMatrix_Mode];
498
499        sprintf(buf, "%i", y);
500        s = [[StrClass alloc] initWithCString:buf];
501
502        [_ctx setObject:numForUInt(y) forKey:WETableMatrix_Index];
503
504        [self->template appendToResponse:_response inContext:_ctx];
505        [s release];
506      }
507
508      /* foreach column */
509      for (x = 0; x < columnCount; x++) {
510        NSArray  *spans;
511        unsigned i;
512
513        spans = [_spans objectAtIndex:x];
514
515        if ([spans count] == 0) { /* no content cells */
516          if ((y == 0) || noEmptySpan) {
517            /* max rowspan, only encode in first row */
518
519            [self _genEmptyCellWithRowSpan:(noEmptySpan ? 1 : rowCount)
520                  toResponse:_response
521                  inContext:_ctx];
522          }
523          continue;
524        }
525
526        /* foreach sub-columns (title-COLSPAN=subcell-count) */
527
528        for (i = 0; i < [spans count]; i++) {
529          NSArray *thread; /* sub-column top-down */
530          unsigned j;
531
532          thread = [spans objectAtIndex:i];
533          NSCAssert([thread count] > 0, @"no contents in thread");
534
535          for (j = 0; j < [thread count]; j++) {
536            id span;
537
538            span = [thread objectAtIndex:j];
539            if (![span occupiesIndex:y])
540              continue;
541
542            if ([span startsAtIndex:y]) {
543              char buf[64];
544              NSString *s;
545
546              sprintf(buf, "%i", [span size]);
547              s = [[StrClass alloc] initWithCString:buf];
548              [_ctx setObject:s forKey:WETableMatrix_RowSpan];
549
550              [self->column setValue:[self->_cols objectAtIndex:x]
551                            inComponent:self->component];
552
553              if ([span object]) {
554                /* setup context */
555                [self->item setValue:[span object] inComponent:self->component];
556
557                /* generate body */
558                [_ctx setObject:@"content" forKey:WETableMatrix_Mode];
559
560                [self->template appendToResponse:_response inContext:_ctx];
561              }
562              else {
563                [self _genEmptyCellWithRowSpan:(noEmptySpan ? 1 : [span size])
564                      toResponse:_response
565                      inContext:_ctx];
566              }
567
568              [_ctx removeObjectForKey:WETableMatrix_RowSpan];
569              [s release]; s = nil;
570            }
571            else if (noEmptySpan) {
572              if ([span object] == nil) {
573                [self _genEmptyCellWithRowSpan:1
574                      toResponse:_response
575                      inContext:_ctx];
576              }
577            }
578          }
579        } /* end of 'foreach sub-columns (title-COLSPAN=subcell-count)' */
580      } /* end of 'foreach column' */
581      [_response appendContentString:@"</tr>"];
582    }
583  }
584
585  /* footer row */
586
587  if ([self->subElems containsObject:@"bottom"]) {
588  }
589}
590
591@end /* WEVSpanTableMatrix */
592
593@implementation WEHSpanTableMatrix
594
595- (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
596  return [_matrix rowSpans];
597}
598
599- (void)appendVerticalSpan:(NSArray *)_threads
600  toResponse:(WOResponse *)_response
601  inContext:(WOContext *)_ctx
602{
603  unsigned x, width;
604
605  width = [self->_cols count];
606
607  for (x = 0; x < width; x++) { /* foreach table column */
608    unsigned j, tc;
609
610    for (j = 0, tc = [_threads count]; j < tc; j++) {
611      id span;
612
613      span = [_threads objectAtIndex:j];
614      if (![span occupiesIndex:x])
615        continue;
616
617      if ([span startsAtIndex:x]) {
618        NSString *s;
619        char buf[64];
620
621        sprintf(buf, "%d", [span size]);
622        s = [[StrClass alloc] initWithCString:buf];
623        [_ctx setObject:s forKey:WETableMatrix_ColSpan];
624
625        if ([span object]) {
626          /* setup context */
627          [self->column setValue:[self->_cols objectAtIndex:x]
628                        inComponent:self->component];
629          [self->item setValue:[span object] inComponent:self->component];
630
631          /* generate body */
632          [_ctx setObject:@"content" forKey:WETableMatrix_Mode];
633          [self->template appendToResponse:_response inContext:_ctx];
634        }
635        else {
636          /* generate empty body */
637          if ([self->subElems containsObject:@"empty"]) {
638            [_ctx setObject:@"empty" forKey:WETableMatrix_Mode];
639            [self->template appendToResponse:_response inContext:_ctx];
640          }
641          else {
642            [_response appendContentString:@"<td colspan=\""];
643            [_response appendContentString:s];
644            [_response appendContentString:@"\">"];
645            [_response appendContentString:@"&nbsp;"];
646            [_response appendContentString:@"</td>\n"];
647          }
648        }
649        [s release]; s = nil;
650      }
651    }
652  }
653}
654
655- (void)appendSpans:(NSArray *)_spans
656  toResponse:(WOResponse *)_response
657  inContext:(WOContext *)_ctx
658{
659  WOComponent *sComponent;
660#if 0 // hh: unused
661  NSString *horizWidth;
662#endif
663  unsigned columnCount;
664
665  sComponent  = [_ctx component];
666  columnCount = [self->_cols count];
667#if 0 // hh: unused
668  horizWidth  =
669    [StrClass stringWithFormat:@"%d%%", (unsigned)(100.0/columnCount)];
670#endif
671
672  [_ctx setObject:numForUInt(columnCount) forKey:WETableMatrix_Count];
673
674  /* head row */
675
676  if ([self->subElems containsObject:@"top"]) {
677    unsigned i, count;
678
679    [_response appendContentString:@"<tr>"];
680
681    /* edge */
682    if ([self->subElems containsObject:@"left"])
683      [_response appendContentString:@"<td>&nbsp;</td>"];
684
685    /* header */
686    [_ctx setObject:@"top" forKey:WETableMatrix_Mode];
687    count = [self->_cols count];
688
689    for (i = 0; i < count; i++) {
690      [_ctx setObject:numForUInt(i) forKey:WETableMatrix_Index];
691
692      [self->column setValue:[self->_cols objectAtIndex:i]
693                    inComponent:sComponent];
694
695      [self->template appendToResponse:_response inContext:_ctx];
696    }
697    [_response appendContentString:@"</tr>"];
698  }
699
700  /* body rows */
701  {
702    unsigned y, width, height;
703
704    width  = [self->_cols count];
705    height = [self->_rows count];
706
707    for (y = 0; y < height; y++) { /* foreach row */
708      NSArray  *rowSpans;
709      unsigned i, count;
710
711      /* apply context */
712      [self->row setValue:[self->_rows objectAtIndex:y]
713                 inComponent:self->component];
714
715      /* get span */
716      rowSpans = [_spans objectAtIndex:y];
717      count    = [rowSpans count];
718
719      /* begin first row */
720      [_response appendContentString:@"<tr>"];
721
722      /* left edge */
723      {
724        char     buf[64];
725        NSString *s;
726
727        sprintf(buf, "%d", count);
728        s = [[StrClass alloc] initWithCString:buf];
729        [_ctx setObject:s forKey:WETableMatrix_RowSpan];
730
731        [_ctx setObject:@"left" forKey:WETableMatrix_Mode];
732
733        [_ctx setObject:numForUInt(y) forKey:WETableMatrix_Index];
734        [self->template appendToResponse:_response inContext:_ctx];
735        [s release]; s = nil;
736        [_ctx removeObjectForKey:WETableMatrix_RowSpan];
737      }
738#if 0 /* hh: why is this commented out ? */
739      /* left label ? */
740      [_response appendContentString:@"<td"];
741      if (count > 1) {
742        NSString *s;
743
744        [_response appendContentString:@" rowspan=\""];
745        s = [[StrClass alloc] initWithFormat:@"%d", count];
746        [_response appendContentString:s];
747        [s release];
748        [_response appendContentString:@"\""];
749      }
750      [_response appendContentString:@">"];
751      [genLabel];
752      [_response appendContentString:@"</td>"];
753#endif
754
755      /* check for empty span */
756      if (count == 0) {
757        NSString *s;
758
759        s = [[StrClass alloc] initWithFormat:@"%d", width];
760
761        /* max rowspan, only encode in first row */
762        [_ctx setObject:@"empty" forKey:WETableMatrix_Mode];
763        [_ctx setObject:s forKey:WETableMatrix_ColSpan];
764        [self->template appendToResponse:_response inContext:_ctx];
765        [_ctx removeObjectForKey:WETableMatrix_ColSpan];
766#if 0 /* hh: why is this commented out ? */
767
768        [_response appendContentString:@"<td colspan=\""];
769        [_response appendContentString:s];
770        [_response appendContentString:@"\">&nbsp;</td>"];
771
772        /* close completly filled row */
773        [_response appendContentString:@"</tr>"];
774#endif
775        [s release];
776        continue;
777      }
778
779      /* first span (same row like vertical label) */
780      if (count > 0) {
781        NSArray *thread;
782
783        thread = [rowSpans objectAtIndex:0];
784
785        [self appendVerticalSpan:thread
786              toResponse:_response
787              inContext:_ctx];
788      }
789      /* close first row */
790      [_response appendContentString:@"</tr>"];
791
792      for (i = 1; i < count; i++) { /* foreach additional span */
793        NSArray *thread;
794
795        thread = [rowSpans objectAtIndex:i];
796
797        [_response appendContentString:@"<tr>"];
798
799        [self appendVerticalSpan:thread
800              toResponse:_response
801              inContext:_ctx];
802
803        [_response appendContentString:@"</tr>"];
804      }
805      [_ctx removeObjectForKey:WETableMatrix_ColSpan];
806    }
807  }
808
809  /* footer row */
810
811  if ([self->subElems containsObject:@"bottom"]) {
812    unsigned i, count;
813
814    [_response appendContentString:@"<tr>"];
815
816    /* edge */
817    if ([self->subElems containsObject:@"left"])
818      [_response appendContentString:@"<td>&nbsp;</td>"];
819
820    /* footer */
821
822    [_ctx setObject:@"bottom" forKey:WETableMatrix_Mode];
823
824    count = [self->_cols count];
825
826    for (i = 0; i < count; i++) {
827      [_ctx setObject:numForUInt(i) forKey:WETableMatrix_Index];
828
829      [self->column setValue:[self->_cols objectAtIndex:i]
830                    inComponent:sComponent];
831
832      [self->template appendToResponse:_response inContext:_ctx];
833    }
834    [_response appendContentString:@"</tr>"];
835  }
836}
837
838@end /* WEVSpanTableMatrix */
839