2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
23 #include <NGObjWeb/WODynamicElement.h>
24 #include <NGObjWeb/WOAssociation.h>
25 #include <NGObjWeb/WOContext.h>
26 #include <NGObjWeb/WOResponse.h>
27 #include "WETableCalcMatrix.h"
31 for examples of this element, look at SkySchedulerViews chart-views.
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";
42 @interface WETableMatrix : WODynamicElement
44 WOAssociation *list; /* array of objects */
45 WOAssociation *item; /* current object */
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 */
52 WOAssociation *itemActive; /* query whether item is active in row/column */
53 WOAssociation *isRowActive;
54 WOAssociation *isColumnActive;
58 /* transient, not reentrant (need to lock) */
59 WOComponent *component;
68 @interface WEHSpanTableMatrix : WETableMatrix
71 @interface WEVSpanTableMatrix : WETableMatrix
73 WOAssociation *rowHeight; /* height of TR tag */
74 WOAssociation *noSpanInEmptyCells; /* do empty cells span rows ? */
78 static BOOL genComments = NO;
80 @implementation WETableMatrix
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;
88 static BOOL didInit = NO;
93 StrClass = [NSString class];
94 NumClass = [NSNumber class];
95 for (i = 0; i < 10; i++)
96 smap[i] = [[NumClass numberWithUnsignedInt:i] retain];
99 static NSNumber *numForUInt(unsigned int i) {
101 if (i < 10) return smap[i];
102 return [NumClass numberWithUnsignedInt:i];
105 - (id)initWithName:(NSString *)_name
106 associations:(NSDictionary *)_config
107 template:(WOElement *)_subs
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");
120 self->template = [_subs retain];
126 [self->isRowActive release];
127 [self->isColumnActive release];
128 [self->itemActive 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];
139 /* matrix delegate */
141 - (BOOL)tableCalcMatrix:(WETableCalcMatrix *)_matrix
142 shouldPlaceObject:(id)_object
143 atPosition:(unsigned)_x:(unsigned)_y
148 _col = [self->_cols objectAtIndex:_x];
149 _row = [self->_rows objectAtIndex:_y];
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];
157 NSLog(@"%i/%i: col %@ row %@", _x,_y, _col, _row);
161 doPlace = [self->itemActive boolValueInComponent:self->component];
163 NSLog(@" %@ placed: %s", self->itemActive, doPlace ? "yes" : "no");
169 - (BOOL)tableCalcMatrix:(WETableCalcMatrix *)_matrix
170 shouldProcessColumn:(unsigned)_x
171 forObject:(id)_object
173 if (!self->isColumnActive)
176 [self->column setValue:[self->_cols objectAtIndex:_x]
177 inComponent:self->component];
178 [self->item setValue:_object inComponent:self->component];
180 return [self->isColumnActive boolValueInComponent:self->component];
182 - (BOOL)tableCalcMatrix:(WETableCalcMatrix *)_matrix
183 shouldProcessRow:(unsigned)_y
184 forObject:(id)_object
186 if (!self->isRowActive)
189 [self->row setValue:[self->_rows objectAtIndex:_y]
190 inComponent:self->component];
191 [self->item setValue:_object inComponent:self->component];
193 return [self->isRowActive boolValueInComponent:self->component];
196 /* HTML generation */
198 - (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
199 [self logWithFormat:@"ERROR: subclasses should override %@!",
200 NSStringFromSelector(_cmd)];
203 - (void)appendSpans:(NSArray *)_spans
204 toResponse:(WOResponse *)_response
205 inContext:(WOContext *)_ctx
207 [self logWithFormat:@"ERROR: subclasses should override %@!",
208 NSStringFromSelector(_cmd)];
211 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
212 NSLog(@"WARNING(WETableMatrix): unsupported invokeActionForRequest called!");
213 return [super invokeActionForRequest:_req inContext:_ctx];
217 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
218 NSAutoreleasePool *pool;
219 WETableCalcMatrix *matrix;
221 unsigned rowCount, colCount, count;
223 self->component = [_ctx component];
225 [[[self->list valueInComponent:self->component] copy] autorelease];
227 [[[self->rows valueInComponent:self->component] copy] autorelease];
229 [[[self->columns valueInComponent:self->component] copy] autorelease];
231 count = [self->objs count];
232 rowCount = [self->_rows count];
233 colCount = [self->_cols count];
235 /* query subelements */
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];
247 if ([self->subElems count] == 0) {
249 NSLog(@"WARNING: no sub-elements !");
254 NSLog(@"subelems: %@", [self->subElems allObjects]);
257 /* fill matrix and calculate spans */
259 pool = [[NSAutoreleasePool alloc] init];
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;
267 [pool release]; pool = nil;
268 [allSpans autorelease];
270 /* generate vertical table */
272 pool = [[NSAutoreleasePool alloc] init];
274 [_response appendContentString:@"<table"];
275 [self appendExtraAttributesToResponse:_response inContext:_ctx];
276 [_response appendContentString:@">"];
278 [self appendSpans:allSpans
282 [_response appendContentString:@"</table>"];
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];
291 /* reset transients */
292 self->subElems = nil;
296 self->component = nil;
300 @end /* WETableMatrix */
302 @implementation WEVSpanTableMatrix
304 - (id)initWithName:(NSString *)_name
305 associations:(NSDictionary *)_config
306 template:(WOElement *)_subs
308 if ((self = [super initWithName:_name associations:_config template:_subs])) {
309 self->rowHeight = WOExtGetProperty(_config, @"rowHeight");
310 self->noSpanInEmptyCells = WOExtGetProperty(_config, @"noSpanInEmptyCells");
316 [self->noSpanInEmptyCells release];
317 [self->rowHeight release];
321 - (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
322 return [_matrix columnSpans];
325 - (void)_genEmptyCellWithRowSpan:(int)_span
326 toResponse:(WOResponse *)_response
327 inContext:(WOContext *)_ctx
333 sprintf(buf, "%i", _span);
334 s = [[StrClass alloc] initWithCString:buf];
339 if ([self->subElems containsObject:@"empty"]) {
341 [_ctx setObject:s forKey:WETableMatrix_RowSpan];
343 [_ctx setObject:@"empty" forKey:WETableMatrix_Mode];
345 [self->template appendToResponse:_response inContext:_ctx];
348 [_ctx removeObjectForKey:WETableMatrix_RowSpan];
351 [_response appendContentString:@"<td"];
353 [_response appendContentString:@" rowspan=\""];
354 [_response appendContentString:s];
355 [_response appendContentString:@"\""];
357 [_response appendContentString:@">"];
359 [_response appendContentString:@" "];
360 [_response appendContentString:@"</td>\n"];
366 - (void)appendSpans:(NSArray *)_spans
367 toResponse:(WOResponse *)_response
368 inContext:(WOContext *)_ctx
370 WOComponent *sComponent;
372 NSString *horizWidth;
374 unsigned columnCount, rowCount;
375 BOOL noEmptySpan; // empty cells do not have ROWSPAN (more cells are written)
377 sComponent = [_ctx component];
378 columnCount = [self->_cols count];
379 rowCount = [self->_rows count];
382 [StrClass stringWithFormat:@"%d%%", (unsigned)(100.0/columnCount)];
385 noEmptySpan = [self->noSpanInEmptyCells boolValueInComponent:sComponent];
387 [_ctx setObject:numForUInt(columnCount) forKey:WETableMatrix_Count];
390 if ([self->subElems containsObject:@"top"]) {
395 if ([self->subElems containsObject:@"left"])
398 [_response appendContentString:@"<tr>\n"];
401 if (numOfLeftLabels > 0) {
405 sprintf(buf, "%i", numOfLeftLabels);
406 s = [[StrClass alloc] initWithCString:buf];
408 if ([self->subElems containsObject:@"topleft"]) {
409 [_ctx setObject:s forKey:WETableMatrix_ColSpan];
410 [_ctx setObject:@"topleft" forKey:WETableMatrix_Mode];
412 [self->template appendToResponse:_response inContext:_ctx];
414 [_ctx removeObjectForKey:WETableMatrix_ColSpan];
417 [_response appendContentString:@" <td"];
418 if (numOfLeftLabels > 1) {
419 [_response appendContentString:@" colspan=\""];
420 [_response appendContentString:s];
421 [_response appendContentString:@"\""];
423 [_response appendContentString:@"> </td>\n"];
429 [_ctx setObject:@"top" forKey:WETableMatrix_Mode];
431 for (x = 0; x < columnCount; x++) {
436 spans = [_spans objectAtIndex:x];
438 sprintf(buf, "%d", [spans count]);
439 s = [[StrClass alloc] initWithCString:buf];
440 [_ctx setObject:s forKey:WETableMatrix_ColSpan];
442 [self->column setValue:[self->_cols objectAtIndex:x]
443 inComponent:sComponent];
444 [_ctx setObject:numForUInt(x) forKey:WETableMatrix_Index];
446 [self->template appendToResponse:_response inContext:_ctx];
448 [s release]; s = nil;
451 [_response appendContentString:@"</tr>"];
454 [_ctx removeObjectForKey:WETableMatrix_ColSpan];
455 [_ctx removeObjectForKey:WETableMatrix_RowSpan];
456 [_ctx removeObjectForKey:WETableMatrix_Index];
463 for (y = 0; y < rowCount; y++) {
466 [self->row setValue:[self->_rows objectAtIndex:y]
467 inComponent:[_ctx component]];
469 if (self->rowHeight) {
470 [_response appendContentString:@"<tr height=\""];
471 [_response appendContentString:
472 [self->rowHeight stringValueInComponent:sComponent]];
473 [_response appendContentString:@"\">"];
476 [_response appendContentString:@"<tr>"];
480 s = [[StrClass alloc] initWithFormat:@"<!-- row %i -->\n", y];
481 [_response appendContentString:s];
490 [_ctx setObject:@"left" forKey:WETableMatrix_Mode];
492 sprintf(buf, "%i", y);
493 s = [[StrClass alloc] initWithCString:buf];
495 [_ctx setObject:numForUInt(y) forKey:WETableMatrix_Index];
497 [self->template appendToResponse:_response inContext:_ctx];
502 for (x = 0; x < columnCount; x++) {
506 spans = [_spans objectAtIndex:x];
508 if ([spans count] == 0) { /* no content cells */
509 if ((y == 0) || noEmptySpan) {
510 /* max rowspan, only encode in first row */
512 [self _genEmptyCellWithRowSpan:(noEmptySpan ? 1 : rowCount)
519 /* foreach sub-columns (title-COLSPAN=subcell-count) */
521 for (i = 0; i < [spans count]; i++) {
522 NSArray *thread; /* sub-column top-down */
525 thread = [spans objectAtIndex:i];
526 NSCAssert([thread count] > 0, @"no contents in thread");
528 for (j = 0; j < [thread count]; j++) {
531 span = [thread objectAtIndex:j];
532 if (![span occupiesIndex:y])
535 if ([span startsAtIndex:y]) {
539 sprintf(buf, "%i", [span size]);
540 s = [[StrClass alloc] initWithCString:buf];
541 [_ctx setObject:s forKey:WETableMatrix_RowSpan];
543 [self->column setValue:[self->_cols objectAtIndex:x]
544 inComponent:self->component];
548 [self->item setValue:[span object] inComponent:self->component];
551 [_ctx setObject:@"content" forKey:WETableMatrix_Mode];
553 [self->template appendToResponse:_response inContext:_ctx];
556 [self _genEmptyCellWithRowSpan:(noEmptySpan ? 1 : [span size])
561 [_ctx removeObjectForKey:WETableMatrix_RowSpan];
562 [s release]; s = nil;
564 else if (noEmptySpan) {
565 if ([span object] == nil) {
566 [self _genEmptyCellWithRowSpan:1
572 } /* end of 'foreach sub-columns (title-COLSPAN=subcell-count)' */
573 } /* end of 'foreach column' */
574 [_response appendContentString:@"</tr>"];
580 if ([self->subElems containsObject:@"bottom"]) {
584 @end /* WEVSpanTableMatrix */
586 @implementation WEHSpanTableMatrix
588 - (NSArray *)spansFromMatrix:(WETableCalcMatrix *)_matrix {
589 return [_matrix rowSpans];
592 - (void)appendVerticalSpan:(NSArray *)_threads
593 toResponse:(WOResponse *)_response
594 inContext:(WOContext *)_ctx
598 width = [self->_cols count];
600 for (x = 0; x < width; x++) { /* foreach table column */
603 for (j = 0, tc = [_threads count]; j < tc; j++) {
606 span = [_threads objectAtIndex:j];
607 if (![span occupiesIndex:x])
610 if ([span startsAtIndex:x]) {
614 sprintf(buf, "%d", [span size]);
615 s = [[StrClass alloc] initWithCString:buf];
616 [_ctx setObject:s forKey:WETableMatrix_ColSpan];
620 [self->column setValue:[self->_cols objectAtIndex:x]
621 inComponent:self->component];
622 [self->item setValue:[span object] inComponent:self->component];
625 [_ctx setObject:@"content" forKey:WETableMatrix_Mode];
626 [self->template appendToResponse:_response inContext:_ctx];
629 /* generate empty body */
630 if ([self->subElems containsObject:@"empty"]) {
631 [_ctx setObject:@"empty" forKey:WETableMatrix_Mode];
632 [self->template appendToResponse:_response inContext:_ctx];
635 [_response appendContentString:@"<td colspan=\""];
636 [_response appendContentString:s];
637 [_response appendContentString:@"\">"];
638 [_response appendContentString:@" "];
639 [_response appendContentString:@"</td>\n"];
642 [s release]; s = nil;
648 - (void)appendSpans:(NSArray *)_spans
649 toResponse:(WOResponse *)_response
650 inContext:(WOContext *)_ctx
652 WOComponent *sComponent;
654 NSString *horizWidth;
656 unsigned columnCount;
658 sComponent = [_ctx component];
659 columnCount = [self->_cols count];
662 [StrClass stringWithFormat:@"%d%%", (unsigned)(100.0/columnCount)];
665 [_ctx setObject:numForUInt(columnCount) forKey:WETableMatrix_Count];
669 if ([self->subElems containsObject:@"top"]) {
672 [_response appendContentString:@"<tr>"];
675 if ([self->subElems containsObject:@"left"])
676 [_response appendContentString:@"<td> </td>"];
679 [_ctx setObject:@"top" forKey:WETableMatrix_Mode];
680 count = [self->_cols count];
682 for (i = 0; i < count; i++) {
683 [_ctx setObject:numForUInt(i) forKey:WETableMatrix_Index];
685 [self->column setValue:[self->_cols objectAtIndex:i]
686 inComponent:sComponent];
688 [self->template appendToResponse:_response inContext:_ctx];
690 [_response appendContentString:@"</tr>"];
695 unsigned y, width, height;
697 width = [self->_cols count];
698 height = [self->_rows count];
700 for (y = 0; y < height; y++) { /* foreach row */
705 [self->row setValue:[self->_rows objectAtIndex:y]
706 inComponent:self->component];
709 rowSpans = [_spans objectAtIndex:y];
710 count = [rowSpans count];
712 /* begin first row */
713 [_response appendContentString:@"<tr>"];
720 sprintf(buf, "%d", count);
721 s = [[StrClass alloc] initWithCString:buf];
722 [_ctx setObject:s forKey:WETableMatrix_RowSpan];
724 [_ctx setObject:@"left" forKey:WETableMatrix_Mode];
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];
731 #if 0 /* hh: why is this commented out ? */
733 [_response appendContentString:@"<td"];
737 [_response appendContentString:@" rowspan=\""];
738 s = [[StrClass alloc] initWithFormat:@"%d", count];
739 [_response appendContentString:s];
741 [_response appendContentString:@"\""];
743 [_response appendContentString:@">"];
745 [_response appendContentString:@"</td>"];
748 /* check for empty span */
752 s = [[StrClass alloc] initWithFormat:@"%d", width];
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 ? */
761 [_response appendContentString:@"<td colspan=\""];
762 [_response appendContentString:s];
763 [_response appendContentString:@"\"> </td>"];
765 /* close completly filled row */
766 [_response appendContentString:@"</tr>"];
772 /* first span (same row like vertical label) */
776 thread = [rowSpans objectAtIndex:0];
778 [self appendVerticalSpan:thread
782 /* close first row */
783 [_response appendContentString:@"</tr>"];
785 for (i = 1; i < count; i++) { /* foreach additional span */
788 thread = [rowSpans objectAtIndex:i];
790 [_response appendContentString:@"<tr>"];
792 [self appendVerticalSpan:thread
796 [_response appendContentString:@"</tr>"];
798 [_ctx removeObjectForKey:WETableMatrix_ColSpan];
804 if ([self->subElems containsObject:@"bottom"]) {
807 [_response appendContentString:@"<tr>"];
810 if ([self->subElems containsObject:@"left"])
811 [_response appendContentString:@"<td> </td>"];
815 [_ctx setObject:@"bottom" forKey:WETableMatrix_Mode];
817 count = [self->_cols count];
819 for (i = 0; i < count; i++) {
820 [_ctx setObject:numForUInt(i) forKey:WETableMatrix_Index];
822 [self->column setValue:[self->_cols objectAtIndex:i]
823 inComponent:sComponent];
825 [self->template appendToResponse:_response inContext:_ctx];
827 [_response appendContentString:@"</tr>"];
831 @end /* WEVSpanTableMatrix */