]> err.no Git - sope/blob - sope-appserver/WEExtensions/WEWeekOverview.m
added strict OSX bundle dependencies
[sope] / sope-appserver / WEExtensions / WEWeekOverview.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "WEContextConditional.h"
23 #include <NGObjWeb/WODynamicElement.h>
24
25 /*
26
27   renders this:
28   _______________________________
29   | MO | TU | WE | TH | FR | SA |
30   |-----------------------------|
31   |    |    |    |    |    | 10 |
32   |  0 |  2 |  4 |  6 |  8 | 11 |
33   |____|____|____|____|____|____|
34   |    |    |    |    |    | SU |
35   |    |    |    |    |    |----|
36   |    |    |    |    |    | 12 |
37   |  1 |  3 |  5 |  7 |  9 | 13 |
38   |____|____|____|____|____|____|
39
40   Would be a nice addition:
41   |a   |b   |c   |d   |e   |f   |
42   -------------------------------
43   |a   |b   |c   |d   |e   |f   |
44   -------------------------------
45   footerRow = 0 / footerRowList = ( 0, 1, 2, 3, 4 );
46   headerRow = 0 / headerRowList = ( 0, 1, 2, 3, 4 );
47   startDate/endDate
48   
49   the numbers corresponde to the indxes in the matrix (self->matrix[])
50
51   Note: it does support additional header and footer rows which can be used
52         to generate content above and below the view itself.
53
54   Usage:
55     WeekOverview: WEWeekOverview {
56       list       = list;
57       item       = item;
58       weekStart  = weekStart;
59       
60       titleStyle   = "weekview_title";
61       contentStyle = "weekview_content";
62     }
63     
64     TitleMode:   WEWeekOverviewTitleMode   {};
65     InfoMode:    WEWeekOverviewInfoMode    {};
66     ContentMode: WEWeekOverviewContentMode {};
67 */
68
69 // TODO: allow stylesheet classes!
70
71 #define SecondsPerWeek (7 * 24 * 60 * 60)
72 #define SecondsPerDay      (24 * 60 * 60)
73
74 @class NSMutableArray;
75
76 #define WEWeekOverview_TitleMode         @"WEWeekOverview_TitleMode"
77 #define WEWeekOverview_TitleModeDidMatch @"WEWeekOverview_TitleModeDidMatch"
78 #define WEWeekOverview_InfoMode          @"WEWeekOverview_InfoMode"
79 #define WEWeekOverview_PMInfoMode        @"WEWeekOverview_PMInfoMode"
80 #define WEWeekOverview_ContentMode       @"WEWeekOverview_ContentMode"
81
82 #define WEWeekOverview_FooterRowMode     @"WEWeekOverview_FooterRowMode"
83 #define WEWeekOverview_HeaderRowMode     @"WEWeekOverview_HeaderRowMode"
84
85 @interface WEWeekOverview : WODynamicElement
86 {
87 @protected
88   WOAssociation  *list;
89   WOAssociation  *item;
90   WOAssociation  *index;
91   WOAssociation  *identifier;
92   
93   WOAssociation  *dayIndex; // 0=firstDay, 1=secondDay, ..., 6=seventh day
94   WOAssociation  *weekStart;
95   
96   WOAssociation  *startDateKey;
97   WOAssociation  *endDateKey;
98   
99   WOAssociation  *titleStyle;
100   WOAssociation  *contentStyle;
101   WOAssociation  *titleColor;   // DEPRECATED
102   WOAssociation  *contentColor; // DEPRECATED
103   
104   WOAssociation  *width;
105   WOAssociation  *border;
106   WOAssociation  *cellpadding;
107   WOAssociation  *cellspacing;
108
109   WOAssociation  *footerRows; // list of elements for footer rows
110   WOAssociation  *footerRow;  // current element in footer row
111   WOAssociation  *headerRows; // list of elements for header rows
112   WOAssociation  *headerRow;  // current element in header row
113   WOAssociation  *colIndex;   // for header/footer row
114   
115   WOAssociation  *isInfoItem; // is current item info entry
116   WOAssociation  *infoItems;  // current info entries
117   
118   WOAssociation  *hideWeekend; /* should Sa/So be rendered? */
119
120 @private
121   NSMutableArray *matrix[14];
122   NSMutableArray *infos[14];
123   BOOL           hasOwnTitle;
124   
125   WOElement      *template;
126 }
127 @end
128
129 #include <math.h> /* Needed for floor() */
130 #include "common.h"
131
132 @interface WOContext(WEWeekOverview)
133
134 - (void)appendWEIntElementID:(int)_intID;
135
136 - (void)enterWEWOMode:(NSString *)_mode elementID:(NSString *)_eid;
137 - (void)leaveWEWOMode:(NSString *)_mode;
138
139 @end
140
141 static NSString *retStrForInt(int i);
142 static NSString *WEInfoElementID = @"i";
143
144 @implementation WOContext(WEWeekOverview)
145
146 - (void)appendWEIntElementID:(int)_intID {
147   NSString *s;
148   // TODO: could make the number switch in here?
149   
150   s = retStrForInt(_intID);
151   [self appendElementIDComponent:s];
152   [s release];
153 }
154
155 - (void)enterWEWOMode:(NSString *)_mode elementID:(NSString *)_eid {
156   [self setObject:@"YES" forKey:_mode];
157   [self appendElementIDComponent:_eid];
158 }
159 - (void)leaveWEWOMode:(NSString *)_mode {
160   [self deleteLastElementIDComponent]; // delete eid
161   [self removeObjectForKey:_mode];
162 }
163
164 @end /* WOContext(WEWeekOverview) */
165
166 @implementation WEWeekOverview
167
168 // premature: don't know a "good" count
169 static NSNumber *smap[10] = { nil,nil,nil,nil,nil,nil,nil,nil,nil,nil };
170 static Class    NumClass  = Nil;
171 static Class    StrClass  = Nil;
172 static NSArray  *emptyArray = nil;
173
174 + (void)initialize {
175   static BOOL didInit = NO;
176   int i;
177   if (didInit) return;
178   didInit = YES;
179   
180   StrClass = [NSString class];
181   NumClass = [NSNumber class];
182   for (i = 0; i < 10; i++)
183     smap[i] = [[NumClass numberWithInt:i] retain];
184   
185   if (emptyArray == nil) emptyArray = [[NSArray alloc] init];
186 }
187
188 static NSNumber *numForInt(int i) {
189   if (i >= 0 && i < 10)
190     return smap[i];
191   return [NumClass numberWithInt:i];
192 }
193 static NSString *retStrForInt(int i) {
194   switch(i) {
195   case 0:  return @"0";
196   case 1:  return @"1";
197   case 2:  return @"2";
198   case 3:  return @"3";
199   case 4:  return @"4";
200   case 5:  return @"5";
201   case 6:  return @"6";
202   case 7:  return @"7";
203   case 8:  return @"8";
204   case 9:  return @"9";
205   case 10: return @"10";
206     // TODO: find useful count!
207   default: {
208     char buf[16];
209     sprintf(buf, "%i", i);
210     return [[StrClass alloc] initWithCString:buf];
211   }
212   }
213 }
214
215 - (id)initWithName:(NSString *)_name
216   associations:(NSDictionary *)_config
217   template:(WOElement *)_tmp
218 {
219   if ((self = [super initWithName:_name associations:_config template:_tmp])) {
220     self->list         = WOExtGetProperty(_config, @"list");
221     self->item         = WOExtGetProperty(_config, @"item");
222     self->index        = WOExtGetProperty(_config, @"index");
223     self->identifier   = WOExtGetProperty(_config, @"identifier");
224     self->dayIndex     = WOExtGetProperty(_config, @"dayIndex");
225     self->weekStart    = WOExtGetProperty(_config, @"weekStart");
226     self->startDateKey = WOExtGetProperty(_config, @"startDateKey");
227     self->endDateKey   = WOExtGetProperty(_config, @"endDateKey");
228     self->hideWeekend  = WOExtGetProperty(_config, @"hideWeekend");
229
230     self->titleColor   = WOExtGetProperty(_config, @"titleColor");
231     self->titleStyle   = WOExtGetProperty(_config, @"titleStyle");
232     self->contentColor = WOExtGetProperty(_config, @"contentColor");
233     self->contentStyle = WOExtGetProperty(_config, @"contentStyle");
234     
235     self->width        = WOExtGetProperty(_config, @"width");
236     self->border       = WOExtGetProperty(_config, @"border");
237     self->cellspacing  = WOExtGetProperty(_config, @"cellspacing");
238     self->cellpadding  = WOExtGetProperty(_config, @"cellpadding");
239
240     self->headerRows   = WOExtGetProperty(_config, @"headerRows");
241     self->headerRow    = WOExtGetProperty(_config, @"headerRow");
242     self->footerRows   = WOExtGetProperty(_config, @"footerRows");
243     self->footerRow    = WOExtGetProperty(_config, @"footerRow");
244     self->colIndex     = WOExtGetProperty(_config, @"columnIndex");
245
246     
247     if (self->startDateKey == nil) {
248       self->startDateKey = 
249         [[WOAssociation associationWithValue:@"startDate"] retain];
250     }
251
252     if (self->endDateKey == nil) {
253       self->endDateKey = 
254         [[WOAssociation associationWithValue:@"endDate"] retain];
255     }
256     
257     if (self->width == nil)
258       self->width = [[WOAssociation associationWithValue:@"100%"] retain];
259     if (self->border == nil)
260       self->border = [[WOAssociation associationWithValue:@"0"] retain];
261     if (self->cellspacing == nil)
262       self->cellspacing = [[WOAssociation associationWithValue:@"2"] retain];
263     if (self->cellpadding == nil)
264       self->cellpadding = [[WOAssociation associationWithValue:@"5"] retain];
265     
266     self->isInfoItem = WOExtGetProperty(_config, @"isInfoItem");
267     self->infoItems  = WOExtGetProperty(_config, @"infoItems");
268
269     self->template = [_tmp retain];    
270   }
271   return self;
272 }
273
274 - (void)resetMatrix {
275   int i;
276   
277   for (i = 0; i < 14; i++) {
278     [self->matrix[i] release]; self->matrix[i] = nil;
279     [self->infos[i]  release]; self->infos[i]  = nil;
280   }
281 }
282
283 - (void)dealloc {
284   [self->hideWeekend  release];
285   [self->list         release];
286   [self->item         release];
287   [self->index        release];
288   [self->identifier   release];
289   [self->dayIndex     release];
290   [self->weekStart    release];
291   [self->startDateKey release];
292   [self->endDateKey   release];
293   [self->titleColor   release];
294   [self->titleStyle   release];
295   [self->contentColor release];
296   [self->contentStyle release];
297   [self->width        release];
298   [self->border       release];
299   [self->cellpadding  release];
300   [self->cellspacing  release];
301   [self->headerRows   release];
302   [self->headerRow    release];
303   [self->footerRows   release];
304   [self->footerRow    release];
305   [self->colIndex     release];
306   [self->infoItems    release];
307   [self->isInfoItem   release];
308
309   [self resetMatrix];
310
311   [self->template release];
312   
313   [super dealloc];
314 }
315
316 static inline void
317 _applyIdentifier(WEWeekOverview *self, WOComponent *comp, NSString *_idx)
318 {
319   NSArray *array;
320   unsigned count, cnt;
321
322   array = [self->list valueInComponent:comp];
323   count = [array count];
324
325   if (count == 0)
326     return;
327     
328   /* find subelement for unique id */
329     
330   for (cnt = 0; cnt < count; cnt++) {
331     NSString *ident;
332       
333     if (self->index)
334       [self->index setUnsignedIntValue:cnt inComponent:comp];
335
336     if (self->item)
337       [self->item setValue:[array objectAtIndex:cnt] inComponent:comp];
338
339     ident = [self->identifier stringValueInComponent:comp];
340
341     if ([ident isEqualToString:_idx]) {
342       /* found subelement with unique id */
343       return;
344     }
345   }
346     
347   [comp logWithFormat:
348           @"WEWeekOverview: array did change, "
349           @"unique-id isn't contained."];
350   [self->item  setValue:nil          inComponent:comp];
351   [self->index setUnsignedIntValue:0 inComponent:comp];
352 }
353
354 static inline void
355 _applyIndex(WEWeekOverview *self, WOComponent *comp, unsigned _idx)
356 {
357   NSArray  *array;
358   unsigned count;
359   
360   array = [self->list valueInComponent:comp];
361   
362   if (self->index)
363     [self->index setUnsignedIntValue:_idx inComponent:comp];
364
365   if (self->item == nil)
366     return;
367   
368   count = [array count];
369   if (_idx < count) {
370     [self->item setValue:[array objectAtIndex:_idx] inComponent:comp];
371     return;
372   }
373
374   [comp logWithFormat:
375             @"WEWeekOverview: array did change, index is invalid."];
376   [self->item  setValue:nil          inComponent:comp];
377   [self->index setUnsignedIntValue:0 inComponent:comp];
378 }
379
380 - (void)_calcMatrixInContext:(WOContext *)_ctx {
381   WOComponent    *comp;
382   NSCalendarDate *startWeek;
383   NSCalendarDate *endWeek;
384   NSArray        *array;
385   NSString       *startKey;
386   NSString       *endKey;
387   int            i, idx, idx2, cnt;
388   
389   [self resetMatrix];
390
391   [_ctx removeObjectForKey:WEWeekOverview_TitleModeDidMatch];
392   [_ctx setObject:@"YES" forKey:WEWeekOverview_TitleMode];
393   [self->template appendToResponse:nil inContext:_ctx];
394   [_ctx removeObjectForKey:WEWeekOverview_TitleMode];
395   
396   if ([_ctx objectForKey:WEWeekOverview_TitleModeDidMatch] != nil)
397     self->hasOwnTitle = YES;
398   else
399     self->hasOwnTitle = NO;
400   
401   comp      = [_ctx component];
402   array     = [self->list valueInComponent:comp];
403   startKey  = [self->startDateKey stringValueInComponent:comp];
404   endKey    = [self->endDateKey   stringValueInComponent:comp];
405   startWeek = (self->weekStart)
406     ? [self->weekStart valueInComponent:comp]
407     : [NSCalendarDate calendarDate];
408   endWeek   = [startWeek addTimeInterval:SecondsPerWeek];
409   
410   for (i = 0, cnt = [array count]; i < cnt; i++) {
411     id             app;
412     NSCalendarDate *sd, *ed;
413     NSTimeInterval diff;
414     BOOL           isInfo;
415     
416     app = [array objectAtIndex:i];
417 #if 0 // hh: so muesste es eigentlich sein: !!!
418     [self->item setValue:app inComponent:comp];
419     
420     sd = [self->startDate valueInComponent:comp]; // item.startDate;
421     ed = [self->endDate   valueInComponent:comp]; // item.startDate;
422 #endif
423     
424     sd  = [app valueForKey:startKey]; // startDate
425     ed  = [app valueForKey:endKey];   // endDate
426
427     if ((sd == nil) && (ed == nil)) continue;
428     
429     diff  = [(sd ? sd : ed) timeIntervalSinceDate:startWeek];
430
431     idx = floor((diff / SecondsPerWeek) * 14);
432
433     if ((self->item) && (self->isInfoItem)) {
434       [self->item setValue:app inComponent:comp];
435       isInfo = [[self->isInfoItem valueInComponent:comp] boolValue];
436     }
437     else isInfo = NO;
438     
439     if ((0 <= idx) && (idx < 14)) {      
440       if (isInfo) {
441         if (self->infos[idx] == nil)
442           self->infos[idx] = [[NSMutableArray alloc] initWithCapacity:2];      
443         [self->infos[idx] addObject:numForInt(i)];
444       }
445       else {
446         if (self->matrix[idx] == nil)
447           self->matrix[idx] = [[NSMutableArray alloc] initWithCapacity:4];
448         [self->matrix[idx] addObject:numForInt(i)];       
449       }
450     }
451     if (idx >= 0)
452       idx = (idx % 2) ? idx + 1: idx +2;
453     
454     diff = [ed timeIntervalSinceDate:startWeek];
455     
456     idx2 = floor((diff / SecondsPerWeek) * 14);
457     idx2 = (idx2 > 13) ? 13 : idx2;
458
459     if (idx2 < 0) continue;
460     
461     while (idx <= idx2) {
462       idx = (idx < 0) ? 0 : idx;
463       if (isInfo) {
464         if (self->infos[idx] == nil)
465           self->infos[idx] = [[NSMutableArray alloc] initWithCapacity:2];      
466         // TODO: optimize
467         [self->infos[idx] addObject:numForInt(i)];
468       }
469       else {
470         if (self->matrix[idx] == nil)
471           self->matrix[idx] = [[NSMutableArray alloc] initWithCapacity:4];
472         // TODO: optimize
473         [self->matrix[idx] addObject:numForInt(i)];       
474       }
475       idx = idx + 2;
476     }
477   }
478 }
479
480 /* common template operations */
481
482 - (void)appendTemplateToResponse:(WOResponse *)_r inContext:(WOContext *)_ctx
483   mode:(NSString *)_mode elementID:(NSString *)_modeId
484   index:(int)_idx
485 {
486   [_ctx enterWEWOMode:_mode elementID:_modeId];
487   [_ctx appendWEIntElementID:_idx];
488   [self->template appendToResponse:_r inContext:_ctx];
489   [_ctx deleteLastElementIDComponent]; // delete idx
490   [_ctx leaveWEWOMode:_mode];
491 }
492
493 /* header rows */
494
495 - (void)appendHeaderRowToResponse:(WOResponse *)_response
496   inContext:(WOContext *)_ctx
497   column:(int)_col
498 {
499   WOComponent *comp;
500   NSString    *bgcolor, *style;
501   
502   comp = [_ctx component];
503
504   if ([self->colIndex isValueSettable])
505     [self->colIndex setIntValue:_col inComponent:comp];
506
507   bgcolor = [self->contentColor stringValueInComponent:comp];
508   style   = [self->contentStyle stringValueInComponent:comp];
509   
510   [_response appendContentString:
511              @"<td valign=\"top\" align=\"left\" width=\"17%\""];
512   if (bgcolor) {
513     [_response appendContentString:@" bgcolor=\""];
514     [_response appendContentString:bgcolor];
515     [_response appendContentCharacter:'"'];
516   }
517   if ([style length] > 0) {
518     [_response appendContentString:@" class=\""];
519     [_response appendContentString:style];
520     [_response appendContentCharacter:'"'];
521   }
522   [_response appendContentCharacter:'>'];
523
524   [self appendTemplateToResponse:_response inContext:_ctx
525         mode:WEWeekOverview_HeaderRowMode elementID:@"h"
526         index:_col];
527   
528   [_response appendContentString:@"</td>"];
529 }
530 - (void)appendHeaderRowToResponse:(WOResponse *)_response
531   inContext:(WOContext *)_ctx
532   row:(id)_rowItem
533 {
534   int i, cnt;
535   
536   if ([self->headerRow isValueSettable])
537     [self->headerRow setValue:_rowItem inComponent:[_ctx component]];
538   
539   /* Saturday/Sunday is the 6th column */
540   cnt = (self->hideWeekend != nil)
541     ? [self->hideWeekend boolValueInComponent:[_ctx component]] ? 5 : 6
542     : 6;
543   
544   for (i = 0; i < cnt; i++)
545     [self appendHeaderRowToResponse:_response inContext:_ctx column:i];
546 }
547 - (void)appendHeaderRowsToResponse:(WOResponse *)_response
548   inContext:(WOContext *)_ctx
549 {
550   /* add n <tr> rows */
551   WOComponent *comp;
552   NSArray     *headers;
553   int         cnt, i;
554   
555   comp = [_ctx component];
556   if ((headers = [self->headerRows valueInComponent:comp]) == nil)
557     return;
558   
559   for (i = 0, cnt = [headers count]; i < cnt; i++) {
560     id one;
561     
562     one = [headers objectAtIndex:i];
563       
564     [_response appendContentString:@"<tr>"];
565     [_ctx appendElementIDComponent:@"hr"]; // header row
566     [_ctx appendWEIntElementID:i];
567     
568     [self appendHeaderRowToResponse:_response inContext:_ctx row:one];
569       
570     [_ctx deleteLastElementIDComponent]; // remove idx (i)
571     [_ctx deleteLastElementIDComponent]; // remove 'hr'
572     [_response appendContentString:@"</tr>"];
573   }
574 }
575
576 /* footer rows */
577
578 - (void)appendFooterRowToResponse:(WOResponse *)_response
579   inContext:(WOContext *)_ctx
580   column:(int)_col
581 {
582   WOComponent *comp;
583   NSString    *bgcolor;
584   
585   comp = [_ctx component];
586   
587   if ([self->colIndex isValueSettable])
588     [self->colIndex setIntValue:_col inComponent:comp];
589   
590   bgcolor = [self->contentColor stringValueInComponent:comp];
591   
592   [_response appendContentString:
593              @"<td valign=\"top\" align=\"left\" width=\"17%\""];
594   if (bgcolor) {
595     [_response appendContentString:@" bgcolor=\""];
596     [_response appendContentString:bgcolor];
597     [_response appendContentCharacter:'"'];
598   }
599   [_response appendContentCharacter:'>'];
600
601   [self appendTemplateToResponse:_response inContext:_ctx
602         mode:WEWeekOverview_FooterRowMode elementID:@"f"
603         index:_col];
604   
605   [_response appendContentString:@"</td>"];
606 }
607
608 - (void)appendFooterRowToResponse:(WOResponse *)_response
609   inContext:(WOContext *)_ctx
610   row:(id)_rowItem
611 {
612   // TODO: this method is a DUP to appendHeaderRowToResponse... => refactor
613   int i, cnt;
614   
615   if ([self->footerRow isValueSettable])
616     [self->footerRow setValue:_rowItem inComponent:[_ctx component]];
617   
618   /* Saturday/Sunday is the 6th column */
619   cnt = (self->hideWeekend != nil)
620     ? [self->hideWeekend boolValueInComponent:[_ctx component]] ? 5 : 6
621     : 6;
622   
623   for (i = 0; i < cnt; i++) 
624     [self appendFooterRowToResponse:_response inContext:_ctx column:i];
625 }
626
627 - (void)appendFooterRowsToResponse:(WOResponse *)_response
628   inContext:(WOContext *)_ctx
629 {
630   // TODO: this is a copy of the head-row method!
631   WOComponent *comp;
632   NSArray     *footers;
633   int i, cnt;
634
635   comp = [_ctx component];
636
637   if ((footers = [self->footerRows valueInComponent:comp]) == nil)
638     return;
639
640   for (i = 0, cnt = [footers count]; i < cnt; i++) {
641     NSString *s;
642     id one;
643
644     one = [footers objectAtIndex:i];
645       
646     [_response appendContentString:@"<tr>"];
647     [_ctx appendElementIDComponent:@"fr"];
648
649     s = retStrForInt(i);
650     [_ctx appendElementIDComponent:s];
651     [s release];
652       
653     [self appendFooterRowToResponse:_response inContext:_ctx row:one];
654       
655     [_ctx deleteLastElementIDComponent]; // remove idx (i)
656     [_ctx deleteLastElementIDComponent]; // remove 'fr'
657     [_response appendContentString:@"</tr>"];
658   }
659 }
660
661 - (void)appendDateTitleToResponse:(WOResponse *)_response
662   inContext:(WOContext *)_ctx
663   day:(int)_day
664 {
665   WOComponent *comp;
666   NSString    *bgcolor, *style;
667
668   comp = [_ctx component];
669   
670   if ([self->dayIndex isValueSettable])
671     [self->dayIndex setIntValue:_day inComponent:comp];
672
673   bgcolor = [self->titleColor stringValueInComponent:comp];
674   style   = [self->titleStyle stringValueInComponent:comp];
675   
676   // TODO: use CSS for alignment, width and color
677   [_response appendContentString:
678              @"<td valign=\"top\" align=\"left\" width=\"17%\""];
679   if ([bgcolor length] > 0) {
680     [_response appendContentString:@" bgcolor=\""];
681     [_response appendContentString:bgcolor];
682     [_response appendContentCharacter:'"'];
683   }
684   if ([style length] > 0) {
685     [_response appendContentString:@" class=\""];
686     [_response appendContentString:style];
687     [_response appendContentCharacter:'"'];
688   }
689   [_response appendContentCharacter:'>'];
690   
691   if (self->hasOwnTitle) {
692     [self appendTemplateToResponse:_response inContext:_ctx
693           mode:WEWeekOverview_TitleMode elementID:@"t"
694           index:_day];
695   }
696   else {
697     NSCalendarDate *date;
698     NSString *s;
699     
700     date = (self->weekStart)
701       ? [self->weekStart valueInComponent:comp]
702       : [NSCalendarDate calendarDate];
703     date = [date addTimeInterval:_day * SecondsPerDay];
704     
705     [_response appendContentString:
706                @"<table cellpadding=\"0\" width=\"100%\" border=\"0\" "
707                @"cellspacing=\"0\">"
708                @"<tr>"
709                @"<td align=\"left\" valign=\"top\">"
710                // TODO: use style for that
711                @"<font color=\"black\" size=\"+2\"><b>"];
712     
713     s = [date descriptionWithCalendarFormat:@"%d"]; // TODO: no cal-desc!
714     [_response appendContentString:s];
715     
716     [_response appendContentString:
717                @"</b></font></td>"
718                @"<td align=\"center\" valign=\"top\">"
719                // TODO: use style for that
720                @"<font color=\"black\">"];
721     s = [date descriptionWithCalendarFormat:@"%A"]; // TODO: no cal-desc
722     [_response appendContentString:s]; // TODO: do not use cal-desc
723     [_response appendContentString:
724                @"</font>"
725                @"</td></tr></table>"];
726   }
727   [_response appendContentString:@"</td>"];
728 }
729
730 - (void)appendContentToResponse:(WOResponse *)_response
731   inContext:(WOContext *)_ctx
732   index:(int)_index
733 {
734   WOComponent *comp;
735   NSArray     *array;
736   id          app;
737   int         i, cnt, idx, count;
738   
739   comp  = [_ctx component];
740   array = [self->list valueInComponent:comp];
741   count = [array count];
742
743   /* idx is the dayIndex 0=1stDay ... 6=7thDay (e.g. 0=Mon ... 6=Sun) */
744   idx = (int)(_index / 2);
745   
746   if ([self->dayIndex isValueSettable])
747     [self->dayIndex setIntValue:idx inComponent:comp];
748
749   if ([self->infoItems isValueSettable]) {
750     cnt = [self->infos[_index] count];
751     if (cnt) {
752       NSMutableArray *infoArr;
753       
754       infoArr = [[NSMutableArray alloc] initWithCapacity:cnt];
755       for (i = 0; i < cnt; i++) {
756         idx = [[self->infos[_index] objectAtIndex:i] intValue];
757         
758         if (idx >= count) {
759           [self logWithFormat:
760                   @"WARNING: WEWeekOverview: info index out of range"];
761           continue;
762         }
763         [infoArr addObject:[array objectAtIndex:idx]];
764       }
765       [self->infoItems setValue:infoArr inComponent:comp];
766       [infoArr release]; infoArr = nil;
767     }
768     else
769       [self->infoItems setValue:emptyArray inComponent:comp];
770   }
771
772   // *** append day info
773   if ((_index % 2) == 0) {
774     // if AM-section...
775     
776     [self appendTemplateToResponse:_response inContext:_ctx
777           mode:WEWeekOverview_InfoMode elementID:WEInfoElementID
778           index:idx];
779   }
780   else if (_index < 10) {
781     /*  P.M. slot of Monday .. Friday */
782     [self appendTemplateToResponse:_response inContext:_ctx
783           mode:WEWeekOverview_PMInfoMode elementID:@"p"
784           index:idx];
785   }
786   
787   // *** append day content
788   [_ctx enterWEWOMode:WEWeekOverview_ContentMode elementID:@"c"];
789   
790   // append section id (0 = Mon AM, 1 = Mon PM, 2 = Tue AM, ...)
791   [_ctx appendWEIntElementID:_index];
792   
793   for (i = 0, cnt = [self->matrix[_index] count]; i < cnt; i++) {
794     idx = [[self->matrix[_index] objectAtIndex:i] intValue];
795
796     if (idx >= count) {
797       NSLog(@"Warning! WEWeekOverview: index out of range");
798       continue;
799     }
800     
801     app = [array objectAtIndex:idx];
802
803     if ([self->item isValueSettable])
804       [self->item  setValue:app inComponent:comp];
805     if ([self->index isValueSettable])
806       [self->index setIntValue:idx inComponent:comp];
807
808     if (self->identifier == nil) {
809       NSString *s;
810       
811       s = retStrForInt(idx);
812       [_ctx appendElementIDComponent:s];
813       [s release];
814     }
815     else {
816       NSString *s;
817
818       s = [self->identifier stringValueInComponent:comp];
819       [_ctx appendElementIDComponent:s];
820     }
821     [self->template appendToResponse:_response inContext:_ctx];
822     [_ctx deleteLastElementIDComponent];
823   }
824   if (cnt == 0)
825     [_response appendContentString:@"&nbsp;"];
826   
827   [_ctx deleteLastElementIDComponent]; // delete section id
828   [_ctx leaveWEWOMode:WEWeekOverview_ContentMode];
829 }
830
831 /* processing requests */
832
833 - (void)takeValuesForHeaderRows:(WORequest *)_req
834   ctx:(WOContext *)_ctx
835 {
836   WOComponent *comp;
837   NSArray     *headers;
838   int i, cnt;
839
840   comp    = [_ctx component];
841   if ((headers = [self->headerRows valueInComponent:comp]) == nil)
842     return;
843
844   [_ctx setObject:@"YES" forKey:WEWeekOverview_HeaderRowMode];
845   [_ctx appendElementIDComponent:@"hr"]; // append 'hr'
846   [_ctx appendZeroElementIDComponent];   // append i
847
848   for (i = 0, cnt = [headers count]; i < cnt; i++) {
849     int c;
850
851     [_ctx appendElementIDComponent:@"h"]; // append 'h'
852     [_ctx appendZeroElementIDComponent];  // append c
853       
854     if ([self->headerRow isValueSettable])
855       [self->headerRow setValue:[headers objectAtIndex:i] inComponent:comp];
856       
857     // TODO: do not go over Saturday (6) with hideWeekend=YES
858     for (c = 0; c < 6; c++) { // go throw columns
859       if ([self->colIndex isValueSettable])
860         [self->colIndex setIntValue:c inComponent:comp];
861         
862       [self->template takeValuesFromRequest:_req inContext:_ctx];
863       [_ctx incrementLastElementIDComponent]; // increment c
864     }
865       
866     [_ctx deleteLastElementIDComponent];    // delete c
867     [_ctx deleteLastElementIDComponent];    // delete 'h'
868     [_ctx incrementLastElementIDComponent]; // increment i
869   }
870   
871   [_ctx deleteLastElementIDComponent]; // remove i
872   [_ctx deleteLastElementIDComponent]; // remove 'hr'
873   [_ctx removeObjectForKey:WEWeekOverview_HeaderRowMode];
874 }
875 - (void)takeValuesForFooterRows:(WORequest *)_req
876   ctx:(WOContext *)_ctx
877 {
878   // TODO: this is a DUP of the header row
879   //       we might implement that code as an internal WODynamicElement?
880   WOComponent *comp;
881   NSArray     *footers;
882   int i, cnt;
883
884   comp    = [_ctx component];
885   if ((footers = [self->footerRows valueInComponent:comp]) == nil)
886     return;
887
888   [_ctx setObject:@"YES" forKey:WEWeekOverview_FooterRowMode];
889   [_ctx appendElementIDComponent:@"hr"]; // append 'fr'
890   [_ctx appendZeroElementIDComponent];   // append i
891   
892   for (i = 0, cnt = [footers count]; i < cnt; i++) {
893       int c;
894       [_ctx appendElementIDComponent:@"f"]; // append 'f'
895       [_ctx appendZeroElementIDComponent];  // append c
896       
897       if ([self->footerRow isValueSettable])
898         [self->footerRow setValue:[footers objectAtIndex:i] inComponent:comp];
899       
900       // TODO: do not go over Saturday (6) with hideWeekend=YES
901       for (c = 0; c < 6; c++) { // go throw columns
902         if ([self->colIndex isValueSettable])
903           [self->colIndex setIntValue:c inComponent:comp];
904         [self->template takeValuesFromRequest:_req inContext:_ctx];
905         [_ctx incrementLastElementIDComponent]; // increment c
906       }
907       
908       [_ctx deleteLastElementIDComponent];    // delete c
909       [_ctx deleteLastElementIDComponent];    // delete 'f'
910       [_ctx incrementLastElementIDComponent]; // increment i
911   }
912
913   [_ctx deleteLastElementIDComponent]; // remove i
914   [_ctx deleteLastElementIDComponent]; // remove 'fr'
915   [_ctx removeObjectForKey:WEWeekOverview_FooterRowMode];
916 }
917
918 - (void)takeValues:(WORequest *)_req
919   mode:(NSString *)_mode modeId:(NSString *)_id
920   ctx:(WOContext *)_ctx
921 {
922   int i;
923   
924   [_ctx setObject:@"YES" forKey:_mode];
925   [_ctx appendElementIDComponent:_id];   // append mode id
926   [_ctx appendZeroElementIDComponent];   // append first day (monday)
927   
928   // TODO: do not go over Saturday (5), Sunday(6) with hideWeekend=YES
929   for (i = 0; i < 7; i++) {
930     if ([self->dayIndex isValueSettable])
931       [self->dayIndex setIntValue:i inComponent:[_ctx component]];
932     
933     [self->template takeValuesFromRequest:_req inContext:_ctx];
934     [_ctx incrementLastElementIDComponent]; // increment day
935   }
936   [_ctx deleteLastElementIDComponent];  // delete day
937   [_ctx deleteLastElementIDComponent];  // delete title mode
938   [_ctx removeObjectForKey:_mode];
939 }
940
941 - (void)takeValuesFromRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
942   WOComponent *comp;
943   NSArray     *array;
944   int         i, count;
945   
946   comp  = [_ctx component];
947   array = [self->list valueInComponent:comp];
948   count = [array count];
949
950   /* titles */
951   [self takeValues:_req mode:WEWeekOverview_TitleMode modeId:@"t" ctx:_ctx];
952
953   /* headers */
954   [self takeValuesForHeaderRows:_req ctx:_ctx];
955   
956   /* infos */
957   [self takeValues:_req mode:WEWeekOverview_InfoMode modeId:WEInfoElementID 
958         ctx:_ctx];
959
960   /* P.M.Infos */
961   [self takeValues:_req mode:WEWeekOverview_PMInfoMode modeId:@"p" ctx:_ctx];
962
963   [self _calcMatrixInContext:_ctx];
964
965   [_ctx setObject:@"YES" forKey:WEWeekOverview_ContentMode];
966   [_ctx appendElementIDComponent:@"c"]; // append content mode
967   [_ctx appendZeroElementIDComponent];  // append section-id
968   for (i = 0; i < 14; i++) {
969     int     j, cnt, idx;
970     
971     cnt   = [self->matrix[i] count];
972     
973     if ((i % 2) == 0 && [self->dayIndex isValueSettable])
974       [self->dayIndex setIntValue:(int)(i / 2) inComponent:comp];
975     
976     for (j = 0; j < cnt; j++) {
977       NSString *s;
978       
979       idx = [[self->matrix[i] objectAtIndex:j] intValue];
980       
981       if (idx >= count) {
982         NSLog(@"Warning! WEWeekRepetition: Index is out of range");
983         continue;
984       }
985       
986       _applyIndex(self, comp, idx);
987       
988       if (self->identifier) {
989         s = [self->identifier stringValueInComponent:comp];
990         [_ctx appendElementIDComponent:s];
991       }
992       else {
993         s = retStrForInt(idx);
994         [_ctx appendElementIDComponent:s];
995         [s release];
996       }
997
998       [self->template takeValuesFromRequest:_req inContext:_ctx];
999       
1000       [_ctx deleteLastElementIDComponent];   // delete index-id
1001     }
1002     [_ctx incrementLastElementIDComponent]; // increase section-id
1003   }
1004   [_ctx deleteLastElementIDComponent];  // delete section-id
1005   [_ctx deleteLastElementIDComponent];  // delete content mode
1006   [_ctx removeObjectForKey:WEWeekOverview_ContentMode];
1007   
1008   /* footers */
1009   [self takeValuesForFooterRows:_req ctx:_ctx];
1010
1011   [self resetMatrix];  
1012 }
1013
1014 - (id)invokeActionForHeader:(WORequest *)_req inContext:(WOContext *)_ctx
1015   row:(id)_row
1016 {
1017   WOComponent *comp;
1018   NSString *section;   // must be 'h'
1019   NSString *colIdx;    // must be between 0 and 6
1020
1021   id result;
1022
1023   comp = [_ctx component];
1024
1025   section = [_ctx currentElementID];
1026   [_ctx consumeElementID];
1027   [_ctx appendElementIDComponent:section];
1028   colIdx  = [_ctx currentElementID];
1029   [_ctx consumeElementID];
1030   [_ctx appendElementIDComponent:colIdx];
1031
1032   if ([self->headerRow isValueSettable])
1033     [self->headerRow setValue:_row inComponent:comp];
1034   if ([self->dayIndex isValueSettable])
1035     [self->colIndex setIntValue:[colIdx intValue] inComponent:comp];
1036
1037   result = [self->template invokeActionForRequest:_req inContext:_ctx];
1038
1039   [_ctx deleteLastElementIDComponent]; // column idx
1040   [_ctx deleteLastElementIDComponent]; // section ('h')
1041
1042   return result;
1043 }
1044 - (id)invokeActionForFooter:(WORequest *)_req inContext:(WOContext *)_ctx
1045   row:(id)_row
1046 {
1047   WOComponent *comp;
1048   NSString *section;   // must be 'f'
1049   NSString *colIdx;    // must be between 0 and 6
1050
1051   id result;
1052
1053   comp = [_ctx component];
1054
1055   section = [_ctx currentElementID];
1056   [_ctx consumeElementID];
1057   [_ctx appendElementIDComponent:section];
1058   colIdx  = [_ctx currentElementID];
1059   [_ctx consumeElementID];
1060   [_ctx appendElementIDComponent:colIdx];
1061
1062   if ([self->footerRow isValueSettable])
1063     [self->footerRow setValue:_row inComponent:comp];
1064   if ([self->dayIndex isValueSettable])
1065     [self->colIndex setIntValue:[colIdx intValue] inComponent:comp];
1066
1067   result = [self->template invokeActionForRequest:_req inContext:_ctx];
1068
1069   [_ctx deleteLastElementIDComponent]; // column idx
1070   [_ctx deleteLastElementIDComponent]; // section ('f')
1071
1072   return result;
1073 }
1074
1075 - (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
1076   WOComponent *comp;
1077   id          result = nil;
1078   NSString    *cid;
1079   NSString    *sectionId;
1080
1081   cid = [_ctx currentElementID];       // get mode ("t" or "i" or "c")
1082   [_ctx consumeElementID];
1083   [_ctx appendElementIDComponent:cid];
1084   sectionId = [_ctx currentElementID];       // get section id
1085   [_ctx consumeElementID];
1086   [_ctx appendElementIDComponent:sectionId];
1087
1088   comp = [_ctx component];
1089
1090   if ([cid isEqualToString:@"t"] ||
1091       [cid isEqualToString:WEInfoElementID] || [cid isEqualToString:@"p"]) {
1092     if ([self->dayIndex isValueSettable])
1093       [self->dayIndex setIntValue:[sectionId intValue] inComponent:comp];
1094     result = [self->template invokeActionForRequest:_req inContext:_ctx];
1095   }
1096   else if ([cid isEqualToString:@"c"]) {
1097     NSString *idxId;
1098
1099     if ([self->dayIndex isValueSettable])
1100       [self->dayIndex setIntValue:([sectionId intValue] / 2) inComponent:comp];
1101
1102     if ((idxId = [_ctx currentElementID])) {
1103       [_ctx consumeElementID];               // consume index-id
1104       [_ctx appendElementIDComponent:idxId];
1105
1106       if (self->identifier)
1107         _applyIdentifier(self, comp, idxId);
1108       else
1109         _applyIndex(self, comp, [idxId intValue]);
1110
1111       result = [self->template invokeActionForRequest:_req inContext:_ctx];
1112
1113       [_ctx deleteLastElementIDComponent]; // delete index-id
1114     }
1115   }
1116   else if ([cid isEqualToString:@"hr"]) {
1117     NSArray  *headers;
1118     int      idx;
1119
1120     headers = [self->headerRows valueInComponent:comp];
1121     idx     = [sectionId intValue];
1122     if (idx >= (int)[headers count]) {
1123       NSLog(@"WARNING! WEWeekOverview: wrong headerrow index");
1124     }
1125     else {
1126       result = [self invokeActionForHeader:_req
1127                      inContext:_ctx
1128                      row:[headers objectAtIndex:idx]];
1129     }
1130   }
1131   else if ([cid isEqualToString:@"fr"]) {
1132     NSArray  *footers;
1133     int      idx;
1134
1135     footers = [self->footerRows valueInComponent:comp];
1136     idx     = [sectionId intValue];
1137     if (idx >= (int)[footers count]) {
1138       NSLog(@"WARNING! WEWeekOverview: wrong footerrow index");
1139     }
1140     else {
1141       result = [self invokeActionForFooter:_req
1142                      inContext:_ctx
1143                      row:[footers objectAtIndex:idx]];
1144     }
1145   }
1146   else
1147     NSLog(@"WARNING! WEWeekOverview: wrong section");
1148
1149   [_ctx deleteLastElementIDComponent]; // delete section id
1150   [_ctx deleteLastElementIDComponent]; // delete mode
1151   
1152   return result;
1153 }
1154
1155 /* generating response */
1156
1157 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
1158   WOComponent *comp;
1159   NSString    *bgcolor, *style;
1160   NSString    *w;  // width
1161   NSString    *b;  // border
1162   NSString    *cp; // cellpadding
1163   NSString    *cs; // cellspacing
1164   int         i;
1165   BOOL        showWeekend;
1166   
1167   [self _calcMatrixInContext:_ctx];
1168   
1169   comp = [_ctx component];
1170   w    = [self->width       stringValueInComponent:comp];
1171   b    = [self->border      stringValueInComponent:comp];
1172   cs   = [self->cellspacing stringValueInComponent:comp];
1173   cp   = [self->cellpadding stringValueInComponent:comp];
1174   
1175   showWeekend = (self->hideWeekend != nil)
1176     ? [self->hideWeekend boolValueInComponent:comp] ? NO : YES
1177     : YES;
1178   
1179   [_response appendContentString:
1180              @"<table border=\""];
1181   [_response appendContentString:b];  // border
1182   [_response appendContentString:@"\" cellpadding=\""];
1183   [_response appendContentString:cp]; // cellspacing
1184   [_response appendContentString:@"\" width=\""];
1185   [_response appendContentString:w];  // width
1186   [_response appendContentString:@"\" cellspacing=\""];
1187   [_response appendContentString:cs]; // cellspaing
1188   [_response appendContentString:@"\">"];
1189   
1190   /*** append title row (monday - saturday) ***/
1191   [_response appendContentString:@"<tr>"];
1192   for (i = 0; i < (showWeekend ? 6 : 5 ); i++)
1193     [self appendDateTitleToResponse:_response inContext:_ctx day:i];
1194   [_response appendContentString:@"</tr>"];
1195   
1196   /**** header rows ****/
1197   [self appendHeaderRowsToResponse:_response inContext:_ctx];
1198
1199   /*** append AM content row + saturday ***/
1200   [_response appendContentString:@"<tr>"];
1201
1202   /* AM weekdays content (this is count=10 because we slot 0=AM/1=PM, etc) */
1203   for (i = 0; i < 10; i = i + 2) {
1204     if ([self->dayIndex isValueSettable])
1205       [self->dayIndex setIntValue:(i / 2) inComponent:comp];
1206
1207     [_response appendContentString:@"<td valign=\"top\""];
1208     if ((bgcolor = [self->contentColor stringValueInComponent:comp])) {
1209       [_response appendContentString:@" bgcolor=\""];
1210       [_response appendContentString:bgcolor];
1211       [_response appendContentCharacter:'"'];
1212     }
1213     if ((style = [self->contentStyle stringValueInComponent:comp])) {
1214       [_response appendContentString:@" class=\""];
1215       [_response appendContentString:style];
1216       [_response appendContentCharacter:'"'];
1217     }
1218     [_response appendContentCharacter:'>'];
1219     [self appendContentToResponse:_response inContext:_ctx index:i];
1220     [_response appendContentString:@"</td>"];
1221   }
1222   /* saturday content */
1223   if (showWeekend) {
1224     if ([self->dayIndex isValueSettable])
1225       [self->dayIndex setIntValue:5 inComponent:comp];
1226
1227     [_response appendContentString:@"<td valign=\"top\""];
1228   
1229     if ((bgcolor = [self->contentColor stringValueInComponent:comp])) {
1230       [_response appendContentString:@" bgcolor=\""];
1231       [_response appendContentString:bgcolor];
1232       [_response appendContentCharacter:'"'];
1233     }
1234     if ((style = [self->contentStyle stringValueInComponent:comp])) {
1235       [_response appendContentString:@" class=\""];
1236       [_response appendContentString:style];
1237       [_response appendContentCharacter:'"'];
1238     }
1239     [_response appendContentCharacter:'>'];
1240     
1241     /* 10 is Saturday AM, 11 is Saturday PM, week overview shows them in one */
1242     [self appendContentToResponse:_response inContext:_ctx index:10];
1243     [self appendContentToResponse:_response inContext:_ctx index:11];
1244     [_response appendContentString:@"</td>"];
1245   }
1246   
1247   [_response appendContentString:@"</tr>"]; /* close AM row */
1248
1249   /*** append PM content row + sunday ***/
1250   [_response appendContentString:@"<tr>"];
1251
1252   /* PM weekdays content */
1253   for (i = 1; i < 11; i = i + 2) {
1254     if ([self->dayIndex isValueSettable])
1255       [self->dayIndex setIntValue:(i/2) inComponent:comp];
1256       
1257     [_response appendContentString:@"<td valign=\"top\""];
1258     
1259     if (showWeekend) /* for Sunday we need an additional <tr> */
1260       [_response appendContentString:@" rowspan=\"2\""]; 
1261     
1262     if ((bgcolor = [self->contentColor stringValueInComponent:comp])) {
1263       [_response appendContentString:@" bgcolor=\""];
1264       [_response appendContentString:bgcolor];
1265       [_response appendContentCharacter:'"'];
1266     }
1267     if ((style = [self->contentStyle stringValueInComponent:comp])) {
1268       [_response appendContentString:@" class=\""];
1269       [_response appendContentString:style];
1270       [_response appendContentCharacter:'"'];
1271     }
1272     [_response appendContentCharacter:'>'];
1273
1274       
1275     [self appendContentToResponse:_response inContext:_ctx index:i];
1276     [_response appendContentString:@"</td>"];
1277   }
1278
1279   if (showWeekend) {
1280     /* sunday title */
1281     [self appendDateTitleToResponse:_response inContext:_ctx day:6];
1282     [_response appendContentString:@"</tr>"];
1283
1284     /*  sunday row */
1285     [_response appendContentString:@"<tr>"];
1286
1287     if ([self->dayIndex isValueSettable])
1288       [self->dayIndex setIntValue:6 inComponent:comp];
1289   
1290     [_response appendContentString:@"<td valign=\"top\""];
1291     if ((bgcolor = [self->contentColor stringValueInComponent:comp])) {
1292       [_response appendContentString:@" bgcolor=\""];
1293       [_response appendContentString:bgcolor];
1294       [_response appendContentCharacter:'"'];
1295     }
1296     [_response appendContentCharacter:'>'];
1297     
1298     /* 12 is Sunday/AM, 13 is Sunday/PM */
1299     [self appendContentToResponse:_response inContext:_ctx index:12];
1300     [self appendContentToResponse:_response inContext:_ctx index:13];
1301     [_response appendContentString:@"</td>"];
1302   }
1303   
1304   [_response appendContentString:@"</tr>"]; /* close PM row */
1305
1306   /***** footer rows *****/
1307   [self appendFooterRowsToResponse:_response inContext:_ctx];
1308
1309   [_response appendContentString:@"</table>"];
1310
1311   [self resetMatrix];
1312 }
1313
1314 @end /* WeekOverview */
1315
1316 @interface WEWeekOverviewTitleMode : WEContextConditional
1317 @end
1318
1319 @implementation WEWeekOverviewTitleMode
1320 - (NSString *)_contextKey {
1321   return WEWeekOverview_TitleMode;
1322 }
1323
1324 - (NSString *)_didMatchKey {
1325   return WEWeekOverview_TitleModeDidMatch;
1326 }
1327 @end /* WEWeekOverviewTitleMode */
1328
1329 // --
1330
1331 @interface WEWeekOverviewInfoMode : WEContextConditional
1332 @end
1333
1334 @implementation WEWeekOverviewInfoMode
1335 - (NSString *)_contextKey {
1336   return WEWeekOverview_InfoMode;
1337 }
1338 @end /* WEWeekOverviewInfoMode */
1339
1340 // --
1341
1342 @interface WEWeekOverviewContentMode : WEContextConditional
1343 @end
1344
1345 @implementation WEWeekOverviewContentMode
1346 - (NSString *)_contextKey {
1347   return WEWeekOverview_ContentMode;
1348 }
1349 @end /* WEWeekOverview_ContentMode */
1350
1351 // --
1352
1353 @interface WEWeekOverviewPMInfoMode : WEContextConditional
1354 @end
1355
1356 @implementation WEWeekOverviewPMInfoMode
1357 - (NSString *)_contextKey {
1358   return WEWeekOverview_PMInfoMode;
1359 }
1360 @end /* WEWeekOverviewPMInfoMode */
1361
1362 @interface WEWeekOverviewHeaderMode : WEContextConditional
1363 @end
1364
1365 @implementation WEWeekOverviewHeaderMode
1366 - (NSString *)_contextKey {
1367   return WEWeekOverview_HeaderRowMode;
1368 }
1369 @end /* WEWeekOverviewHeaderMode */
1370
1371 @interface WEWeekOverviewFooterMode : WEContextConditional
1372 @end
1373
1374 @implementation WEWeekOverviewFooterMode
1375 - (NSString *)_contextKey {
1376   return WEWeekOverview_FooterRowMode;
1377 }
1378 @end /* WEWeekOverviewFooterMode */