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
141static NSString *retStrForInt(int i);
142static 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
169static NSNumber *smap[10] = { nil,nil,nil,nil,nil,nil,nil,nil,nil,nil };
170static Class    NumClass  = Nil;
171static Class    StrClass  = Nil;
172static 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
188static NSNumber *numForInt(int i) {
189  if (i >= 0 && i < 10)
190    return smap[i];
191  return [NumClass numberWithInt:i];
192}
193static 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
316static 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
354static 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  // THREAD: uses temporary ivars
382  WOComponent    *comp;
383  NSCalendarDate *startWeek;
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
409  for (i = 0, cnt = [array count]; i < cnt; i++) {
410    id             app;
411    NSCalendarDate *sd, *ed;
412    NSTimeInterval diff;
413    BOOL           isInfo;
414
415    app = [array objectAtIndex:i];
416#if 0 // hh: so muesste es eigentlich sein: !!!
417    [self->item setValue:app inComponent:comp];
418
419    sd = [self->startDate valueInComponent:comp]; // item.startDate;
420    ed = [self->endDate   valueInComponent:comp]; // item.startDate;
421#endif
422
423    sd  = [app valueForKey:startKey]; // startDate
424    ed  = [app valueForKey:endKey];   // endDate
425
426    if ((sd == nil) && (ed == nil)) continue;
427
428    diff  = [(sd ? sd : ed) timeIntervalSinceDate:startWeek];
429
430    idx = floor((diff / SecondsPerWeek) * 14);
431
432    if ((self->item != nil) && (self->isInfoItem)) {
433      [self->item setValue:app inComponent:comp];
434      isInfo = [[self->isInfoItem valueInComponent:comp] boolValue];
435    }
436    else
437      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 isNotEmpty]) {
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 isNotEmpty]) {
680    [_response appendContentString:@" bgcolor=\""];
681    [_response appendContentString:bgcolor];
682    [_response appendContentCharacter:'"'];
683  }
684  if ([style isNotEmpty]) {
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  // THREAD: uses temporary ivars
964  [self _calcMatrixInContext:_ctx];
965
966  [_ctx setObject:@"YES" forKey:WEWeekOverview_ContentMode];
967  [_ctx appendElementIDComponent:@"c"]; // append content mode
968  [_ctx appendZeroElementIDComponent];  // append section-id
969  for (i = 0; i < 14; i++) {
970    int     j, cnt, idx;
971
972    cnt   = [self->matrix[i] count];
973
974    if ((i % 2) == 0 && [self->dayIndex isValueSettable])
975      [self->dayIndex setIntValue:(int)(i / 2) inComponent:comp];
976
977    for (j = 0; j < cnt; j++) {
978      NSString *s;
979
980      idx = [[self->matrix[i] objectAtIndex:j] intValue];
981
982      if (idx >= count) {
983        NSLog(@"Warning! WEWeekRepetition: Index is out of range");
984        continue;
985      }
986
987      _applyIndex(self, comp, idx);
988
989      if (self->identifier) {
990        s = [self->identifier stringValueInComponent:comp];
991        [_ctx appendElementIDComponent:s];
992      }
993      else {
994        s = retStrForInt(idx);
995        [_ctx appendElementIDComponent:s];
996        [s release];
997      }
998
999      [self->template takeValuesFromRequest:_req inContext:_ctx];
1000
1001      [_ctx deleteLastElementIDComponent];   // delete index-id
1002    }
1003    [_ctx incrementLastElementIDComponent]; // increase section-id
1004  }
1005  [_ctx deleteLastElementIDComponent];  // delete section-id
1006  [_ctx deleteLastElementIDComponent];  // delete content mode
1007  [_ctx removeObjectForKey:WEWeekOverview_ContentMode];
1008
1009  /* footers */
1010  [self takeValuesForFooterRows:_req ctx:_ctx];
1011
1012  [self resetMatrix];
1013}
1014
1015- (id)invokeActionForHeader:(WORequest *)_req inContext:(WOContext *)_ctx
1016  row:(id)_row
1017{
1018  WOComponent *comp;
1019  NSString *section;   // must be 'h'
1020  NSString *colIdx;    // must be between 0 and 6
1021
1022  id result;
1023
1024  comp = [_ctx component];
1025
1026  section = [_ctx currentElementID];
1027  [_ctx consumeElementID];
1028  [_ctx appendElementIDComponent:section];
1029  colIdx  = [_ctx currentElementID];
1030  [_ctx consumeElementID];
1031  [_ctx appendElementIDComponent:colIdx];
1032
1033  if ([self->headerRow isValueSettable])
1034    [self->headerRow setValue:_row inComponent:comp];
1035  if ([self->dayIndex isValueSettable])
1036    [self->colIndex setIntValue:[colIdx intValue] inComponent:comp];
1037
1038  result = [self->template invokeActionForRequest:_req inContext:_ctx];
1039
1040  [_ctx deleteLastElementIDComponent]; // column idx
1041  [_ctx deleteLastElementIDComponent]; // section ('h')
1042
1043  return result;
1044}
1045- (id)invokeActionForFooter:(WORequest *)_req inContext:(WOContext *)_ctx
1046  row:(id)_row
1047{
1048  WOComponent *comp;
1049  NSString *section;   // must be 'f'
1050  NSString *colIdx;    // must be between 0 and 6
1051
1052  id result;
1053
1054  comp = [_ctx component];
1055
1056  section = [_ctx currentElementID];
1057  [_ctx consumeElementID];
1058  [_ctx appendElementIDComponent:section];
1059  colIdx  = [_ctx currentElementID];
1060  [_ctx consumeElementID];
1061  [_ctx appendElementIDComponent:colIdx];
1062
1063  if ([self->footerRow isValueSettable])
1064    [self->footerRow setValue:_row inComponent:comp];
1065  if ([self->dayIndex isValueSettable])
1066    [self->colIndex setIntValue:[colIdx intValue] inComponent:comp];
1067
1068  result = [self->template invokeActionForRequest:_req inContext:_ctx];
1069
1070  [_ctx deleteLastElementIDComponent]; // column idx
1071  [_ctx deleteLastElementIDComponent]; // section ('f')
1072
1073  return result;
1074}
1075
1076- (id)invokeActionForRequest:(WORequest *)_req inContext:(WOContext *)_ctx {
1077  WOComponent *comp;
1078  id          result = nil;
1079  NSString    *cid;
1080  NSString    *sectionId;
1081
1082  cid = [_ctx currentElementID];       // get mode ("t" or "i" or "c")
1083  [_ctx consumeElementID];
1084  [_ctx appendElementIDComponent:cid];
1085  sectionId = [_ctx currentElementID];       // get section id
1086  [_ctx consumeElementID];
1087  [_ctx appendElementIDComponent:sectionId];
1088
1089  comp = [_ctx component];
1090
1091  if ([cid isEqualToString:@"t"] ||
1092      [cid isEqualToString:WEInfoElementID] || [cid isEqualToString:@"p"]) {
1093    if ([self->dayIndex isValueSettable])
1094      [self->dayIndex setIntValue:[sectionId intValue] inComponent:comp];
1095    result = [self->template invokeActionForRequest:_req inContext:_ctx];
1096  }
1097  else if ([cid isEqualToString:@"c"]) {
1098    NSString *idxId;
1099
1100    if ([self->dayIndex isValueSettable])
1101      [self->dayIndex setIntValue:([sectionId intValue] / 2) inComponent:comp];
1102
1103    if ((idxId = [_ctx currentElementID])) {
1104      [_ctx consumeElementID];               // consume index-id
1105      [_ctx appendElementIDComponent:idxId];
1106
1107      if (self->identifier)
1108        _applyIdentifier(self, comp, idxId);
1109      else
1110        _applyIndex(self, comp, [idxId intValue]);
1111
1112      result = [self->template invokeActionForRequest:_req inContext:_ctx];
1113
1114      [_ctx deleteLastElementIDComponent]; // delete index-id
1115    }
1116  }
1117  else if ([cid isEqualToString:@"hr"]) {
1118    NSArray  *headers;
1119    int      idx;
1120
1121    headers = [self->headerRows valueInComponent:comp];
1122    idx     = [sectionId intValue];
1123    if (idx >= (int)[headers count]) {
1124      [self warnWithFormat:@"WEWeekOverview: wrong headerrow index"];
1125    }
1126    else {
1127      result = [self invokeActionForHeader:_req
1128                     inContext:_ctx
1129                     row:[headers objectAtIndex:idx]];
1130    }
1131  }
1132  else if ([cid isEqualToString:@"fr"]) {
1133    NSArray  *footers;
1134    int      idx;
1135
1136    footers = [self->footerRows valueInComponent:comp];
1137    idx     = [sectionId intValue];
1138    if (idx >= (int)[footers count]) {
1139      [self warnWithFormat:@"WEWeekOverview: wrong footerrow index"];
1140    }
1141    else {
1142      result = [self invokeActionForFooter:_req
1143                     inContext:_ctx
1144                     row:[footers objectAtIndex:idx]];
1145    }
1146  }
1147  else
1148    NSLog(@"WARNING! WEWeekOverview: wrong section");
1149
1150  [_ctx deleteLastElementIDComponent]; // delete section id
1151  [_ctx deleteLastElementIDComponent]; // delete mode
1152
1153  return result;
1154}
1155
1156/* generating response */
1157
1158- (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
1159  WOComponent *comp;
1160  NSString    *bgcolor, *style;
1161  NSString    *w;  // width
1162  NSString    *b;  // border
1163  NSString    *cp; // cellpadding
1164  NSString    *cs; // cellspacing
1165  int         i;
1166  BOOL        showWeekend;
1167
1168  if ([_ctx isRenderingDisabled]) {
1169    [self->template appendToResponse:_response inContext:_ctx];
1170    return;
1171  }
1172
1173  [self _calcMatrixInContext:_ctx];
1174
1175  comp = [_ctx component];
1176  w    = [self->width       stringValueInComponent:comp];
1177  b    = [self->border      stringValueInComponent:comp];
1178  cs   = [self->cellspacing stringValueInComponent:comp];
1179  cp   = [self->cellpadding stringValueInComponent:comp];
1180
1181  showWeekend = (self->hideWeekend != nil)
1182    ? [self->hideWeekend boolValueInComponent:comp] ? NO : YES
1183    : YES;
1184
1185  // TODO: use CSS over here
1186  [_response appendContentString:
1187             @"<table border=\""];
1188  [_response appendContentString:b];  // border
1189  [_response appendContentString:@"\" cellpadding=\""];
1190  [_response appendContentString:cp]; // cellspacing
1191  [_response appendContentString:@"\" width=\""];
1192  [_response appendContentString:w];  // width
1193  [_response appendContentString:@"\" cellspacing=\""];
1194  [_response appendContentString:cs]; // cellspaing
1195  [_response appendContentString:@"\">"];
1196
1197  /*** append title row (monday - saturday) ***/
1198  [_response appendContentString:@"<tr>"];
1199  for (i = 0; i < (showWeekend ? 6 : 5 ); i++)
1200    [self appendDateTitleToResponse:_response inContext:_ctx day:i];
1201  [_response appendContentString:@"</tr>"];
1202
1203  /**** header rows ****/
1204  [self appendHeaderRowsToResponse:_response inContext:_ctx];
1205
1206  /*** append AM content row + saturday ***/
1207  [_response appendContentString:@"<tr>"];
1208
1209  /* AM weekdays content (this is count=10 because we slot 0=AM/1=PM, etc) */
1210  for (i = 0; i < 10; i = i + 2) {
1211    if ([self->dayIndex isValueSettable])
1212      [self->dayIndex setIntValue:(i / 2) inComponent:comp];
1213
1214    [_response appendContentString:@"<td valign=\"top\""];
1215    if ((bgcolor = [self->contentColor stringValueInComponent:comp])) {
1216      [_response appendContentString:@" bgcolor=\""];
1217      [_response appendContentString:bgcolor];
1218      [_response appendContentCharacter:'"'];
1219    }
1220    if ((style = [self->contentStyle stringValueInComponent:comp])) {
1221      [_response appendContentString:@" class=\""];
1222      [_response appendContentString:style];
1223      [_response appendContentCharacter:'"'];
1224    }
1225    [_response appendContentCharacter:'>'];
1226    [self appendContentToResponse:_response inContext:_ctx index:i];
1227    [_response appendContentString:@"</td>"];
1228  }
1229  /* saturday content */
1230  if (showWeekend) {
1231    if ([self->dayIndex isValueSettable])
1232      [self->dayIndex setIntValue:5 inComponent:comp];
1233
1234    [_response appendContentString:@"<td valign=\"top\""];
1235
1236    if ((bgcolor = [self->contentColor stringValueInComponent:comp])) {
1237      [_response appendContentString:@" bgcolor=\""];
1238      [_response appendContentString:bgcolor];
1239      [_response appendContentCharacter:'"'];
1240    }
1241    if ((style = [self->contentStyle stringValueInComponent:comp])) {
1242      [_response appendContentString:@" class=\""];
1243      [_response appendContentString:style];
1244      [_response appendContentCharacter:'"'];
1245    }
1246    [_response appendContentCharacter:'>'];
1247
1248    /* 10 is Saturday AM, 11 is Saturday PM, week overview shows them in one */
1249    [self appendContentToResponse:_response inContext:_ctx index:10];
1250    [self appendContentToResponse:_response inContext:_ctx index:11];
1251    [_response appendContentString:@"</td>"];
1252  }
1253
1254  [_response appendContentString:@"</tr>"]; /* close AM row */
1255
1256  /*** append PM content row + sunday ***/
1257  [_response appendContentString:@"<tr>"];
1258
1259  /* PM weekdays content */
1260  for (i = 1; i < 11; i = i + 2) {
1261    if ([self->dayIndex isValueSettable])
1262      [self->dayIndex setIntValue:(i/2) inComponent:comp];
1263
1264    [_response appendContentString:@"<td valign=\"top\""];
1265
1266    if (showWeekend) /* for Sunday we need an additional <tr> */
1267      [_response appendContentString:@" rowspan=\"2\""];
1268
1269    if ((bgcolor = [self->contentColor stringValueInComponent:comp])) {
1270      [_response appendContentString:@" bgcolor=\""];
1271      [_response appendContentString:bgcolor];
1272      [_response appendContentCharacter:'"'];
1273    }
1274    if ((style = [self->contentStyle stringValueInComponent:comp])) {
1275      [_response appendContentString:@" class=\""];
1276      [_response appendContentString:style];
1277      [_response appendContentCharacter:'"'];
1278    }
1279    [_response appendContentCharacter:'>'];
1280
1281
1282    [self appendContentToResponse:_response inContext:_ctx index:i];
1283    [_response appendContentString:@"</td>"];
1284  }
1285
1286  if (showWeekend) {
1287    /* sunday title */
1288    [self appendDateTitleToResponse:_response inContext:_ctx day:6];
1289    [_response appendContentString:@"</tr>"];
1290
1291    /*  sunday row */
1292    [_response appendContentString:@"<tr>"];
1293
1294    if ([self->dayIndex isValueSettable])
1295      [self->dayIndex setIntValue:6 inComponent:comp];
1296
1297    [_response appendContentString:@"<td valign=\"top\""];
1298    if ((bgcolor = [self->contentColor stringValueInComponent:comp])) {
1299      [_response appendContentString:@" bgcolor=\""];
1300      [_response appendContentString:bgcolor];
1301      [_response appendContentCharacter:'"'];
1302    }
1303    [_response appendContentCharacter:'>'];
1304
1305    /* 12 is Sunday/AM, 13 is Sunday/PM */
1306    [self appendContentToResponse:_response inContext:_ctx index:12];
1307    [self appendContentToResponse:_response inContext:_ctx index:13];
1308    [_response appendContentString:@"</td>"];
1309  }
1310
1311  [_response appendContentString:@"</tr>"]; /* close PM row */
1312
1313  /***** footer rows *****/
1314  [self appendFooterRowsToResponse:_response inContext:_ctx];
1315
1316  [_response appendContentString:@"</table>"];
1317
1318  [self resetMatrix];
1319}
1320
1321@end /* WeekOverview */
1322
1323@interface WEWeekOverviewTitleMode : WEContextConditional
1324@end
1325
1326@implementation WEWeekOverviewTitleMode
1327- (NSString *)_contextKey {
1328  return WEWeekOverview_TitleMode;
1329}
1330
1331- (NSString *)_didMatchKey {
1332  return WEWeekOverview_TitleModeDidMatch;
1333}
1334@end /* WEWeekOverviewTitleMode */
1335
1336// --
1337
1338@interface WEWeekOverviewInfoMode : WEContextConditional
1339@end
1340
1341@implementation WEWeekOverviewInfoMode
1342- (NSString *)_contextKey {
1343  return WEWeekOverview_InfoMode;
1344}
1345@end /* WEWeekOverviewInfoMode */
1346
1347// --
1348
1349@interface WEWeekOverviewContentMode : WEContextConditional
1350@end
1351
1352@implementation WEWeekOverviewContentMode
1353- (NSString *)_contextKey {
1354  return WEWeekOverview_ContentMode;
1355}
1356@end /* WEWeekOverview_ContentMode */
1357
1358// --
1359
1360@interface WEWeekOverviewPMInfoMode : WEContextConditional
1361@end
1362
1363@implementation WEWeekOverviewPMInfoMode
1364- (NSString *)_contextKey {
1365  return WEWeekOverview_PMInfoMode;
1366}
1367@end /* WEWeekOverviewPMInfoMode */
1368
1369@interface WEWeekOverviewHeaderMode : WEContextConditional
1370@end
1371
1372@implementation WEWeekOverviewHeaderMode
1373- (NSString *)_contextKey {
1374  return WEWeekOverview_HeaderRowMode;
1375}
1376@end /* WEWeekOverviewHeaderMode */
1377
1378@interface WEWeekOverviewFooterMode : WEContextConditional
1379@end
1380
1381@implementation WEWeekOverviewFooterMode
1382- (NSString *)_contextKey {
1383  return WEWeekOverview_FooterRowMode;
1384}
1385@end /* WEWeekOverviewFooterMode */
1386