2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #include <NGObjWeb/WODynamicElement.h>
23 #include <NGObjWeb/WOAssociation.h>
24 #include <NGObjWeb/WOContext.h>
25 #include <NGObjWeb/WOResponse.h>
26 #include "WETableCalcMatrix.h"
30 for examples of this element, look at SkySchedulerViews chart-views.
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";
41 @interface WETableMatrix : WODynamicElement
43 WOAssociation *list; /* array of objects */
44 WOAssociation *item; /* current object */
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 */
51 WOAssociation *itemActive; /* query whether item is active in row/column */
52 WOAssociation *isRowActive;
53 WOAssociation *isColumnActive;
57 /* transient, not reentrant (need to lock) */
58 WOComponent *component;
67 @interface WEHSpanTableMatrix : WETableMatrix
70 @interface WEVSpanTableMatrix : WETableMatrix
72 WOAssociation *rowHeight; /* height of TR tag */
73 WOAssociation *noSpanInEmptyCells; /* do empty cells span rows ? */
77 static BOOL genComments = NO;
79 @implementation WETableMatrix
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;
87 static BOOL didInit = NO;
92 StrClass = [NSString class];
93 NumClass = [NSNumber class];
94 for (i = 0; i < 10; i++)
95 smap[i] = [[NumClass numberWithUnsignedInt:i] retain];
98 static NSNumber *numForUInt(unsigned int i) {
100 if (i < 10) return smap[i];
101 return [NumClass numberWithUnsignedInt:i];
104 - (id)initWithName:(NSString *)_name
105 associations:(NSDictionary *)_config
106 template:(WOElement *)_subs
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");
119 self->template = [_subs retain];
125 [self->isRowActive release];
126 [self->isColumnActive release];
127 [self->itemActive 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];
138 /* matrix delegate */
140 - (BOOL)tableCalcMatrix:(WETableCalcMatrix *)_matrix
141 shouldPlaceObject:(id)_object
142 atPosition:(unsigned)_x:(unsigned)_y
147 _col = [self->_cols objectAtIndex:_x];
148 _row = [self->_rows objectAtIndex:_y];
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];
156 NSLog(@"%i/%i: col %@ row %@", _x,_y, _col, _row);
160 doPlace = [self->itemActive boolValueInComponent:self->component];
162 NSLog(@" %@ placed: %s", self->itemActive, doPlace ? "yes" : "no");
168 - (BOOL)tableCalcMatrix:(WETableCalcMatrix *)_matrix
169 shouldProcessColumn:(unsigned)_x
170 forObject:(id)_object
172 if (!self->isColumnActive)
175 [self->column setValue:[self->_cols objectAtIndex:_x]
176 inComponent:self->component];
177 [self->item setValue:_object inComponent:self->component];
179 return [self->isColumnActive boolValueInComponent:self->component];
181 - (BOOL)tableCalcMatrix:(WETableCalcMatrix *)_matrix
182 shouldProcessRow:(unsigned)_y
183 forObject:(id)_object
185 if (!self->isRowActive)
188 [self->row setValue:[self->_rows objectAtIndex:_y]
189 inComponent:self->component];
190 [self->item setValue:_object inComponent:self->component];
192 return [self->isRowActive boolValueInComponent:self->component];
195 /* HTML generation */
197 - (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
198 [self logWithFormat:@"ERROR: subclasses should override %@!",
199 NSStringFromSelector(_cmd)];
202 - (void)appendSpans:(NSArray *)_spans
203 toResponse:(WOResponse *)_response
204 inContext:(WOContext *)_ctx
206 [self logWithFormat:@"ERROR: subclasses should override %@!",
207 NSStringFromSelector(_cmd)];
210 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
211 NSLog(@"WARNING(WETableMatrix): unsupported invokeActionForRequest called!");
212 return [super invokeActionForRequest:_req inContext:_ctx];
216 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
217 NSAutoreleasePool *pool;
218 WETableCalcMatrix *matrix;
220 unsigned rowCount, colCount, count;
222 self->component = [_ctx component];
224 [[[self->list valueInComponent:self->component] copy] autorelease];
226 [[[self->rows valueInComponent:self->component] copy] autorelease];
228 [[[self->columns valueInComponent:self->component] copy] autorelease];
230 count = [self->objs count];
231 rowCount = [self->_rows count];
232 colCount = [self->_cols count];
234 /* query subelements */
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];
246 if ([self->subElems count] == 0) {
248 NSLog(@"WARNING: no sub-elements !");
253 NSLog(@"subelems: %@", [self->subElems allObjects]);
256 /* fill matrix and calculate spans */
258 pool = [[NSAutoreleasePool alloc] init];
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;
266 [pool release]; pool = nil;
267 [allSpans autorelease];
269 /* generate vertical table */
271 pool = [[NSAutoreleasePool alloc] init];
273 [_response appendContentString:@"<table"];
274 [self appendExtraAttributesToResponse:_response inContext:_ctx];
275 [_response appendContentString:@">"];
277 [self appendSpans:allSpans
281 [_response appendContentString:@"</table>"];
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];
290 /* reset transients */
291 self->subElems = nil;
295 self->component = nil;
299 @end /* WETableMatrix */
301 @implementation WEVSpanTableMatrix
303 - (id)initWithName:(NSString *)_name
304 associations:(NSDictionary *)_config
305 template:(WOElement *)_subs
307 if ((self = [super initWithName:_name associations:_config template:_subs])) {
308 self->rowHeight = WOExtGetProperty(_config, @"rowHeight");
309 self->noSpanInEmptyCells = WOExtGetProperty(_config, @"noSpanInEmptyCells");
315 [self->noSpanInEmptyCells release];
316 [self->rowHeight release];
320 - (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
321 return [_matrix columnSpans];
324 - (void)_genEmptyCellWithRowSpan:(int)_span
325 toResponse:(WOResponse *)_response
326 inContext:(WOContext *)_ctx
332 sprintf(buf, "%i", _span);
333 s = [[StrClass alloc] initWithCString:buf];
338 if ([self->subElems containsObject:@"empty"]) {
340 [_ctx setObject:s forKey:WETableMatrix_RowSpan];
342 [_ctx setObject:@"empty" forKey:WETableMatrix_Mode];
344 [self->template appendToResponse:_response inContext:_ctx];
347 [_ctx removeObjectForKey:WETableMatrix_RowSpan];
350 [_response appendContentString:@"<td"];
352 [_response appendContentString:@" rowspan=\""];
353 [_response appendContentString:s];
354 [_response appendContentString:@"\""];
356 [_response appendContentString:@">"];
358 [_response appendContentString:@" "];
359 [_response appendContentString:@"</td>\n"];
365 - (void)appendSpans:(NSArray *)_spans
366 toResponse:(WOResponse *)_response
367 inContext:(WOContext *)_ctx
369 WOComponent *sComponent;
371 NSString *horizWidth;
373 unsigned columnCount, rowCount;
374 BOOL noEmptySpan; // empty cells do not have ROWSPAN (more cells are written)
376 sComponent = [_ctx component];
377 columnCount = [self->_cols count];
378 rowCount = [self->_rows count];
381 [StrClass stringWithFormat:@"%d%%", (unsigned)(100.0/columnCount)];
384 noEmptySpan = [self->noSpanInEmptyCells boolValueInComponent:sComponent];
386 [_ctx setObject:numForUInt(columnCount) forKey:WETableMatrix_Count];
389 if ([self->subElems containsObject:@"top"]) {
394 if ([self->subElems containsObject:@"left"])
397 [_response appendContentString:@"<tr>\n"];
400 if (numOfLeftLabels > 0) {
404 sprintf(buf, "%i", numOfLeftLabels);
405 s = [[StrClass alloc] initWithCString:buf];
407 if ([self->subElems containsObject:@"topleft"]) {
408 [_ctx setObject:s forKey:WETableMatrix_ColSpan];
409 [_ctx setObject:@"topleft" forKey:WETableMatrix_Mode];
411 [self->template appendToResponse:_response inContext:_ctx];
413 [_ctx removeObjectForKey:WETableMatrix_ColSpan];
416 [_response appendContentString:@" <td"];
417 if (numOfLeftLabels > 1) {
418 [_response appendContentString:@" colspan=\""];
419 [_response appendContentString:s];
420 [_response appendContentString:@"\""];
422 [_response appendContentString:@"> </td>\n"];
428 [_ctx setObject:@"top" forKey:WETableMatrix_Mode];
430 for (x = 0; x < columnCount; x++) {
435 spans = [_spans objectAtIndex:x];
437 sprintf(buf, "%d", [spans count]);
438 s = [[StrClass alloc] initWithCString:buf];
439 [_ctx setObject:s forKey:WETableMatrix_ColSpan];
441 [self->column setValue:[self->_cols objectAtIndex:x]
442 inComponent:sComponent];
443 [_ctx setObject:numForUInt(x) forKey:WETableMatrix_Index];
445 [self->template appendToResponse:_response inContext:_ctx];
447 [s release]; s = nil;
450 [_response appendContentString:@"</tr>"];
453 [_ctx removeObjectForKey:WETableMatrix_ColSpan];
454 [_ctx removeObjectForKey:WETableMatrix_RowSpan];
455 [_ctx removeObjectForKey:WETableMatrix_Index];
462 for (y = 0; y < rowCount; y++) {
465 [self->row setValue:[self->_rows objectAtIndex:y]
466 inComponent:[_ctx component]];
468 if (self->rowHeight) {
469 [_response appendContentString:@"<tr height=\""];
470 [_response appendContentString:
471 [self->rowHeight stringValueInComponent:sComponent]];
472 [_response appendContentString:@"\">"];
475 [_response appendContentString:@"<tr>"];
479 s = [[StrClass alloc] initWithFormat:@"<!-- row %i -->\n", y];
480 [_response appendContentString:s];
489 [_ctx setObject:@"left" forKey:WETableMatrix_Mode];
491 sprintf(buf, "%i", y);
492 s = [[StrClass alloc] initWithCString:buf];
494 [_ctx setObject:numForUInt(y) forKey:WETableMatrix_Index];
496 [self->template appendToResponse:_response inContext:_ctx];
501 for (x = 0; x < columnCount; x++) {
505 spans = [_spans objectAtIndex:x];
507 if ([spans count] == 0) { /* no content cells */
508 if ((y == 0) || noEmptySpan) {
509 /* max rowspan, only encode in first row */
511 [self _genEmptyCellWithRowSpan:(noEmptySpan ? 1 : rowCount)
518 /* foreach sub-columns (title-COLSPAN=subcell-count) */
520 for (i = 0; i < [spans count]; i++) {
521 NSArray *thread; /* sub-column top-down */
524 thread = [spans objectAtIndex:i];
525 NSCAssert([thread count] > 0, @"no contents in thread");
527 for (j = 0; j < [thread count]; j++) {
530 span = [thread objectAtIndex:j];
531 if (![span occupiesIndex:y])
534 if ([span startsAtIndex:y]) {
538 sprintf(buf, "%i", [span size]);
539 s = [[StrClass alloc] initWithCString:buf];
540 [_ctx setObject:s forKey:WETableMatrix_RowSpan];
542 [self->column setValue:[self->_cols objectAtIndex:x]
543 inComponent:self->component];
547 [self->item setValue:[span object] inComponent:self->component];
550 [_ctx setObject:@"content" forKey:WETableMatrix_Mode];
552 [self->template appendToResponse:_response inContext:_ctx];
555 [self _genEmptyCellWithRowSpan:(noEmptySpan ? 1 : [span size])
560 [_ctx removeObjectForKey:WETableMatrix_RowSpan];
561 [s release]; s = nil;
563 else if (noEmptySpan) {
564 if ([span object] == nil) {
565 [self _genEmptyCellWithRowSpan:1
571 } /* end of 'foreach sub-columns (title-COLSPAN=subcell-count)' */
572 } /* end of 'foreach column' */
573 [_response appendContentString:@"</tr>"];
579 if ([self->subElems containsObject:@"bottom"]) {
583 @end /* WEVSpanTableMatrix */
585 @implementation WEHSpanTableMatrix
587 - (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
588 return [_matrix rowSpans];
591 - (void)appendVerticalSpan:(NSArray *)_threads
592 toResponse:(WOResponse *)_response
593 inContext:(WOContext *)_ctx
597 width = [self->_cols count];
599 for (x = 0; x < width; x++) { /* foreach table column */
602 for (j = 0, tc = [_threads count]; j < tc; j++) {
605 span = [_threads objectAtIndex:j];
606 if (![span occupiesIndex:x])
609 if ([span startsAtIndex:x]) {
613 sprintf(buf, "%d", [span size]);
614 s = [[StrClass alloc] initWithCString:buf];
615 [_ctx setObject:s forKey:WETableMatrix_ColSpan];
619 [self->column setValue:[self->_cols objectAtIndex:x]
620 inComponent:self->component];
621 [self->item setValue:[span object] inComponent:self->component];
624 [_ctx setObject:@"content" forKey:WETableMatrix_Mode];
625 [self->template appendToResponse:_response inContext:_ctx];
628 /* generate empty body */
629 if ([self->subElems containsObject:@"empty"]) {
630 [_ctx setObject:@"empty" forKey:WETableMatrix_Mode];
631 [self->template appendToResponse:_response inContext:_ctx];
634 [_response appendContentString:@"<td colspan=\""];
635 [_response appendContentString:s];
636 [_response appendContentString:@"\">"];
637 [_response appendContentString:@" "];
638 [_response appendContentString:@"</td>\n"];
641 [s release]; s = nil;
647 - (void)appendSpans:(NSArray *)_spans
648 toResponse:(WOResponse *)_response
649 inContext:(WOContext *)_ctx
651 WOComponent *sComponent;
653 NSString *horizWidth;
655 unsigned columnCount;
657 sComponent = [_ctx component];
658 columnCount = [self->_cols count];
661 [StrClass stringWithFormat:@"%d%%", (unsigned)(100.0/columnCount)];
664 [_ctx setObject:numForUInt(columnCount) forKey:WETableMatrix_Count];
668 if ([self->subElems containsObject:@"top"]) {
671 [_response appendContentString:@"<tr>"];
674 if ([self->subElems containsObject:@"left"])
675 [_response appendContentString:@"<td> </td>"];
678 [_ctx setObject:@"top" forKey:WETableMatrix_Mode];
679 count = [self->_cols count];
681 for (i = 0; i < count; i++) {
682 [_ctx setObject:numForUInt(i) forKey:WETableMatrix_Index];
684 [self->column setValue:[self->_cols objectAtIndex:i]
685 inComponent:sComponent];
687 [self->template appendToResponse:_response inContext:_ctx];
689 [_response appendContentString:@"</tr>"];
694 unsigned y, width, height;
696 width = [self->_cols count];
697 height = [self->_rows count];
699 for (y = 0; y < height; y++) { /* foreach row */
704 [self->row setValue:[self->_rows objectAtIndex:y]
705 inComponent:self->component];
708 rowSpans = [_spans objectAtIndex:y];
709 count = [rowSpans count];
711 /* begin first row */
712 [_response appendContentString:@"<tr>"];
719 sprintf(buf, "%d", count);
720 s = [[StrClass alloc] initWithCString:buf];
721 [_ctx setObject:s forKey:WETableMatrix_RowSpan];
723 [_ctx setObject:@"left" forKey:WETableMatrix_Mode];
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];
730 #if 0 /* hh: why is this commented out ? */
732 [_response appendContentString:@"<td"];
736 [_response appendContentString:@" rowspan=\""];
737 s = [[StrClass alloc] initWithFormat:@"%d", count];
738 [_response appendContentString:s];
740 [_response appendContentString:@"\""];
742 [_response appendContentString:@">"];
744 [_response appendContentString:@"</td>"];
747 /* check for empty span */
751 s = [[StrClass alloc] initWithFormat:@"%d", width];
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 ? */
760 [_response appendContentString:@"<td colspan=\""];
761 [_response appendContentString:s];
762 [_response appendContentString:@"\"> </td>"];
764 /* close completly filled row */
765 [_response appendContentString:@"</tr>"];
771 /* first span (same row like vertical label) */
775 thread = [rowSpans objectAtIndex:0];
777 [self appendVerticalSpan:thread
781 /* close first row */
782 [_response appendContentString:@"</tr>"];
784 for (i = 1; i < count; i++) { /* foreach additional span */
787 thread = [rowSpans objectAtIndex:i];
789 [_response appendContentString:@"<tr>"];
791 [self appendVerticalSpan:thread
795 [_response appendContentString:@"</tr>"];
797 [_ctx removeObjectForKey:WETableMatrix_ColSpan];
803 if ([self->subElems containsObject:@"bottom"]) {
806 [_response appendContentString:@"<tr>"];
809 if ([self->subElems containsObject:@"left"])
810 [_response appendContentString:@"<td> </td>"];
814 [_ctx setObject:@"bottom" forKey:WETableMatrix_Mode];
816 count = [self->_cols count];
818 for (i = 0; i < count; i++) {
819 [_ctx setObject:numForUInt(i) forKey:WETableMatrix_Index];
821 [self->column setValue:[self->_cols objectAtIndex:i]
822 inComponent:sComponent];
824 [self->template appendToResponse:_response inContext:_ctx];
826 [_response appendContentString:@"</tr>"];
830 @end /* WEVSpanTableMatrix */