2 Copyright (C) 2000-2008 SKYRIX Software AG
3 Copyright (C) 2008 Helge Hess
5 This file is part of SOPE.
7 SOPE is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with SOPE; see the file COPYING. If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include <NGObjWeb/WODynamicElement.h>
25 @class NSMutableArray, NSCalendarDate;
29 @interface WEMonthOverview : WODynamicElement
31 WOAssociation *list; // list of appointments
32 WOAssociation *item; // current item in list
33 WOAssociation *index; // index of current element
34 WOAssociation *identifier; // unique identifier for current item
36 WOAssociation *currentDay; // current day, e.g. 31.Aug, 1.Sep, 2.Sep, ...
38 WOAssociation *year; // year
39 WOAssociation *month; // month
40 WOAssociation *timeZone; // timeZone
42 WOAssociation *firstDay; // 0 - Sunday .. 6 - Saturday (default:1)
43 WOAssociation *tableTags; // make table tags
45 WOAssociation *startDateKey;
46 WOAssociation *endDateKey;
48 WOAssociation *labelStyle; // style sheet classes
49 WOAssociation *contentStyle;
51 WOAssociation *labelColor;
52 WOAssociation *contentColor;
55 /* Note: the matrix does NOT start at the 1st of a month! (the first row
56 * contains dates from the previous month, unless the 1st is Monday)
58 NSMutableArray *matrix[MatrixSize]; /* 42 mutable arrays (for each day) */
61 int firstDisplayedDay; // first day to be displayed Sun 0 .. Sat 6
62 int weeks; // number of weeks to display
63 NSCalendarDate *start; // reference date in matrix
67 // extra attributes forwarded to table data
70 @end /* WEMonthOverview */
72 @interface WEMonthLabel : WODynamicElement
74 WOAssociation *orientation;
75 // left/top | top | right/top | right | right/bottom | bottom | left/bottom
77 WOAssociation *dayOfWeek;
78 // set if orientation is top or bottom
79 WOAssociation *weekOfYear;
80 // set if orientation is left or right
81 WOAssociation *colspan;
82 // set if orientation is header
85 @end /* WEMonthLabel */
87 @interface WEMonthTitle : WODynamicElement
91 @end /* WEMonthTitle */
94 #include "WEContextConditional.h"
95 #include <math.h> /* needed for floor() */
98 static NSString *WEMonthOverview_InfoMode = @"WEMonthOverview_InfoMode";
99 static NSString *WEMonthOverview_ContentMode = @"WEMonthOverview_ContentMode";
101 #define SecondsPerDay (24 * 60 * 60)
103 @interface WOContext(WEMonthOverview)
105 - (void)setupMonthOverviewContextWithValue:(id)_value forKey:(NSString *)_key;
106 - (void)setupMonthOverviewContextForQueryMode;
107 - (void)setupMonthOverviewContextWithOrientation:(NSString *)_orient;
109 - (void)tearDownMonthOverviewContext;
110 - (NSDictionary *)monthOverviewContext;
111 - (NSMutableArray *)monthOverviewQueryObjects;
113 - (void)enableMonthOverviewInfoMode;
114 - (void)disableMonthOverviewInfoMode;
115 - (void)enableMonthOverviewContentMode;
116 - (void)disableMonthOverviewContentMode;
120 @implementation WOContext(WEMonthOverview)
122 - (void)setupMonthOverviewContextWithValue:(id)_value forKey:(NSString *)_key {
125 d = [[NSDictionary alloc] initWithObjects:&_value forKeys:&_key count:1];
126 [self setObject:d forKey:@"WEMonthOverview"];
129 - (void)setupMonthOverviewContextForQueryMode {
132 d = [[NSDictionary alloc] initWithObjectsAndKeys:
133 [NSMutableArray arrayWithCapacity:4],
135 [self setObject:d forKey:@"WEMonthOverview"];
138 - (void)setupMonthOverviewContextWithOrientation:(NSString *)_orient {
141 d = [[NSDictionary alloc] initWithObjectsAndKeys:@"--", _orient, nil];
142 [self setObject:d forKey:@"WEMonthOverview"];
146 - (void)tearDownMonthOverviewContext {
147 [self removeObjectForKey:@"WEMonthOverview"];
150 - (NSDictionary *)monthOverviewContext {
151 return [self objectForKey:@"WEMonthOverview"];
153 - (NSMutableArray *)monthOverviewQueryObjects {
154 return [[self monthOverviewContext] valueForKey:@"query"];
157 - (void)enableMonthOverviewInfoMode {
158 [self setObject:@"YES" forKey:WEMonthOverview_InfoMode];
160 - (void)disableMonthOverviewInfoMode {
161 [self removeObjectForKey:WEMonthOverview_InfoMode];
164 - (void)enableMonthOverviewContentMode {
165 [self setObject:@"YES" forKey:WEMonthOverview_ContentMode];
167 - (void)disableMonthOverviewContentMode {
168 [self removeObjectForKey:WEMonthOverview_ContentMode];
171 @end /* WOContext(WEMonthOverview) */
173 @implementation WEMonthOverview
175 static Class StrClass = nil;
178 if (StrClass == Nil) StrClass = [NSString class];
181 static NSString *retStrForInt(int i) {
193 case 10: return @"10";
194 // TODO: find useful count!
197 sprintf(buf, "%i", i);
198 return [[StrClass alloc] initWithCString:buf];
203 - (id)initWithName:(NSString *)_name
204 associations:(NSDictionary*)_config
205 template:(WOElement *)_t
207 if ((self = [super initWithName:_name associations:_config template:_t])) {
208 self->list = WOExtGetProperty(_config, @"list");
209 self->item = WOExtGetProperty(_config, @"item");
210 self->index = WOExtGetProperty(_config, @"index");
211 self->identifier = WOExtGetProperty(_config, @"identifier");
213 self->currentDay = WOExtGetProperty(_config, @"currentDay");
215 self->year = WOExtGetProperty(_config, @"year");
216 self->month = WOExtGetProperty(_config, @"month");
217 self->timeZone = WOExtGetProperty(_config, @"timeZone");
218 self->firstDay = WOExtGetProperty(_config, @"firstDay");
219 self->tableTags = WOExtGetProperty(_config, @"tableTags");
221 self->startDateKey = WOExtGetProperty(_config, @"startDateKey");
222 self->endDateKey = WOExtGetProperty(_config, @"endDateKey");
224 self->labelStyle = WOExtGetProperty(_config, @"labelStyle");
225 self->contentStyle = WOExtGetProperty(_config, @"contentStyle");
227 self->labelColor = WOExtGetProperty(_config, @"labelColor");
228 self->contentColor = WOExtGetProperty(_config, @"contentColor");
230 if (self->startDateKey == nil) {
232 [[WOAssociation associationWithValue:@"startDate"] retain];
234 if (self->endDateKey == nil) {
236 [[WOAssociation associationWithValue:@"endDate"] retain];
238 self->template = [_t retain];
243 - (void)resetMatrix {
246 for (i=0; i < MatrixSize; i++) {
247 [self->matrix[i] release];
248 self->matrix[i] = nil;
250 [self->matrixInfo.start release]; self->matrixInfo.start = nil;
254 [self->list release];
255 [self->item release];
256 [self->index release];
257 [self->identifier release];
258 [self->currentDay release];
259 [self->year release];
260 [self->month release];
261 [self->timeZone release];
262 [self->firstDay release];
263 [self->tableTags release];
265 [self->startDateKey release];
266 [self->endDateKey release];
267 [self->labelStyle release];
268 [self->contentStyle release];
269 [self->labelColor release];
270 [self->contentColor release];
274 [self->template release];
282 _applyIdentifier(WEMonthOverview *self, WOComponent *comp, NSString *_idx)
288 array = [self->list valueInComponent:comp];
289 count = [array count];
294 /* find subelement for unique id */
296 for (cnt = 0; cnt < count; cnt++) {
300 [self->index setUnsignedIntValue:cnt inComponent:comp];
303 [self->item setValue:[array objectAtIndex:cnt] inComponent:comp];
305 ident = [self->identifier stringValueInComponent:comp];
307 if ([ident isEqualToString:_idx]) {
308 /* found subelement with unique id */
314 @"WEMonthOverview: array did change, "
315 @"unique-id isn't contained."];
316 [self->item setValue:nil inComponent:comp];
317 [self->index setUnsignedIntValue:0 inComponent:comp];
321 _applyIndex(WEMonthOverview *self, WOComponent *comp, unsigned _idx)
326 array = [self->list valueInComponent:comp];
329 [self->index setUnsignedIntValue:_idx inComponent:comp];
331 if (self->item == nil)
334 count = [array count];
337 [self->item setValue:[array objectAtIndex:_idx] inComponent:comp];
342 @"WEMonthOverview: array did change, index is invalid."];
343 [self->item setValue:nil inComponent:comp];
344 [self->index setUnsignedIntValue:0 inComponent:comp];
349 _generateCell(WEMonthOverview *self, WOResponse *response,
350 WOContext *ctx, NSString *key, id value,
351 NSCalendarDate *dateId)
353 [ctx setupMonthOverviewContextWithValue:value forKey:key];
355 [ctx appendElementIDComponent:key];
360 s = retStrForInt([dateId timeIntervalSince1970]);
361 [ctx appendElementIDComponent:s];
365 [self->template appendToResponse:response inContext:ctx];
367 if (dateId) [ctx deleteLastElementIDComponent];
368 [ctx deleteLastElementIDComponent];
369 [ctx tearDownMonthOverviewContext];
373 _takeValuesInCell(WEMonthOverview *self, WORequest *request,
374 WOContext *ctx, NSString *key, id value)
376 [ctx setupMonthOverviewContextWithValue:value forKey:key];
378 [ctx appendElementIDComponent:key];
379 [self->template takeValuesFromRequest:request inContext:ctx];
380 [ctx deleteLastElementIDComponent];
381 // TODO: no teardown of context?
384 - (void)_calcMatrixInContext:(WOContext *)_ctx {
389 int m, y; // month, year
394 comp = [_ctx component];
395 array = [self->list valueInComponent:comp];
396 startKey = [self->startDateKey stringValueInComponent:comp];
397 endKey = [self->endDateKey stringValueInComponent:comp];
399 y = (self->year == nil)
400 ? [[NSCalendarDate calendarDate] yearOfCommonEra]
401 : [self->year intValueInComponent:comp];
403 m = (self->month == nil)
404 ? [[NSCalendarDate calendarDate] monthOfYear]
405 : [self->month intValueInComponent:comp];
409 NSCalendarDate *monthStart = nil;
410 NSCalendarDate *d = nil;
411 NSTimeZone *tz = nil;
412 int firstDisplayedDay, firstIdx;
415 tz = [self->timeZone valueInComponent:comp];
417 monthStart = [NSCalendarDate dateWithYear:y month:m day:1 hour:0 minute:0
418 second:0 timeZone:tz];
420 d = [monthStart dateByAddingYears:0 months:0 days:i];
422 while ([d monthOfYear] == m) {
424 d = [monthStart dateByAddingYears:0 months:0 days:i];
427 firstDisplayedDay = (self->firstDay != nil)
428 ? ([self->firstDay intValueInComponent:comp] % 7)
431 firstIdx = (([monthStart dayOfWeek] - firstDisplayedDay) + 7) % 7;
433 self->matrixInfo.weeks = ceil((float)(firstIdx + i) / 7);
434 self->matrixInfo.firstDisplayedDay = firstDisplayedDay;
436 // keep the timezone in the date
437 self->matrixInfo.start =
438 [[monthStart dateByAddingYears:0 months:0 days:-firstIdx] retain];
441 NSLog(@"MONTH START: %@", monthStart);
442 NSLog(@"MATRIX START: %@", self->matrixInfo.start);
447 * The 'matrix start' is the day the grid starts. Eg if we are displaying
448 * Feb 2008, Feb 1st does not start on Monday. The matrix looks like:
450 * Week Mon Tue Wed Thu Fri Sat Sun
451 * 5 28 29 30 31 1 2 3 [starts in January]
453 * 7 11 12 13 14 15 16 17
454 * 8 18 19 20 21 22 23 24
455 * 9 25 26 27 28 29 1 2 [goes into march]
457 * Hence the matrix start will be 2008-01-28 00:00 [TZ].
460 for (i = 0, cnt = [array count]; i < cnt; i++) {
462 NSCalendarDate *sd, *ed;
463 NSTimeInterval diff; // seconds since matrixInfo.start (1. 00:00)
467 app = [array objectAtIndex:i]; // appointment
468 sd = [app valueForKey:startKey]; // startDate
469 ed = [app valueForKey:endKey]; // endDate
471 if (sd == nil && ed == nil) continue;
474 diff = [sd timeIntervalSinceDate:self->matrixInfo.start];
475 idx = floor(diff / SecondsPerDay); /* eg 0 */
477 NSLog(@" DTSTART: %i (offset=%.3fs)", idx, diff);
480 if (idx >= 0 && idx < MatrixSize) {
481 if (self->matrix[idx] == nil)
482 self->matrix[idx] = [[NSMutableArray alloc] initWithCapacity:4];
484 [self->matrix[idx] addObject:[NSNumber numberWithInt:i]];
486 idx = (idx < 0) ? 0 : idx + 1;
489 diff = [ed timeIntervalSinceDate:self->matrixInfo.start];
490 idx2 = floor(diff / SecondsPerDay);
492 NSLog(@" DTEND: %i (offset=%.3fs)", idx2, diff);
494 idx2 = (idx2 > MatrixSize) ? MatrixSize : idx2; /* limit length */
496 /* Add event to all columns covered. we store the indices! */
497 iNum = [[NSNumber alloc] initWithInt:i];
498 for (; idx <= idx2; idx++) { /* inclusive! */
500 NSLog(@" ADD %i TO: %i", i, idx);
502 if (self->matrix[idx] == nil)
503 self->matrix[idx] = [[NSMutableArray alloc] initWithCapacity:4];
505 [self->matrix[idx] addObject:iNum];
507 [iNum release]; iNum = nil;
510 /* fill up remaining slots with empty arrays */
511 for (i = 0; i < MatrixSize; i++) {
512 if (self->matrix[i] == nil)
513 self->matrix[i] = [[NSArray alloc] init];
517 - (void)appendContentToResponse:(WOResponse *)_response
518 inContext:(WOContext *)_ctx
524 int i, cnt, idx, count;
526 comp = [_ctx component];
527 array = [self->list valueInComponent:comp];
528 count = [array count];
530 // *** append day info
531 [_ctx enableMonthOverviewInfoMode];
532 [_ctx appendElementIDComponent:@"i"];
533 [self->template appendToResponse:_response inContext:_ctx];
534 [_ctx deleteLastElementIDComponent];
535 [_ctx disableMonthOverviewInfoMode];
537 // *** append day content
538 [_ctx enableMonthOverviewContentMode];
539 [_ctx appendElementIDComponent:@"c"]; // append content mode
540 for (i = 0, cnt = [self->matrix[_idx] count]; i < cnt; i++) {
543 idx = [[self->matrix[_idx] objectAtIndex:i] intValue];
546 NSLog(@"Warning! WEMonthOverview: index out of range");
549 app = [array objectAtIndex:idx];
551 if ([self->item isValueSettable])
552 [self->item setValue:app inComponent:comp];
553 if ([self->index isValueSettable])
554 [self->index setIntValue:idx inComponent:comp];
556 if (self->identifier == nil) {
557 s = retStrForInt(idx);
558 [_ctx appendElementIDComponent:s];
562 s = [self->identifier stringValueInComponent:comp];
563 [_ctx appendElementIDComponent:s];
566 [self->template appendToResponse:_response inContext:_ctx];
567 [_ctx deleteLastElementIDComponent];
569 [_ctx deleteLastElementIDComponent]; // delete content mode
570 [_ctx disableMonthOverviewContentMode];
573 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
579 BOOL renderDefaults = NO;
582 BOOL hasLeftTop = NO;
585 BOOL hasRightTop = NO;
587 BOOL hasLeftBottom = NO;
589 BOOL hasRightBottom = NO;
592 if ([_ctx isRenderingDisabled]) {
593 [self->template appendToResponse:_response inContext:_ctx];
597 [self _calcMatrixInContext:_ctx];
599 comp = [_ctx component];
601 useTableTags = (self->tableTags)
602 ? [self->tableTags boolValueInComponent:comp]
605 { // query mode ... testing orientations
606 NSEnumerator *queryE;
609 // only query mode .. no value setting
610 [_ctx setupMonthOverviewContextForQueryMode];
612 /* this walks over all subelements and collects 'query' info */
614 [self->template appendToResponse:_response inContext:_ctx];
616 /* now process the results */
618 queryE = [[_ctx monthOverviewQueryObjects] objectEnumerator];
620 while ((orient = [queryE nextObject])) {
621 if ((!hasHeader) && ([orient isEqualToString:@"header"]))
623 if ((!hasCell) && ([orient isEqualToString:@"cell"]))
625 if ((!hasTitle) && ([orient isEqualToString:@"title"]))
627 if ((!hasLeftTop) && ([orient isEqualToString:@"left/top"]))
629 if ((!hasLeftBottom) && ([orient isEqualToString:@"left/bottom"]))
631 if ((!hasLeft) && ([orient isEqualToString:@"left"]))
633 if ((!hasTop) && ([orient isEqualToString:@"top"]))
635 if ((!hasRightTop) && ([orient isEqualToString:@"right/top"]))
637 if ((!hasRight) && ([orient isEqualToString:@"right"]))
639 if ((!hasRightBottom) && ([orient isEqualToString:@"right/bottom"]))
640 hasRightBottom = YES;
641 if ((!hasBottom) && ([orient isEqualToString:@"bottom"]))
645 if (!(hasLeft || hasRight || hasTop || hasBottom))
646 renderDefaults = YES;
648 [_ctx tearDownMonthOverviewContext];
653 [_response appendContentString:@"<table"];
654 [self appendExtraAttributesToResponse:_response inContext:_ctx];
655 [_response appendContentString:@">"];
658 /* generating head */
663 if ((hasLeft) || (hasLeftTop) || (hasLeftBottom))
665 if ((hasRight) || (hasRightTop) || (hasRightBottom))
668 [_response appendContentString:@"<tr>"];
670 s = retStrForInt(width);
671 _generateCell(self, _response, _ctx, @"header", s, nil);
674 [_response appendContentString:@"</tr>"];
678 if ((hasTop) || (hasLeftTop) || (hasRightTop) || (renderDefaults)) {
679 [_response appendContentString:@"<tr>"];
682 _generateCell(self, _response, _ctx, @"left/top", @"--", nil);
683 else if (hasLeft || hasLeftBottom || renderDefaults)
684 [_response appendContentString:@"<td> </td>"];
689 dow = self->matrixInfo.firstDisplayedDay;
690 for (i = 0; i < 7; i++) {
691 _generateCell(self, _response, _ctx, @"top",
692 [[NSNumber numberWithInt:dow] stringValue], nil);
693 dow = (dow == 6) ? 0 : dow+1;
696 else if (renderDefaults) {
700 day = self->matrixInfo.start;
701 for (i = 0; i < 7; i++) {
704 [_response appendContentString:@"<td align=\"center\""];
705 if ((style = [self->labelStyle stringValueInComponent:comp])) {
706 [_response appendContentString:@" class=\""];
707 [_response appendContentHTMLAttributeValue:style];
708 [_response appendContentCharacter:'"'];
710 if ((bgcolor = [self->labelColor stringValueInComponent:comp])) {
711 [_response appendContentString:@" bgcolor=\""];
712 [_response appendContentString:bgcolor];
713 [_response appendContentCharacter:'"'];
715 [_response appendContentString:@"><b>"];
716 /* TODO: replace with manual string */
717 s = [day descriptionWithCalendarFormat:@"%A"];
718 [_response appendContentString:s];
719 [_response appendContentString:@"</b></td>"];
720 day = [day tomorrow];
723 else if (hasRightTop || hasLeftTop) {
724 [_response appendContentString:
725 @"<td></td><td></td><td></td><td></td>"
726 @"<td></td><td></td><td></td>"];
730 _generateCell(self, _response, _ctx, @"right/top", @"--", nil);
731 else if (hasRightBottom || hasRight)
732 [_response appendContentString:@"<td> </td>"];
734 [_response appendContentString:@"</tr>"];
737 /* generating content */
741 int i, j, maxNumberOfWeeks;
743 day = self->matrixInfo.start;
744 maxNumberOfWeeks = [day numberOfWeeksInYear];
747 retStrForInt([[day dateByAddingYears:0 months:0 days:3] weekOfYear]);
749 for (i = 0; i < self->matrixInfo.weeks; i++) {
750 [_response appendContentString:@"<tr>"];
753 _generateCell(self, _response, _ctx, @"left", week, nil);
755 else if (renderDefaults) {
756 [_response appendContentString:@"<td width=\"2%\" align=\"center\""];
757 if ((style = [self->labelStyle stringValueInComponent:comp])) {
758 [_response appendContentString:@" class=\""];
759 [_response appendContentHTMLAttributeValue:style];
760 [_response appendContentCharacter:'"'];
762 if ((bgcolor = [self->labelColor stringValueInComponent:comp])) {
763 [_response appendContentString:@" bgcolor=\""];
764 [_response appendContentString:bgcolor];
765 [_response appendContentCharacter:'"'];
767 [_response appendContentCharacter:'>'];
768 [_response appendContentString:week];
769 [_response appendContentString:@"</td>"];
771 else if (hasLeftTop || hasLeftBottom)
772 [_response appendContentString:@"<td> </td>"];
774 /* append days of week */
775 for (j = 0; j < 7; j++) {
778 if ([self->currentDay isValueSettable])
779 [self->currentDay setValue:day inComponent:comp];
781 [_response appendContentString:@"<td"];
782 if ((style = [self->contentStyle stringValueInComponent:comp])) {
783 [_response appendContentString:@" class=\""];
784 [_response appendContentHTMLAttributeValue:style];
785 [_response appendContentCharacter:'"'];
787 if ((bgcolor = [self->contentColor stringValueInComponent:comp])) {
788 [_response appendContentString:@" bgcolor=\""];
789 [_response appendContentString:bgcolor];
790 [_response appendContentCharacter:'"'];
792 [_response appendContentCharacter:'>'];
795 [_response appendContentString:
796 @"<table border='0' height='100%' cellspacing='0'"
797 @" cellpadding='2' width='100%'><tr>"];
800 _generateCell(self, _response, _ctx, @"title", @"--", day);
802 [_response appendContentString:
803 @"<td valign=\"top\">"
804 @"<font size=\"4\" color=\"black\">"
806 s = retStrForInt([day dayOfMonth]);
807 [_response appendContentString:s];
809 [_response appendContentString:@"</u></font></td>"];
813 /*** appending content ***/
814 [_ctx appendElementIDComponent:@"cell"];
815 s = retStrForInt([day timeIntervalSince1970]);
816 [_ctx appendElementIDComponent:s];
818 [_response appendContentString:@"<td valign=\"top\">"];
819 [self appendContentToResponse:_response inContext:_ctx index:(i*7+j)];
820 [_response appendContentString:@"</td>"];
821 [_ctx deleteLastElementIDComponent]; // delete date
822 [_ctx deleteLastElementIDComponent]; // delete "cell"
824 [_response appendContentString:@"</tr></table></td>"];
825 day = [day tomorrow];
829 _generateCell(self, _response, _ctx, @"right", week, nil);
830 else if (hasRightTop || hasRightBottom)
831 [_response appendContentString:@"<td> </td>"];
833 [_response appendContentString:@"</tr>"];
837 nextWeek = ([week intValue] % maxNumberOfWeeks) + 1;
838 [week release]; week = nil;
839 week = retStrForInt(nextWeek);
842 [week release]; week = nil;
845 /* generating footer */
846 if ((hasBottom) || (hasLeftBottom) || (hasRightBottom)) {
848 [_response appendContentString:@"<tr>"];
851 _generateCell(self, _response, _ctx, @"left/bottom", @"--", nil);
852 else if (hasLeft || hasLeftTop)
853 [_response appendContentString:@"<td> </td>"];
856 int i, dow = 0; // dayOfWeek
858 dow = self->matrixInfo.firstDisplayedDay;
860 for (i = 0; i < 7; i++) {
863 s = retStrForInt(dow);
864 _generateCell(self, _response, _ctx, @"bottom", s, nil);
866 dow = (dow == 6) ? 0 : dow + 1;
870 [_response appendContentString:
871 @"<td></td><td></td><td></td><td></td>"
872 @"<td></td><td></td><td></td>"];
876 _generateCell(self, _response, _ctx, @"right/bottom", @"--", nil);
877 else if (hasRightTop || hasRight)
878 [_response appendContentString:@"<td> </td>"];
880 [_response appendContentString:@"</tr>"];
885 [_response appendContentString:@"</table>"];
891 - (void)takeContentValues:(WORequest *)_req inContext:(WOContext *)_ctx
899 comp = [_ctx component];
900 array = [self->list valueInComponent:comp];
901 count = [array count];
903 [_ctx appendElementIDComponent:@"c"]; // append content mode
905 for (i = 0, cnt = [self->matrix[_idx] count]; i < cnt; i++) {
908 idx = [[self->matrix[_idx] objectAtIndex:i] intValue];
910 if (self->index != nil)
911 [self->index setUnsignedIntValue:idx inComponent:comp];
912 if (self->item != nil)
913 [self->item setValue:[array objectAtIndex:idx] inComponent:comp];
915 s = (self->identifier != nil)
916 ? [[self->identifier stringValueInComponent:comp] retain]
917 : (id)retStrForInt(idx);
919 [_ctx appendElementIDComponent:s]; // append index-id
922 [self->template takeValuesFromRequest:_req inContext:_ctx];
923 [_ctx deleteLastElementIDComponent]; // delte index-id
927 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
928 WOComponent *sComponent;
932 unsigned int weekOfYear;
935 [self _calcMatrixInContext:_ctx];
937 sComponent = [_ctx component];
939 day = self->matrixInfo.start;
941 weekOfYear = [[day dateByAddingYears:0 months:0 days:3] weekOfYear];
942 sprintf(buf, "%d", weekOfYear);
943 week = [StrClass stringWithCString:buf];
945 // TODO: weird use of NSString for week?
946 for (i = 0; i < self->matrixInfo.weeks; i++) {
947 for (j = 0; j < 7; j++) {
950 if ([self->currentDay isValueSettable])
951 [self->currentDay setValue:day inComponent:sComponent];
953 sprintf(buf, "%d", (unsigned)[day timeIntervalSince1970]);
954 eid = [[StrClass alloc] initWithCString:buf];
955 [_ctx appendElementIDComponent:eid];
958 _takeValuesInCell(self, _req, _ctx, @"title", @"--");
959 [self takeContentValues:_req inContext:_ctx index:(i * 7 + j)];
960 [_ctx deleteLastElementIDComponent];
962 day = [day tomorrow];
964 sprintf(buf, "%d", ([week intValue] + 1));
965 week = [StrClass stringWithCString:buf];
971 - (id)invokeContentAction:(WORequest *)_request inContext:(WOContext *)_ctx{
973 NSString *idxId = nil;
975 if ((idxId = [_ctx currentElementID]) == 0) // no content nor info mode
978 [_ctx consumeElementID]; // consume mode
979 [_ctx appendElementIDComponent:idxId]; // append mode ("c" or "i")
981 if ([idxId isEqualToString:@"i"])
983 result = [self->template invokeActionForRequest:_request inContext:_ctx];
984 else if ((idxId = [_ctx currentElementID])) {
986 [_ctx consumeElementID]; // consume index-id
987 [_ctx appendElementIDComponent:idxId];
989 if (self->identifier)
990 _applyIdentifier(self, [_ctx component], idxId);
992 _applyIndex(self, [_ctx component], [idxId intValue]);
994 result = [self->template invokeActionForRequest:_request inContext:_ctx];
996 [_ctx deleteLastElementIDComponent]; // delete index-id
998 [_ctx deleteLastElementIDComponent]; // delete mode
1003 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
1004 WOComponent *sComponent;
1009 sComponent = [_ctx component];
1011 if ((orient = [_ctx currentElementID]) == nil) {
1013 logWithFormat:@"%@: MISSING ORIENTATION ID in URL !", self];
1017 [_ctx consumeElementID];
1018 [_ctx appendElementIDComponent:orient];
1020 [_ctx setupMonthOverviewContextWithOrientation:orient];
1022 if ([orient isEqualToString:@"cell"] || [orient isEqualToString:@"title"]){
1023 /* content or 'title' */
1024 if ((ident = [_ctx currentElementID]) != nil) {
1025 NSCalendarDate *day;
1028 [_ctx consumeElementID]; // consume date-id
1029 [_ctx appendElementIDComponent:ident];
1031 ti = [ident intValue];
1033 day = [NSCalendarDate dateWithTimeIntervalSince1970:ti];
1034 [day setTimeZone:[self->timeZone valueInComponent:sComponent]];
1036 if ([self->currentDay isValueSettable])
1037 [self->currentDay setValue:day inComponent:sComponent];
1039 if ([orient isEqualToString:@"title"])
1040 result = [self->template invokeActionForRequest:_req inContext:_ctx];
1042 result = [self invokeContentAction:_req inContext:_ctx];
1044 [_ctx deleteLastElementIDComponent]; // delete 'cell' or 'title'
1048 logWithFormat:@"%@: MISSING DATE ID in '%@' URL !", self, orient];
1051 /* neither 'cell' nor 'title' (some label) */
1052 result = [self->template invokeActionForRequest:_req inContext:_ctx];
1054 [_ctx deleteLastElementIDComponent]; /* delete orient */
1056 // TODO: no teardown of month-overview context?
1061 @end /* WEMonthOverview */
1064 @implementation WEMonthLabel
1066 - (id)initWithName:(NSString *)_name
1067 associations:(NSDictionary*)_config
1068 template:(WOElement *)_t
1070 if ((self = [super initWithName:_name associations:_config template:_t])) {
1071 self->orientation = WOExtGetProperty(_config, @"orientation");
1072 self->dayOfWeek = WOExtGetProperty(_config, @"dayOfWeek");
1073 self->weekOfYear = WOExtGetProperty(_config, @"weekOfYear");
1074 self->colspan = WOExtGetProperty(_config, @"colspan");
1076 self->template = [_t retain];
1082 [self->orientation release];
1083 [self->dayOfWeek release];
1084 [self->weekOfYear release];
1085 [self->colspan release];
1087 [self->template release];
1091 /* handle requests */
1093 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
1094 NSDictionary *monthViewContextDict;
1099 orient = [self->orientation valueInComponent:[_ctx component]];
1100 isEdge = ([orient rangeOfString:@"/"].length > 0);
1102 monthViewContextDict = [_ctx monthOverviewContext];
1103 if ((tmp = [monthViewContextDict objectForKey:orient]) == nil)
1107 [_ctx appendElementIDComponent:orient];
1108 [self->template takeValuesFromRequest:_req inContext:_ctx];
1109 [_ctx deleteLastElementIDComponent];
1112 [self->template takeValuesFromRequest:_req inContext:_ctx];
1115 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
1116 NSDictionary *monthViewContextDict;
1122 orient = [self->orientation valueInComponent:[_ctx component]];
1123 isEdge = ([orient rangeOfString:@"/"].length > 0);
1125 monthViewContextDict = [_ctx monthOverviewContext];
1126 if ((tmp = [monthViewContextDict objectForKey:orient]) == nil)
1130 return [self->template invokeActionForRequest:_req inContext:_ctx];
1132 tmp = [_ctx currentElementID];
1133 [_ctx consumeElementID];
1134 [_ctx appendElementIDComponent:tmp];
1136 if ([orient isEqualToString:@"top"] ||
1137 [orient isEqualToString:@"bottom"]) {
1138 [self->dayOfWeek setIntValue:[tmp intValue]
1139 inComponent:[_ctx component]];
1141 else if ([orient isEqualToString:@"left"] ||
1142 [orient isEqualToString:@"right"]) {
1143 [self->weekOfYear setIntValue:[tmp intValue]
1144 inComponent:[_ctx component]];
1146 else if ([orient isEqualToString:@"header"]) {
1147 [self->colspan setIntValue:[tmp intValue]
1148 inComponent:[_ctx component]];
1151 result = [self->template invokeActionForRequest:_req inContext:_ctx];
1153 [_ctx deleteLastElementIDComponent];
1157 /* generate response */
1159 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
1160 NSDictionary *monthViewContextDict;
1161 NSMutableArray *queryContext;
1167 if ([_ctx isRenderingDisabled]) {
1168 [self->template appendToResponse:_response inContext:_ctx];
1172 orient = [self->orientation stringValueInComponent:[_ctx component]];
1173 isEdge = ([orient rangeOfString:@"/"].length > 0);
1175 if (orient == nil) return;
1177 if ((queryContext = [_ctx monthOverviewQueryObjects]) != nil) {
1178 [queryContext addObject:orient];
1182 monthViewContextDict = [_ctx monthOverviewContext];
1183 if ((tmp = [monthViewContextDict objectForKey:orient]) == nil)
1190 orientIntValue = [tmp intValue];
1191 if ([orient isEqualToString:@"top"] ||
1192 [orient isEqualToString:@"bottom"]) {
1193 [self->dayOfWeek setIntValue:orientIntValue
1194 inComponent:[_ctx component]];
1196 else if ([orient isEqualToString:@"left"] ||
1197 [orient isEqualToString:@"right"]) {
1198 [self->weekOfYear setIntValue:orientIntValue
1199 inComponent:[_ctx component]];
1201 else if ([orient isEqualToString:@"header"]) {
1202 [self->colspan setIntValue:orientIntValue
1203 inComponent:[_ctx component]];
1204 cols = [tmp intValue];
1208 [_response appendContentString:@"<td"];
1213 colStr = retStrForInt(cols);
1214 [_response appendContentString:@" colspan=\""];
1215 [_response appendContentString:colStr];
1216 [_response appendContentString:@"\""];
1220 [self appendExtraAttributesToResponse:_response inContext:_ctx];
1221 [_response appendContentString:@">"];
1224 [_ctx appendElementIDComponent:[tmp stringValue]];
1226 [self->template appendToResponse:_response inContext:_ctx];
1229 [_ctx deleteLastElementIDComponent];
1231 // close table data tag
1232 [_response appendContentString:@"</td>"];
1235 @end /* WEMonthLabel */
1238 @implementation WEMonthTitle
1240 - (id)initWithName:(NSString *)_name
1241 associations:(NSDictionary*)_config
1242 template:(WOElement *)_t
1244 if ((self = [super initWithName:_name associations:_config template:_t])) {
1245 self->template = [_t retain];
1251 [self->template release];
1255 /* handling requests */
1257 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
1258 NSDictionary *monthViewContextDict;
1261 monthViewContextDict = [_ctx monthOverviewContext];
1262 if ((tmp = [monthViewContextDict objectForKey:@"title"]) != nil)
1263 [self->template takeValuesFromRequest:_req inContext:_ctx];
1266 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
1267 NSDictionary *monthViewContextDict;
1270 monthViewContextDict = [_ctx monthOverviewContext];
1271 if ((tmp = [monthViewContextDict objectForKey:@"title"]) != nil)
1272 return [self->template invokeActionForRequest:_req inContext:_ctx];
1277 /* generating response */
1279 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
1280 NSDictionary *monthViewContextDict;
1283 if ([_ctx isRenderingDisabled]) {
1284 [self->template appendToResponse:_response inContext:_ctx];
1288 if ((tmp = [_ctx monthOverviewQueryObjects]) != nil) {
1289 [(NSMutableArray *)tmp addObject:@"title"];
1293 monthViewContextDict = [_ctx monthOverviewContext];
1294 if ((tmp = [monthViewContextDict objectForKey:@"title"]) != nil) {
1295 // append table date, forwarding extra attributes
1296 [_response appendContentString:@"<td"];
1297 [self appendExtraAttributesToResponse:_response inContext:_ctx];
1298 [_response appendContentString:@">"];
1300 [self->template appendToResponse:_response inContext:_ctx];
1301 // close table data tag
1302 [_response appendContentString:@"</td>"];
1306 @end /* WEMonthTitle */
1309 @interface WEMonthOverviewInfoMode : WEContextConditional
1312 @implementation WEMonthOverviewInfoMode
1314 - (NSString *)_contextKey {
1315 return WEMonthOverview_InfoMode;
1318 @end /* WEMonthOverviewInfoMode */
1321 @interface WEMonthOverviewContentMode : WEContextConditional
1324 @implementation WEMonthOverviewContentMode
1326 - (NSString *)_contextKey {
1327 return WEMonthOverview_ContentMode;
1330 @end /* WEMonthOverviewContentMode */