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 "WETableView.h"
23#include "WETableView+Grouping.h"
24#include "WETableViewState.h"
25#include "WETableViewColorConfig.h"
26#include "WETableViewIconConfig.h"
27#include "WETableViewLabelConfig.h"
28
29#include "common.h"
30#include <NGObjWeb/NGObjWeb.h>
31#include <EOControl/EOSortOrdering.h>
32#include "WEContextConditional.h"
33
34@interface WETableView(JavaScriptAdditions)
35
36- (void)_appendGroupCollapseScript:(WOResponse *)_resp
37                         inContext:(WOContext *)_ctx;
38- (void)jsButton:(WOResponse *)_resp ctx:(WOContext *)_ctx
39  name:(NSString *)_name button:(NSString *)_button;
40
41- (void)appendJavaScript:(WOResponse *)_resp inContext:(WOContext *)_ctx;
42- (void)_appendTableContentAsScript:(WOResponse *)_resp
43  inContext:(WOContext *)_ctx;
44
45- (void)_appendScriptLink:(WOResponse *)_response name:(NSString *)_name;
46- (void)_appendScriptImgName:(WOResponse *)_response name:(NSString *)_name;
47
48@end
49
50#include <NGObjWeb/WEClientCapabilities.h>
51
52@implementation WETableView
53
54static NSNumber *YesNumber = nil;
55static NSNumber *NoNumber  = nil;
56static Class    StrClass   = Nil;
57static BOOL ShowNavigationAlways   = YES;
58static BOOL ShowNavigationInFooter = YES;
59
60+ (void)initialize {
61  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
62
63  StrClass = [NSString class];
64  if (YesNumber == nil) YesNumber = [[NSNumber numberWithBool:YES] retain];
65  if (NoNumber  == nil) NoNumber  = [[NSNumber numberWithBool:NO]  retain];
66
67  ShowNavigationAlways = [ud boolForKey:@"WETableView_showBlindNavigation"];
68}
69
70static NSString *retStrForInt(int i) {
71  switch(i) {
72  case 0:  return @"0";
73  case 1:  return @"1";
74  case 2:  return @"2";
75  case 3:  return @"3";
76  case 4:  return @"4";
77  case 5:  return @"5";
78  case 6:  return @"6";
79  case 7:  return @"7";
80  case 8:  return @"8";
81  case 9:  return @"9";
82  case 10: return @"10";
83    // TODO: find useful count!
84  default:
85    return [[StrClass alloc] initWithFormat:@"%i", i];
86  }
87}
88
89- (id)initWithName:(NSString *)_name
90  associations:(NSDictionary *)_config
91  template:(WOElement *)_c
92{
93  if ((self = [super initWithName:_name associations:_config template:_c])) {
94    self->state = [[WETableViewState alloc] init];
95
96    self->list           = WOExtGetProperty(_config, @"list");
97    self->currentBatch   = WOExtGetProperty(_config, @"currentBatch");
98    self->batchSize      = WOExtGetProperty(_config, @"batchSize");
99
100    self->item           = WOExtGetProperty(_config, @"item");
101    self->index          = WOExtGetProperty(_config, @"index");
102    self->identifier     = WOExtGetProperty(_config, @"identifier");
103    self->previousItem   = WOExtGetProperty(_config, @"previousItem");
104    self->previousIndex  = WOExtGetProperty(_config, @"previousIndex");
105    self->sortedKey      = WOExtGetProperty(_config, @"sortedKey");
106    self->isDescending   = WOExtGetProperty(_config, @"isDescending");
107    self->selection      = WOExtGetProperty(_config, @"selection");
108    self->groups         = WOExtGetProperty(_config, @"groups");
109    self->showGroup      = WOExtGetProperty(_config, @"showGroup");
110
111    // display state
112    self->indexOfFirst =
113      WOExtGetProperty(_config, @"indexOfFirstDisplayedObject");
114    self->indexOfLast  =
115      WOExtGetProperty(_config, @"indexOfLastDisplayedObject");
116
117    self->collapseOnClient = WOExtGetProperty(_config, @"collapseOnClient");
118    self->scrollOnClient   = WOExtGetProperty(_config, @"scrollOnClient");
119    self->autoScroll       = WOExtGetProperty(_config, @"autoScroll");
120    self->showBatchResizeButtons =
121      WOExtGetProperty(_config, @"showBatchResizeButtons");
122
123    // actions
124    self->sortAction     = WOExtGetProperty(_config, @"sortAction");
125    self->firstAction    = WOExtGetProperty(_config, @"firstAction");
126    self->previousAction = WOExtGetProperty(_config, @"previousAction");
127    self->nextAction     = WOExtGetProperty(_config, @"nextAction");
128    self->lastAction     = WOExtGetProperty(_config, @"lastAction");
129
130    // config stuff:
131    self->colors =
132      [[WETableViewColorConfig alloc] initWithAssociations:_config];
133    self->groupColor     = WOExtGetProperty(_config, @"groupColor");
134    self->fontColor      = WOExtGetProperty(_config, @"fontColor");
135    self->fontFace       = WOExtGetProperty(_config, @"fontFace");
136    self->fontSize       = WOExtGetProperty(_config, @"fontSize");
137
138    // icons:
139    self->icons = [[WETableViewIconConfig alloc] initWithAssociations:_config];
140    self->groupOpenedIcon = WOExtGetProperty(_config, @"groupOpenedIcon");
141    self->groupClosedIcon = WOExtGetProperty(_config, @"groupClosedIcon");
142
143    // labels:
144    self->labels =
145      [[WETableViewLabelConfig alloc] initWithAssociations:_config];
146
147    self->cellspacing    = WOExtGetProperty(_config, @"cellspacing");
148    self->cellpadding    = WOExtGetProperty(_config, @"cellpadding");
149    self->border         = WOExtGetProperty(_config, @"border");
150
151    self->showGroupTitle = WOExtGetProperty(_config, @"showGroupTitle");
152
153#define SetAssociationValue(_a_, _value_) \
154         if (_a_ == nil)                  \
155           _a_ = [[WOAssociation associationWithValue:_value_] retain];
156
157    SetAssociationValue(self->cellspacing,   @"0");
158    SetAssociationValue(self->cellpadding,   @"1");
159    SetAssociationValue(self->border,        @"0");
160
161#undef SetAssociationValue
162
163    if (self->list == nil) {
164      [self logWithFormat:@"ERROR: no 'list' binding is set!"];
165      [self release];
166      return nil;
167    }
168
169    self->state->doScriptScrolling = NO;
170    self->template = [_c retain];
171  }
172  return self;
173}
174
175- (void)dealloc {
176  [self->state  release];
177  [self->colors release];
178  [self->labels release];
179  [self->icons  release];
180
181  [self->list          release];
182  [self->currentBatch  release];
183  [self->batchSize     release];
184
185  [self->item          release];
186  [self->index         release];
187  [self->identifier    release];
188  [self->previousItem  release];
189  [self->previousIndex release];
190  [self->sortedKey     release];
191  [self->isDescending  release];
192  [self->selection     release];
193  [self->groups        release];
194  [self->showGroup     release];
195
196  [self->indexOfFirst  release];
197  [self->indexOfLast   release];
198
199  [self->collapseOnClient release];
200  [self->scrollOnClient   release];
201  [self->autoScroll       release];
202  [self->showBatchResizeButtons release];
203
204  [self->sortAction     release];
205  [self->firstAction    release];
206  [self->previousAction release];
207  [self->nextAction     release];
208  [self->lastAction     release];
209
210  [self->fontColor   release];
211  [self->fontFace    release];
212  [self->fontSize    release];
213
214  [self->groupColor      release];
215  [self->groupOpenedIcon release];
216  [self->groupClosedIcon release];
217
218  [self->allObjects release];
219  [self->scriptID   release];
220  [self->template   release];
221
222  [self->cellpadding release];
223  [self->cellspacing release];
224  [self->border release];
225
226  [self->showGroupTitle release];
227
228  [super dealloc];
229}
230
231static inline void
232_applyIdentifier(WETableView *self, WOComponent *comp, NSString *_idx)
233{
234  unsigned count;
235  count = [self->allObjects count];
236
237  if (count > 0) {
238    unsigned i;
239
240    /* find subelement for unique id */
241
242    for (i = 0; i < count; i++) {
243      NSString *ident;
244
245      if (self->index)
246        [self->index setUnsignedIntValue:i inComponent:comp];
247
248      if (self->item) {
249        [self->item setValue:[self->allObjects objectAtIndex:i]
250                    inComponent:comp];
251      }
252      if ([self->previousItem isValueSettable]) {
253        [self->previousItem setValue:(i > self->state->firstIndex)
254             ? [self->allObjects objectAtIndex:i-1] : nil
255             inComponent:comp];
256      }
257      if ([self->previousIndex isValueSettable])
258        [self->previousIndex setUnsignedIntValue:(i-1) inComponent:comp];
259
260      ident = [self->identifier stringValueInComponent:comp];
261
262      if ([ident isEqualToString:_idx]) {
263        /* found subelement with unique id */
264        return;
265      }
266    }
267
268    [comp logWithFormat:
269          @"WETableView: array did change, "
270          @"unique-id isn't contained."];
271    [self->item  setValue:nil          inComponent:comp];
272    [self->index setUnsignedIntValue:0 inComponent:comp];
273  }
274}
275
276static inline void _applyItems_(WETableView *self, WOComponent *cmp, int i) {
277  if ([self->index isValueSettable])
278    [self->index setUnsignedIntValue:i inComponent:cmp];
279  if ([self->item isValueSettable])
280    [self->item setValue:[self->allObjects objectAtIndex:i] inComponent:cmp];
281  if ([self->previousItem isValueSettable]) {
282    id value;
283
284    value = (i > (int)self->state->firstIndex)
285      ? [self->allObjects objectAtIndex:(i - 1)]
286      : nil;
287    [self->previousItem setValue:value inComponent:cmp];
288  }
289  if ([self->previousIndex isValueSettable])
290    [self->previousIndex setUnsignedIntValue:(i-1) inComponent:cmp];
291}
292
293static inline void _applyState_(WETableView *self, WOComponent *cmp) {
294  if ([self->currentBatch isValueSettable])
295    [self->currentBatch setUnsignedIntValue:self->state->currentBatch
296                                inComponent:cmp];
297}
298
299- (void)updateStateInContext:(WOContext *)_ctx {
300  WEClientCapabilities *ccaps;
301  BOOL        isAutoScroll;
302  WOComponent *cmp;
303  NSArray     *array;
304  unsigned    batchIdx, size, cnt;
305
306  cmp            = [_ctx component];
307  ccaps          = [[_ctx request] clientCapabilities];
308  isAutoScroll   = [ccaps doesSupportCSSOverflow];
309  self->state->doScriptScrolling  = [ccaps isInternetExplorer];
310  self->state->doScriptCollapsing = [ccaps isInternetExplorer];
311  if (self->state->doScriptCollapsing) {
312    self->state->doScriptCollapsing =
313      [self->collapseOnClient boolValueInComponent:cmp];
314  }
315
316  if ([[self->autoScroll valueInComponent:cmp] intValue] < 10)
317    isAutoScroll   = NO;
318  else if (isAutoScroll)
319    self->state->doScriptScrolling = NO;
320
321  /* use JavaScript on InternetExplorer only */
322  self->state->doScriptScrolling = (self->state->doScriptScrolling)
323    ? (self->state->batchCount > 1)
324    : NO;
325
326  self->state->doScriptScrolling = (self->state->doScriptScrolling)
327    ? [self->scrollOnClient boolValueInComponent:cmp]
328    : NO;
329
330  array    = [self->list         valueInComponent:cmp];
331  batchIdx = [self->currentBatch unsignedIntValueInComponent:cmp];
332  size     = [self->batchSize    unsignedIntValueInComponent:cmp];
333  cnt      = [array count];
334  size     = (isAutoScroll) ? cnt : size;
335  batchIdx = (batchIdx) ? batchIdx : 1;
336  batchIdx = ((batchIdx * size) < (cnt+size)) ? batchIdx : 1;
337
338  ASSIGN(self->allObjects, array);
339
340  self->state->currentBatch = batchIdx;
341  self->state->batchSize    = size;
342
343  self->state->batchCount = (!size) ? 1 :(cnt / size) + ((cnt % size) ? 1 : 0);
344  self->state->firstIndex = (batchIdx - 1) * size;
345  self->state->lastIndex  =
346    (size == 0 || !((self->state->firstIndex+size) < cnt))
347    ? cnt-1
348    : self->state->firstIndex + size - 1;
349
350  if ([self->indexOfFirst isValueSettable]) {
351    [self->indexOfFirst setUnsignedIntValue:self->state->firstIndex
352                        inComponent:cmp];
353  }
354
355  if ([self->indexOfLast isValueSettable]) {
356    [self->indexOfLast setUnsignedIntValue:self->state->lastIndex
357                       inComponent:cmp];
358  }
359
360  self->state->doCheckBoxes =
361    ([_ctx isInForm] && [self->selection isValueSettable]) ? YES : NO;
362  self->state->doOverflow = isAutoScroll;
363}
364
365- (void)updateScriptIdInContext:(WOContext *)_ctx {
366  NSArray  *tmp;
367  NSString *str;
368
369  tmp = [[_ctx elementID] componentsSeparatedByString:@"."];
370  str = [tmp componentsJoinedByString:@""];
371
372  ASSIGN(self->scriptID, str);
373}
374
375- (void)updateConfigInContext:(WOContext *)_ctx {
376  WOComponent *cmp;
377  NSString    *tmp;
378
379  cmp = [_ctx component];
380
381  [self->colors updateConfigInContext:_ctx];
382  [self->icons  updateConfigInContext:_ctx];
383  [self->labels updateConfigInContext:_ctx];
384
385#define SetConfigInContext(_a_, _key_)                                  \
386      if (_a_ && (tmp = [_a_ valueInComponent:cmp]))                    \
387        [_ctx setObject:tmp forKey:_key_];                              \
388
389  SetConfigInContext(self->fontColor,       WETableView_fontColor);
390  SetConfigInContext(self->fontFace,        WETableView_fontFace);
391  SetConfigInContext(self->fontSize,        WETableView_fontSize);
392#undef SetConfigInContext
393}
394
395- (void)removeConfigInContext:(WOContext *)_ctx {
396  [_ctx removeObjectForKey:WETableView_titleColor];
397  [_ctx removeObjectForKey:WETableView_headerColor];
398  [_ctx removeObjectForKey:WETableView_footerColor];
399  [_ctx removeObjectForKey:WETableView_evenColor];
400  [_ctx removeObjectForKey:WETableView_oddColor];
401  [_ctx removeObjectForKey:WETableView_fontColor];
402  [_ctx removeObjectForKey:WETableView_fontFace];
403  [_ctx removeObjectForKey:WETableView_fontSize];
404
405  [_ctx removeObjectForKey:WETableView_downwardIcon];
406  [_ctx removeObjectForKey:WETableView_upwardIcon];
407  [_ctx removeObjectForKey:WETableView_nonSortIcon];
408
409  [_ctx removeObjectForKey:WETableView_first];
410  [_ctx removeObjectForKey:WETableView_first_blind];
411  [_ctx removeObjectForKey:WETableView_previous];
412  [_ctx removeObjectForKey:WETableView_previous_blind];
413  [_ctx removeObjectForKey:WETableView_next];
414  [_ctx removeObjectForKey:WETableView_next_blind];
415  [_ctx removeObjectForKey:WETableView_last];
416  [_ctx removeObjectForKey:WETableView_last_blind];
417  [_ctx removeObjectForKey:WETableView_select_all];
418  [_ctx removeObjectForKey:WETableView_deselect_all];
419
420  [_ctx removeObjectForKey:WETableView_ofLabel];
421  [_ctx removeObjectForKey:WETableView_toLabel];
422  [_ctx removeObjectForKey:WETableView_firstLabel];
423  [_ctx removeObjectForKey:WETableView_previousLabel];
424  [_ctx removeObjectForKey:WETableView_nextLabel];
425  [_ctx removeObjectForKey:WETableView_lastLabel];
426  [_ctx removeObjectForKey:WETableView_pageLabel];
427  [_ctx removeObjectForKey:WETableView_sortLabel];
428}
429
430- (NSArray *)_collectDataInContext:(WOContext *)_ctx
431{
432  NSAutoreleasePool *pool;
433  NSMutableArray    *matrix    = nil;
434  NSMutableArray    *headInfos = nil;
435  NSString          *k         = nil;
436  WOComponent       *cmp       = nil;
437  id                oldGroup   = nil;
438  int               i, first, last;
439  int               sortedHeadIndex = -2;
440
441  pool   = [[NSAutoreleasePool alloc] init];
442  cmp    = [_ctx component];
443  k      = [self->sortedKey stringValueInComponent:cmp];
444
445  first  = self->state->firstIndex;
446  last   = self->state->lastIndex;
447  matrix = [NSMutableArray arrayWithCapacity:last-first+1];
448
449  [_ctx setObject:YesNumber forKey:WETableView_CollectMode];
450  [_ctx setObject:k         forKey:WETableView_SORTEDKEY];
451
452  self->state->groupCount = 0;
453
454  for (i=first; i<=last; i++) {
455    NSMutableArray *infos = nil;
456    NSString       *tmp   = nil;
457
458    _applyItems_(self, cmp, i);
459
460    [_ctx removeObjectForKey:WETableView_INFOS];
461    [self->template appendToResponse:nil inContext:_ctx];
462    infos = [_ctx objectForKey:WETableView_INFOS];
463
464    if (infos == nil)
465      infos = [NSArray array];
466
467    NSAssert(infos != nil, @"Infos is nil.");
468
469    if (headInfos == nil) {
470      unsigned j, cnt;
471      headInfos = [[NSMutableArray alloc] initWithArray:infos];
472
473      for (j=0, cnt=[headInfos count]; j<cnt; j++) {
474        WETableViewInfo *headInfo = [headInfos objectAtIndex:j];
475
476        headInfo->isEven = (((i-first) % 2) == 0) ? YES : NO;
477      }
478    }
479    else {
480      unsigned j, cnt;
481      BOOL     isEven = NO;
482
483      cnt = [infos count];
484
485      if (sortedHeadIndex == -2) { // first time
486        for (j=0; j < cnt; j++) {
487          WETableViewInfo *info;
488
489          info = [infos objectAtIndex:j];
490          if (info->isSorted) {
491            sortedHeadIndex = j;
492            break;
493          }
494        }
495        sortedHeadIndex = (sortedHeadIndex < 0) ? -1 : sortedHeadIndex;
496      }
497
498      if (cnt) {
499        WETableViewInfo *headInfo;
500        WETableViewInfo *info;
501
502        if (sortedHeadIndex >= 0) {
503          NSAssert(sortedHeadIndex < (int)cnt,
504                   @"SortedHeadIndex out of range!!!");
505          headInfo = [headInfos objectAtIndex:sortedHeadIndex];
506          info     = [infos     objectAtIndex:sortedHeadIndex];
507          isEven = (!info->isGroup) ? !headInfo->isEven : headInfo->isEven;
508        }
509        else { // sortedHeadIndex == -1 --> no column is sorted
510          headInfo = [headInfos lastObject];
511          isEven = !headInfo->isEven;
512        }
513      }
514
515      for (j = 0; j < cnt; j++) {
516        WETableViewInfo *info     = [infos     objectAtIndex:j];
517        WETableViewInfo *headInfo = [headInfos objectAtIndex:j];
518
519        if (!info->isGroup || ((int)j != sortedHeadIndex)) {
520          info->isEven  = isEven;
521          info->isGroup = NO;
522          [headInfos replaceObjectAtIndex:j withObject:info];
523        }
524        else
525          headInfo->rowSpan++;
526      }
527    }
528    {
529      BOOL doGroupTitle = [self->showGroupTitle boolValueInComponent:cmp];
530
531      // insert groupMode (to render the group title)
532      tmp = [self->groups valueInComponent:cmp];
533      if ((tmp != nil) && ![oldGroup isEqual:tmp] && doGroupTitle) {
534        oldGroup = [self->groups valueInComponent:cmp];
535        self->state->groupCount++;
536        [infos addObject:tmp];
537        [infos addObject:WETableView_GroupMode];
538      }
539    }
540
541    [matrix addObject:infos];
542  }
543  [_ctx removeObjectForKey:WETableView_INFOS];
544  [_ctx removeObjectForKey:WETableView_CollectMode];
545  [_ctx removeObjectForKey:WETableView_SORTEDKEY];
546
547  matrix = [matrix retain];
548  [headInfos release];
549  [pool      release];
550
551  return [matrix autorelease];
552}
553
554- (void)_appendNav:(NSString *)_nav isBlind:(BOOL)_isBlind
555  toResponse:(WOResponse *)_response inContext:(WOContext *)_ctx
556{
557  NSString *imgUri;
558  NSString *label;
559  BOOL     doForm   = [_ctx isInForm];
560
561  imgUri = [WETableView_ stringByAppendingString:_nav];
562  imgUri = [imgUri stringByAppendingString:(_isBlind) ? @"_blind" : @""];
563  imgUri = [_ctx objectForKey:imgUri];
564  imgUri = WEUriOfResource(imgUri,_ctx);
565
566  label  = WETableLabelForKey(_nav, _ctx);
567
568  [_response appendContentString:@"<td valign='middle' width='5'>"];
569  // append as submit button
570  if (doForm && !_isBlind && !self->state->doScriptScrolling && imgUri) {
571    [_ctx appendElementIDComponent:_nav];
572    [_response appendContentString:@"<input type=\"image\" border=\"0\""];
573    [_response appendContentString:@" name=\""];
574    [_response appendContentString:[_ctx elementID]];
575    [_response appendContentString:@"\" src=\""];
576    [_response appendContentString:imgUri];
577    [_response appendContentString:@"\" alt=\""];
578    [_response appendContentString:label];
579    [_response appendContentString:@"\" title=\""];
580    [_response appendContentString:label];
581    [_response appendContentString:@"\" />"];
582    [_ctx deleteLastElementIDComponent];
583    [_response appendContentString:@"</td>"];
584    return;
585  }
586
587  /* open anker */
588  if (!_isBlind || self->state->doScriptScrolling) {
589    [_ctx appendElementIDComponent:_nav];
590    [_response appendContentString:@"<a href=\""];
591    if (self->state->doScriptScrolling)
592      [self _appendScriptLink:_response name:_nav];
593    else
594      [_response appendContentString:[_ctx componentActionURL]];
595    [_response appendContentString:@"\">"];
596  }
597  if (imgUri == nil) {
598    [_response appendContentCharacter:'['];
599    [_response appendContentString:label];
600    [_response appendContentCharacter:']'];
601  }
602  else {
603    [_response appendContentString:@"<img border=\"0\" src=\""];
604    [_response appendContentString:imgUri];
605    if (self->state->doScriptScrolling) {
606      [_response appendContentString:@"\" name=\""];
607      [self _appendScriptImgName:_response name:_nav];
608    }
609    [_response appendContentString:@"\" alt=\""];
610    [_response appendContentString:label];
611    [_response appendContentString:@"\" title=\""];
612    [_response appendContentString:label];
613    [_response appendContentString:@"\" />"];
614  }
615  /* close anker */
616  if (!_isBlind || self->state->doScriptScrolling) {
617    [_response appendContentString:@"</a>"];
618    [_ctx deleteLastElementIDComponent];
619  }
620  [_response appendContentString:@"</td>"];
621}
622
623- (void)_appendPreviousNav:(WOResponse *)_resp inContext:(WOContext *)_ctx {
624  int  batch, batchCount;
625  BOOL isFirstBlind, isPreviousBlind, isNextBlind, isLastBlind;
626
627  batch      = self->state->currentBatch;
628  batchCount = self->state->batchCount;
629
630  isFirstBlind    = (batch < 2);
631  isPreviousBlind = (batch < 2);
632  isNextBlind     = ((batchCount-1) < batch);
633  isLastBlind     = ((batchCount-1) < batch);
634
635  if ((ShowNavigationAlways) ||
636      (!(isFirstBlind && isPreviousBlind && isNextBlind && isLastBlind))) {
637    [self _appendNav:@"first"    isBlind:isFirstBlind
638          toResponse:_resp     inContext:_ctx];
639    [self _appendNav:@"previous"   isBlind:isPreviousBlind
640          toResponse:_resp       inContext:_ctx];
641  }
642}
643
644- (void)_appendNextNav:(WOResponse *)_resp inContext:(WOContext *)_ctx {
645  int  batch, batchCount;
646  BOOL isFirstBlind, isPreviousBlind, isNextBlind, isLastBlind;
647
648  batch      = self->state->currentBatch;
649  batchCount = self->state->batchCount;
650
651  isFirstBlind    = (batch < 2);
652  isPreviousBlind = (batch < 2);
653  isNextBlind     = ((batchCount-1) < batch);
654  isLastBlind     = ((batchCount-1) < batch);
655
656  if ((ShowNavigationAlways) ||
657      (!(isFirstBlind && isPreviousBlind && isNextBlind && isLastBlind))) {
658    [self _appendNav:@"next"     isBlind:isNextBlind
659          toResponse:_resp     inContext:_ctx];
660    [self _appendNav:@"last"     isBlind:isLastBlind
661          toResponse:_resp     inContext:_ctx];
662  }
663}
664
665- (void)_appendNavigation:(WOResponse *)_resp inContext:(WOContext *)_ctx {
666  [_resp appendContentString:
667         @"<table border='0' cellspacing='0' cellpadding='0'><tr>"];
668
669  [self _appendPreviousNav:_resp inContext:_ctx];
670
671  /* append extra buttons */
672  [_resp appendContentString:@"<td valign='middle'>"];
673  [_ctx setObject:YesNumber forKey:WETableView_ButtonMode];
674  [_ctx appendElementIDComponent:@"button"];
675  [self->template appendToResponse:_resp     inContext:_ctx];
676  [_ctx deleteLastElementIDComponent];
677  [_ctx removeObjectForKey:WETableView_ButtonMode];
678  [_resp appendContentString:@"</td>"];
679
680  [self _appendNextNav:_resp inContext:_ctx];
681
682  [_resp appendContentString:@"</tr></table>"];
683}
684
685- (void)_appendTitle:(WOResponse *)_response inContext:(WOContext *)_ctx {
686  NSString *bg;
687
688  bg = [_ctx objectForKey:WETableView_titleColor];
689
690  /* open title bar*/
691  [_response appendContentString:
692             @"<table border='0' width='100%' cellpadding='0' cellspacing='0'>"
693             @"<tr>"];
694
695  /* append title */
696  [_ctx setObject:YesNumber forKey:WETableView_TitleMode];
697  [_ctx appendElementIDComponent:@"title"];
698  WEAppendTD(_response, @"left", @"middle", bg);                   // <td..>
699  [self->template appendToResponse:_response inContext:_ctx];
700  [_response appendContentString:@"</td>"];                        // </td>
701  [_ctx deleteLastElementIDComponent]; // delete "title"
702  [_ctx removeObjectForKey:WETableView_TitleMode];
703
704  /* append navigation + extra buttons */
705  WEAppendTD(_response, @"right", @"middle", bg);                  // <td..>
706  [self _appendNavigation:_response inContext:_ctx];
707  [_response appendContentString:@"</td>"];                        // </td>
708
709  /* close title bar*/
710  [_response appendContentString:@"</tr></table>"];
711}
712
713- (void)_appendHeader:(WOResponse *)_response inContext:(WOContext *)_ctx {
714  if (self->sortedKey)
715    [_ctx setObject:[self->sortedKey stringValueInComponent:[_ctx component]]
716             forKey:WETableView_SORTEDKEY];
717  if (self->isDescending)
718    [_ctx setObject:[self->isDescending valueInComponent:[_ctx component]]
719             forKey:WETableView_ISDESCENDING];
720  [_ctx setObject:YesNumber forKey:WETableView_HeaderMode];
721  [_response appendContentString:@"<tr>"];
722
723  [_ctx appendElementIDComponent:@"header"];
724  {
725    NSString *bn;
726    bn = retStrForInt(self->state->currentBatch);
727    [_ctx appendElementIDComponent:bn]; // append batchNumber
728    [bn release];
729  }
730  if (self->state->doCheckBoxes) {
731    NSString *img;
732    NSArray  *selArray;
733    BOOL     doSelectAll;
734
735    selArray    = [self->selection valueInComponent:[_ctx component]];
736    doSelectAll = ([selArray count] < ([self->allObjects count] / 2));
737
738    img = [_ctx objectForKey:(doSelectAll)
739                ? WETableView_select_all
740                : WETableView_deselect_all];
741
742    img = WEUriOfResource(img, _ctx);
743
744    [_response appendContentString:
745               @"<td  align=\"center\" bgcolor=\""];
746    [_response appendContentString:
747               [_ctx objectForKey:WETableView_headerColor]];
748    [_response appendContentString:@"\">"];
749
750    if (doSelectAll)
751      [_ctx appendElementIDComponent:@"_sa"]; // select all
752    else
753      [_ctx appendElementIDComponent:@"_dsa"]; // deselect all
754
755    [_response appendContentString:@"<a href=\""];
756    [_response appendContentString:[_ctx componentActionURL]];
757    [_response appendContentString:@"\">"];
758
759    if (img) {
760      [_response appendContentString:@"<img border=\"0\" src=\""];
761      [_response appendContentString:img];
762      [_response appendContentString:@"\" alt=\""];
763      [_response appendContentString:(doSelectAll)
764                 ? @"selectall"
765                 : @"deselect all"];
766      [_response appendContentString:@"\" title=\""];
767      [_response appendContentString:(doSelectAll)
768                 ? @"selectall"
769                 : @"deselect all"];
770      [_response appendContentString:@"\" />"];
771    }
772    else
773      [_response appendContentString:(doSelectAll) ? @"[+]" : @"[-]"];
774
775    [_response appendContentString:@"</a>"];
776    [_ctx deleteLastElementIDComponent];    // (de)select all
777    [_response appendContentString:@"</td>"];
778  }
779  [self->template appendToResponse:_response inContext:_ctx];
780
781  [_ctx deleteLastElementIDComponent]; // delete batchNumber
782  [_ctx deleteLastElementIDComponent]; // delete "header"
783
784  if (self->state->showBatchResizeButtons) {
785    int cnt;
786
787    cnt = (self->state->lastIndex - self->state->firstIndex + 1);
788
789    if (cnt == (int)self->state->batchSize && !self->state->doOverflow) {
790      [_response appendContentString:@"<td width=\"1%\""];
791      if ([_ctx objectForKey:WETableView_headerColor]) {
792        [_response appendContentString:@" bgcolor=\""];
793        [_response appendContentString:
794                   [_ctx objectForKey:WETableView_headerColor]];
795        [_response appendContentString:@"\""];
796      }
797      [_response appendContentString:@"></td>"];
798    }
799  }
800
801  [_response appendContentString:@"</tr>"];
802  [_ctx removeObjectForKey:WETableView_HeaderMode];
803  [_ctx removeObjectForKey:WETableView_SORTEDKEY];
804  [_ctx removeObjectForKey:WETableView_ISDESCENDING];
805}
806
807- (void)_appendResizeButtons:(WOResponse *)_response
808  actionUrl:(NSString *)_actionUrl
809  inContext:(WOContext *)_ctx
810{
811  NSString *img;
812  NSString *uri;
813
814  // append batchSize--Button
815  img = [self->icons->minusResizeIcon stringValueInComponent:[_ctx component]];
816  img = WEUriOfResource(img, _ctx);
817  uri = [_actionUrl stringByAppendingString:@".mm"];
818
819  if (img && [_ctx isInForm]) {
820    uri = [[uri componentsSeparatedByString:@"/"] lastObject];
821    [_response appendContentString:@"<input type=\"image\" border=\"0\""];
822    [_response appendContentString:@" name=\""];
823    [_response appendContentString:uri];
824    [_response appendContentString:@"\" src=\""];
825    [_response appendContentString:img];
826    [_response appendContentString:@"\" alt=\"minus\" title=\"minus\" />"];
827  }
828  else {
829    [_response appendContentString:@"<a href=\""];
830    [_response appendContentString:uri];
831    [_response appendContentString:@"\">"];
832  }
833
834  if (img && ![_ctx isInForm]) {
835    [_response appendContentString:@"<img border=\"0\" src=\""];
836    [_response appendContentString:img];
837    [_response appendContentString:@"\" alt=\"minus\" title=\"minus\" />"];
838  }
839  else if (!img)
840    [_response appendContentString:@"-"];
841
842  if (!(img && [_ctx isInForm]))
843    [_response appendContentString:@"</a>"];
844
845  // append batchSize--Button
846  img = [self->icons->plusResizeIcon stringValueInComponent:[_ctx component]];
847  img = WEUriOfResource(img, _ctx);
848  uri = [_actionUrl stringByAppendingString:@".pp"] ;
849
850  if (img && [_ctx isInForm]) {
851    uri = [[uri componentsSeparatedByString:@"/"] lastObject];
852    [_response appendContentString:@"<input type=\"image\" border=\"0\""];
853    [_response appendContentString:@" name=\""];
854    [_response appendContentString:uri];
855    [_response appendContentString:@"\" src=\""];
856    [_response appendContentString:img];
857    [_response appendContentString:@"\" alt=\"plus\" title=\"plus\" />"];
858  }
859  else {
860    [_response appendContentString:@"<a href=\""];
861    [_response appendContentString:uri];
862    [_response appendContentString:@"\">"];
863  }
864
865  if (img && ![_ctx isInForm]) {
866    [_response appendContentString:@"<img border=\"0\" src=\""];
867    [_response appendContentString:img];
868    [_response appendContentString:@"\" alt=\"plus\" title=\"plus\" />"];
869  }
870  else if (!img)
871    [_response appendContentString:@"+"];
872
873  if (!(img && [_ctx isInForm]))
874    [_response appendContentString:@"</a>"];
875
876}
877
878- (void)_appendBatchResizeButtons:(WOResponse *)_response
879  rowSpan:(unsigned int)_rowSpan
880  actionUrl:(NSString *)_actionUrl
881  inContext:(WOContext *)_ctx
882{
883  NSString *s;
884
885  // open "td"
886  s = [[StrClass alloc] initWithFormat:
887                  @"<td align='center' valign='bottom' width='5' rowspan='%d'", _rowSpan];
888  [_response appendContentString:s];
889  [s release];
890
891  if ([_ctx objectForKey:WETableView_footerColor]) {
892    [_response appendContentString:@" bgcolor='"];
893    [_response appendContentString:
894               [_ctx objectForKey:WETableView_footerColor]];
895    [_response appendContentCharacter:'\''];
896  }
897  [_response appendContentCharacter:'>'];
898
899      // apppend resize buttons
900  [self _appendResizeButtons:_response
901        actionUrl:_actionUrl
902        inContext:_ctx];
903  // close "td"
904  [_response appendContentString:@"</td>"];
905}
906
907- (void)_appendData:(WOResponse *)_response inContext:(WOContext *)_ctx {
908  WOComponent *comp;
909  NSArray     *matrix;
910  NSString    *batchSizeUrl = nil;
911  NSArray     *selArray     = nil;
912  NSString    *groupId      = nil;
913  BOOL        hideObject    = NO;
914  unsigned    i, cnt, first;
915
916  comp     = [_ctx component];
917  matrix   = [self _collectDataInContext:_ctx];
918  first    = self->state->firstIndex;
919  cnt      = [matrix count];
920
921  if (matrix == nil || cnt == 0)
922    return;
923
924  if (self->state->doCheckBoxes)
925    selArray = [self->selection valueInComponent:comp];
926
927  if (self->state->doScriptCollapsing)
928    [self _appendGroupCollapseScript:_response inContext:_ctx];
929
930  [_ctx appendElementIDComponent:@"data"];
931  {
932    NSString *bn;
933    bn = retStrForInt(self->state->currentBatch);
934    [_ctx appendElementIDComponent:bn]; // append batchNumber
935    [bn release];
936  }
937
938  batchSizeUrl = [_ctx componentActionURL];
939
940  if (self->identifier == nil) {
941    NSString *s;
942
943    s = retStrForInt(first);
944    [_ctx appendElementIDComponent:s];
945    [s release];
946  }
947
948  for (i = 0; i < cnt; i++) {
949    NSMutableArray *infos = nil;
950
951    _applyItems_(self, comp, first+i);
952    if (self->identifier) {
953      NSString *ident;
954
955      ident = [self->identifier stringValueInComponent:comp];
956      [_ctx appendElementIDComponent:ident];
957    }
958
959    infos = [matrix objectAtIndex:i];
960
961    if ([[infos lastObject] isEqual:WETableView_GroupMode]) {
962      unsigned rowSpan;
963
964      groupId = [StrClass stringWithFormat:@"group%d", i];
965
966      rowSpan = ((i==0) && self->state->showBatchResizeButtons)
967        ? cnt+self->state->groupCount
968        : 0;
969      [self _appendGroupTitle:_response
970            inContext:_ctx
971            infos:infos
972            actionUrl:batchSizeUrl
973            rowSpan:rowSpan
974            groupId:groupId];
975
976      if ((self->state->groupCount > 0) && !self->state->doScriptCollapsing &&
977          (self->showGroup) && ![self->showGroup boolValueInComponent:comp])
978        hideObject = YES;
979      else
980        hideObject = NO;
981    }
982
983    [_ctx setObject:infos forKey:WETableView_INFOS];
984
985    if (hideObject) {
986      if (self->identifier == nil)
987        [_ctx incrementLastElementIDComponent];
988      else
989        [_ctx deleteLastElementIDComponent]; // delete identifier
990      continue;
991    }
992
993    [_response appendContentString:@"<tr"];
994    if (groupId) {
995      [_response appendContentString:@" groupName=\""];
996      [_response appendContentString:groupId];
997      [_response appendContentCharacter:'"'];
998      if (self->state->doScriptCollapsing &&
999          self->showGroup && ![self->showGroup boolValueInComponent:comp])
1000        [_response appendContentString:@" style=\"display:none;\""];
1001    }
1002    [_response appendContentCharacter:'>'];
1003
1004    [_ctx setObject:YesNumber forKey:WETableView_DataMode];
1005
1006    if (self->state->doCheckBoxes) {
1007      WETableViewInfo *info = nil;
1008      NSString        *bg   = nil;
1009      NSString *s;
1010
1011      info = ([infos count]) ? [infos objectAtIndex:0] : nil;
1012
1013      bg = (info && info->isEven)
1014        ? [_ctx objectForKey:WETableView_evenColor]
1015        : [_ctx objectForKey:WETableView_oddColor];
1016
1017      [_ctx appendElementIDComponent:@"cb"];
1018      [_response appendContentString:@"<td width=\"15\" align=\"left\""];
1019      [_response appendContentString:@" bgcolor=\""];
1020      [_response appendContentString:bg];
1021      [_response appendContentString:@"\"><input type=\"checkbox\" name=\""];
1022      [_response appendContentHTMLAttributeValue:[_ctx elementID]];
1023      [_response appendContentString:@"\" value=\""];
1024      s = retStrForInt(first + i);
1025      [_response appendContentString:s];
1026      [s release];
1027      [_response appendContentCharacter:'"'];
1028
1029      if ([selArray containsObject:[self->allObjects objectAtIndex:first+i]])
1030        [_response appendContentString:@" checked=\"checked\""];
1031
1032      [_response appendContentString:@" />"];
1033      [_response appendContentString:@"</td>"];
1034
1035      [_ctx deleteLastElementIDComponent]; // delete "cb"
1036    }
1037
1038    [self->template appendToResponse:_response inContext:_ctx];
1039
1040    if (!i && self->state->showBatchResizeButtons &&!self->state->groupCount) {
1041      [self _appendBatchResizeButtons:_response
1042                              rowSpan:cnt+self->state->groupCount
1043                            actionUrl:batchSizeUrl
1044                            inContext:_ctx];
1045    }
1046
1047    [_response appendContentString:@"</tr>"];
1048
1049    if (self->identifier == nil)
1050      [_ctx incrementLastElementIDComponent];
1051    else
1052      [_ctx deleteLastElementIDComponent]; // delete identifier
1053  }
1054  if (self->identifier == nil)
1055    [_ctx deleteLastElementIDComponent]; // delete index
1056  [_ctx deleteLastElementIDComponent];   // delete batchNumber
1057  [_ctx deleteLastElementIDComponent];   // delete "data"
1058  [_ctx removeObjectForKey:WETableView_DataMode];
1059}
1060
1061- (void)_appendFooter:(WOResponse *)_response inContext:(WOContext *)_ctx {
1062  NSString *bg      = nil;
1063  unsigned first    = self->state->firstIndex + 1;
1064  unsigned last     = self->state->lastIndex  + 1;
1065  unsigned count    = [self->allObjects count];
1066  unsigned batch    = self->state->currentBatch;
1067  unsigned batchCnt = self->state->batchCount;
1068  NSString *s;
1069
1070  first    = (count)    ? first    : 0;
1071  batchCnt = (batchCnt) ? batchCnt : 1;
1072  bg       = [_ctx objectForKey:WETableView_footerColor];
1073
1074  [_response appendContentString:
1075             @"<table border='0' width='100%' cellpadding='0' cellspacing='0'>"];
1076  [_response appendContentString:@"<tr>"];                        // <TR>
1077
1078  WEAppendTD(_response, @"left", nil, bg);                        //   <TD...>
1079
1080  [_ctx setObject:YesNumber forKey:WETableView_FooterMode];
1081  [_ctx appendElementIDComponent:@"footer"];
1082  [self->template appendToResponse:_response inContext:_ctx];
1083  [_ctx deleteLastElementIDComponent];
1084  [_ctx removeObjectForKey:WETableView_FooterMode];
1085
1086  [_response appendContentString:@"<small>"];
1087  if (!self->state->doOverflow) {
1088    s = [[StrClass alloc] initWithFormat:@"%d ", first];
1089    [_response appendContentString:s];
1090    [s release];
1091    [_response appendContentString:WETableLabelForKey(@"to", _ctx)];
1092    s = [[StrClass alloc] initWithFormat:@" %d ", last];
1093    [_response appendContentString:s];
1094    [s release];
1095    [_response appendContentString:WETableLabelForKey(@"of", _ctx)];
1096  }
1097  s = [[StrClass alloc] initWithFormat:@" %d", count];
1098  [_response appendContentString:s];
1099  [s release];
1100  [_response appendContentString:@"</small>"];
1101
1102  [_response appendContentString:@"</td>"];                       // </td>
1103
1104  WEAppendTD(_response, @"right", nil, bg);                     // <td...>
1105  if (!self->state->doOverflow) {
1106    if (ShowNavigationInFooter) {
1107      [_response appendContentString:
1108                 @"<table border='0' cellpadding='0' cellspacing='0'><tr>"];
1109      [self _appendPreviousNav:_response inContext:_ctx];
1110      WEAppendTD(_response, nil, nil, bg);                        // <td...>
1111    }
1112    [_response appendContentString:@"<small>"];
1113    [_response appendContentString:@"&nbsp;"];
1114    [_response appendContentString:WETableLabelForKey(@"page", _ctx)];
1115    s = [[StrClass alloc] initWithFormat:@": %d ", batch];
1116    [_response appendContentString:s];
1117    [s release];
1118    [_response appendContentString:WETableLabelForKey(@"of", _ctx)];
1119    s = [[StrClass alloc] initWithFormat:@" %d", batchCnt];
1120    [_response appendContentString:s];
1121    [s release];
1122    [_response appendContentString:@"&nbsp;"];
1123    [_response appendContentString:@"</small>"];
1124    if (ShowNavigationInFooter) {
1125      [_response appendContentString:@"</td>"];
1126      [self _appendNextNav:_response inContext:_ctx];
1127      [_response appendContentString:@"</tr></table>"];
1128    }
1129  }
1130  else {
1131    [self _appendResizeButtons:_response
1132          actionUrl:[_ctx componentActionURL]
1133          inContext:_ctx];
1134  }
1135
1136  [_response appendContentString:@"</td></tr>"];                // </td></tr>
1137  [_response appendContentString:@"</table>"];                  // </table>
1138}
1139
1140// --- action handler -----------------------------------------------------
1141
1142- (void)_handleSortActionInContext:(WOContext *)_ctx {
1143  WOComponent *cmp;
1144  NSString    *key;
1145  NSString    *oldKey;
1146  BOOL        isDesc;
1147  BOOL        oldIsDesc;
1148
1149  if (self->sortedKey == nil || self->isDescending == nil)
1150    return; // nothing to do
1151
1152  if ([_ctx objectForKey:WETableView_SORTEDKEY] == nil ||
1153      [_ctx objectForKey:WETableView_ISDESCENDING] == nil)
1154    return; // nothing to do
1155
1156  cmp    = [_ctx component];
1157  key    = [_ctx  objectForKey:WETableView_SORTEDKEY];
1158  isDesc = [[_ctx objectForKey:WETableView_ISDESCENDING] boolValue];
1159
1160  oldIsDesc = [self->isDescending boolValueInComponent:cmp];
1161  oldKey    = [self->sortedKey  stringValueInComponent:cmp];
1162
1163  if ([oldKey isEqual:key] && oldIsDesc == isDesc)
1164    return; // nothing to do
1165
1166  if ([self->isDescending isValueSettable])
1167    [self->isDescending setBoolValue:isDesc inComponent:cmp];
1168  if ([self->sortedKey isValueSettable])
1169    [self->sortedKey setStringValue:key inComponent:cmp];
1170
1171  if (self->sortAction == nil && key != nil) {
1172    EOSortOrdering *so;
1173    NSArray        *soArray;
1174    SEL            sel;
1175    NSArray        *tmp;
1176
1177    sel = (isDesc) ? EOCompareDescending : EOCompareAscending;
1178    so  = [EOSortOrdering sortOrderingWithKey:key selector:sel];
1179
1180    soArray = [[NSArray alloc] initWithObjects:&so count:1];
1181    tmp = [self->allObjects sortedArrayUsingKeyOrderArray:soArray];
1182    [soArray release];
1183
1184    if ([self->list isValueSettable])
1185      [self->list setValue:tmp inComponent:[_ctx component]];
1186    else {
1187      [[_ctx component] debugWithFormat:
1188                          @"couldn't set sorted list on 'list' binding"];
1189    }
1190  }
1191  else if (self->sortAction)
1192    [self->sortAction valueInComponent:cmp];
1193}
1194
1195- (void)_handleFirstButtonInContext:(WOContext *)_ctx {
1196  if (self->firstAction)
1197    [self->firstAction valueInComponent:[_ctx component]];
1198  else if (self->state->currentBatch != 1) {
1199    self->state->currentBatch = 1;
1200    _applyState_(self, [_ctx component]);
1201  }
1202}
1203
1204- (void)_handlePreviousButtonInContext:(WOContext *)_ctx {
1205  if (self->previousAction)
1206    [self->previousAction valueInComponent:[_ctx component]];
1207  else {
1208    unsigned batch = self->state->currentBatch;
1209
1210    self->state->currentBatch = ((batch -1) > 0) ? batch - 1 : 1;
1211    _applyState_(self, [_ctx component]);
1212  }
1213}
1214
1215- (void)_handleNextButtonInContext:(WOContext *)_ctx {
1216  if (self->nextAction)
1217    [self->nextAction valueInComponent:[_ctx component]];
1218  else {
1219    unsigned batch = self->state->currentBatch;
1220    unsigned cnt   = self->state->batchCount;
1221
1222    self->state->currentBatch = ((batch +1) < cnt) ? batch + 1 : cnt;
1223    _applyState_(self, [_ctx component]);
1224  }
1225}
1226
1227- (void)_handleLastButtonInContext:(WOContext *)_ctx {
1228  if (self->lastAction)
1229    [self->lastAction valueInComponent:[_ctx component]];
1230  else {
1231    self->state->currentBatch = self->state->batchCount;
1232    _applyState_(self, [_ctx component]);
1233  }
1234}
1235
1236/* handle request */
1237
1238- (void)takeValuesFromRequest:(WORequest *)_request
1239  forBatch:(int)_batch
1240  selections:(NSMutableArray *)_selArray
1241  inContext:(WOContext *)_ctx
1242{
1243  NSString *eid, *s;
1244  int      i, first, last;
1245
1246  first = self->state->firstIndex;
1247  last  = self->state->lastIndex;
1248
1249  {
1250    NSString *bn;
1251    bn = retStrForInt(self->state->currentBatch);
1252    [_ctx appendElementIDComponent:bn]; // append batchNumber
1253    [bn release];
1254  }
1255
1256  eid = [_ctx elementID];
1257
1258  if ([_request formValueForKey:[eid stringByAppendingString:@".pp.x"]]) {
1259    [_ctx addActiveFormElement:self];
1260    [_ctx setRequestSenderID:
1261          [[_ctx senderID] stringByAppendingString:@".pp"]];
1262  }
1263  else if ([_request formValueForKey:[eid stringByAppendingString:@".mm.x"]]) {
1264    [_ctx addActiveFormElement:self];
1265    [_ctx setRequestSenderID:
1266          [[_ctx senderID] stringByAppendingString:@".mm"]];
1267  }
1268
1269  if (self->identifier == nil) { // append index
1270    s = retStrForInt(first);
1271    [_ctx appendElementIDComponent:s];
1272    [s release];
1273  }
1274
1275  for (i = first; i <= last; i++) {
1276    _applyItems_(self, [_ctx component], i);
1277    if (self->identifier) {
1278      NSString *s;
1279
1280      s = [self->identifier stringValueInComponent:[_ctx component]];
1281      [_ctx appendElementIDComponent:s];
1282    }
1283
1284    if (_selArray) {
1285      NSString *cbID; // checkBoxID
1286      id       formValue;
1287      id       obj;
1288
1289      cbID = [[_ctx elementID] stringByAppendingString:@".cb"];
1290      obj  = [self->item valueInComponent:[_ctx component]];
1291
1292      if (obj) {
1293        if ((formValue = [_request formValueForKey:cbID])) {
1294          if (![_selArray containsObject:obj])
1295            [_selArray addObject:obj];
1296        }
1297        else if ([_selArray containsObject:obj])
1298          [_selArray removeObject:obj];
1299      }
1300    }
1301    [self->template takeValuesFromRequest:_request inContext:_ctx];
1302
1303    if (self->identifier == nil)
1304      [_ctx incrementLastElementIDComponent];
1305    else
1306      [_ctx deleteLastElementIDComponent]; // delete identifier
1307  }
1308  if (self->identifier == nil)
1309    [_ctx deleteLastElementIDComponent]; // delete index
1310
1311  [_ctx deleteLastElementIDComponent]; // delete batchNumber
1312}
1313
1314- (void)takeValuesFromRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
1315  int            i, firstBatch, lastBatch, savedCurrentBatch;
1316  NSString       *eid;
1317  NSMutableArray *selArray = nil;
1318
1319  [self updateStateInContext:_ctx];
1320
1321  eid      = [_ctx elementID];
1322
1323  /* handle "data" section */
1324
1325  if (self->state->doCheckBoxes) {
1326    selArray = [self->selection valueInComponent:[_ctx component]];
1327    selArray = [selArray mutableCopyWithZone:[self zone]];
1328  }
1329
1330  firstBatch = self->state->doScriptScrolling ? 1 : self->state->currentBatch;
1331  lastBatch  = (self->state->doScriptScrolling)
1332    ? self->state->batchCount
1333    : self->state->currentBatch;
1334
1335  [_ctx appendElementIDComponent:@"data"];
1336  [_ctx setObject:YesNumber forKey:WETableView_DataMode];
1337
1338  savedCurrentBatch = self->state->currentBatch;
1339  for (i = firstBatch; i <= lastBatch; i++) {
1340    self->state->currentBatch = i;
1341    _applyState_(self, [_ctx component]);
1342    [self updateStateInContext:_ctx];
1343
1344    [self takeValuesFromRequest:_rq
1345          forBatch:i
1346          selections:selArray
1347          inContext:_ctx];
1348  }
1349  [_ctx removeObjectForKey:WETableView_DataMode];
1350
1351  if (self->state->currentBatch != (unsigned)savedCurrentBatch) {
1352    self->state->currentBatch = savedCurrentBatch;
1353    _applyState_(self, [_ctx component]);
1354  }
1355
1356  [_ctx deleteLastElementIDComponent]; // delete "data"
1357
1358  if (self->state->doCheckBoxes) {
1359    [self->selection setValue:selArray inComponent:[_ctx component]];
1360    [selArray release];
1361  }
1362
1363  // handle header (sort buttons, ...)
1364  [_ctx setObject:YesNumber forKey:WETableView_HeaderMode];
1365  [_ctx appendElementIDComponent:@"header"];
1366
1367  for (i = 1; i <= (int)self->state->batchCount; i++) {
1368    NSString *s;
1369
1370    // TODO: improve
1371    s = retStrForInt(i);
1372    [_ctx appendElementIDComponent:s];
1373    [s release];
1374
1375    [self->template takeValuesFromRequest:_rq inContext:_ctx];
1376    [_ctx deleteLastElementIDComponent]; // delete batchNumber
1377  }
1378
1379  [_ctx deleteLastElementIDComponent]; // delete "header"
1380  [_ctx removeObjectForKey:WETableView_HeaderMode];
1381
1382  // handle title
1383  [_ctx setObject:YesNumber forKey:WETableView_TitleMode];
1384  [_ctx appendElementIDComponent:@"title"];
1385  [self->template takeValuesFromRequest:_rq inContext:_ctx];
1386  [_ctx deleteLastElementIDComponent]; // delete "title"
1387  [_ctx removeObjectForKey:WETableView_TitleMode];
1388
1389  // handle buttons
1390  [_ctx setObject:YesNumber forKey:WETableView_ButtonMode];
1391  [_ctx appendElementIDComponent:@"button"];
1392  [self->template takeValuesFromRequest:_rq inContext:_ctx];
1393  [_ctx deleteLastElementIDComponent]; // delete "button"
1394  [_ctx removeObjectForKey:WETableView_ButtonMode];
1395
1396  // handle footer
1397  [_ctx setObject:YesNumber forKey:WETableView_FooterMode];
1398  [_ctx appendElementIDComponent:@"footer"];
1399
1400  // reset autoScrollHeight
1401  if ([_rq formValueForKey:[eid stringByAppendingString:@".footer.pp.x"]]) {
1402    [_ctx addActiveFormElement:self];
1403    [_ctx setRequestSenderID:
1404          [[_ctx senderID] stringByAppendingString:@".pp"]];
1405  }
1406  else if ([_rq formValueForKey:
1407		  [eid stringByAppendingString:@".footer.mm.x"]]) {
1408    [_ctx addActiveFormElement:self];
1409    [_ctx setRequestSenderID:
1410          [[_ctx senderID] stringByAppendingString:@".mm"]];
1411  }
1412
1413  [self->template takeValuesFromRequest:_rq inContext:_ctx];
1414  [_ctx deleteLastElementIDComponent]; // delete "footer"
1415  [_ctx removeObjectForKey:WETableView_FooterMode];
1416
1417  if ([_rq formValueForKey:[eid stringByAppendingString:@".first.x"]]) {
1418    [_ctx addActiveFormElement:self];
1419    [_ctx setRequestSenderID:
1420          [[_ctx senderID] stringByAppendingString:@".first"]];
1421  }
1422  if ([_rq formValueForKey:[eid stringByAppendingString:@".next.x"]]) {
1423    [_ctx addActiveFormElement:self];
1424    [_ctx setRequestSenderID:
1425          [[_ctx senderID] stringByAppendingString:@".next"]];
1426  }
1427  if ([_rq formValueForKey:[eid stringByAppendingString:@".last.x"]]) {
1428    [_ctx addActiveFormElement:self];
1429    [_ctx setRequestSenderID:
1430          [[_ctx senderID] stringByAppendingString:@".last"]];
1431  }
1432  if ([_rq formValueForKey:[eid stringByAppendingString:@".previous.x"]]) {
1433    [_ctx addActiveFormElement:self];
1434    [_ctx setRequestSenderID:
1435          [[_ctx senderID] stringByAppendingString:@".previous"]];
1436  }
1437}
1438
1439- (id)increaseAutoScrollHeightInContext:(WOContext *)_ctx {
1440  if ([self->autoScroll isValueSettable]) {
1441    int sh; // scrollHeight
1442
1443    sh = [self->autoScroll intValueInComponent:[_ctx component]] + 20;
1444    [self->autoScroll setIntValue:sh inComponent:[_ctx component]];
1445  }
1446  return nil;
1447}
1448
1449- (id)decreaseAutoScrollHeightInContext:(WOContext *)_ctx {
1450  if ([self->autoScroll isValueSettable]) {
1451    int sh; // scrollHeight
1452
1453    sh = [self->autoScroll intValueInComponent:[_ctx component]] - 20;
1454    if (sh > 50)
1455      [self->autoScroll setIntValue:sh inComponent:[_ctx component]];
1456  }
1457  return nil;
1458}
1459
1460
1461- (id)increaseBatchSizeInContext:(WOContext *)_ctx {
1462  if ([self->batchSize isValueSettable]) {
1463    int bs;
1464
1465    bs = [self->batchSize intValueInComponent:[_ctx component]] + 1;
1466    [self->batchSize setIntValue:bs inComponent:[_ctx component]];
1467  }
1468  return nil;
1469}
1470
1471- (id)decreaseBatchSizeInContext:(WOContext *)_ctx {
1472  if ([self->batchSize isValueSettable]) {
1473    int bs;
1474
1475    bs = [self->batchSize intValueInComponent:[_ctx component]] - 1;
1476    if (bs > 1)
1477      [self->batchSize setIntValue:bs inComponent:[_ctx component]];
1478  }
1479  return nil;
1480}
1481
1482- (id)invokeActionForRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
1483  WOComponent    *cmp;
1484  NSString       *eid;
1485  id             result = nil;
1486
1487  [self updateStateInContext:_ctx];
1488
1489  eid = [_ctx currentElementID];
1490  cmp = [_ctx component];
1491
1492  if ([eid isEqual:@"first"])
1493    [self _handleFirstButtonInContext:_ctx];
1494  else if ([eid isEqual:@"previous"])
1495    [self _handlePreviousButtonInContext:_ctx];
1496  else if ([eid isEqual:@"next"])
1497    [self _handleNextButtonInContext:_ctx];
1498  else if ([eid isEqual:@"last"])
1499    [self _handleLastButtonInContext:_ctx];
1500  else if ([eid isEqual:@"data"]) {
1501    NSString *idxId;
1502
1503    [_ctx consumeElementID];             // consume "data"
1504    [_ctx appendElementIDComponent:eid]; // append  "data"
1505
1506    {
1507      NSString *bn;
1508
1509      bn = [_ctx currentElementID];
1510      if ([self->currentBatch isValueSettable])
1511        [self->currentBatch setIntValue:[bn intValue] inComponent:cmp];
1512
1513      [_ctx consumeElementID];            // consume batchNumber
1514      [_ctx appendElementIDComponent:bn]; // append batch
1515    }
1516
1517    if ((idxId = [_ctx currentElementID])) {
1518      [_ctx consumeElementID];               // consume index-id
1519      [_ctx appendElementIDComponent:idxId]; // append index-id
1520
1521      // reset batchSize
1522      if ([idxId isEqualToString:@"pp"])
1523        result = [self increaseBatchSizeInContext:_ctx];
1524      else if ([idxId isEqualToString:@"mm"])
1525        result = [self decreaseBatchSizeInContext:_ctx];
1526      else {
1527        if (self->identifier == nil) {
1528          NSInteger idx;
1529
1530          idx   = [idxId integerValue];
1531          if (idx < [self->allObjects count] && idx >= 0) {
1532            _applyItems_(self, cmp, idx);
1533          }
1534          else
1535            NSLog(@"WETableView: index is out of range!");
1536        }
1537        else
1538          _applyIdentifier(self, cmp, idxId);
1539
1540        result = [self invokeGrouping:_rq inContext:_ctx];
1541      }
1542      [_ctx deleteLastElementIDComponent]; // delete index-id
1543    }
1544    [_ctx deleteLastElementIDComponent]; // delete batchNumber
1545    [_ctx deleteLastElementIDComponent]; // delete "data"
1546  }
1547  else if ([eid isEqual:@"header"]) {
1548    [_ctx consumeElementID];             // consume "header"
1549    [_ctx appendElementIDComponent:eid]; // append  "header"
1550
1551    if ([self->currentBatch isValueSettable]) {
1552      int bn = [[_ctx currentElementID] intValue];
1553      [self->currentBatch setIntValue:bn inComponent:cmp];
1554    }
1555    [_ctx appendElementIDComponent:[_ctx currentElementID]]; // batchNumber
1556    [_ctx consumeElementID];                         // consume batchNumber
1557
1558    // handle selectAllCheckBoxes:
1559    if ([[_ctx currentElementID] isEqualToString:@"_sa"]) {
1560      NSMutableArray *selArray;
1561
1562      selArray = [self->allObjects mutableCopyWithZone:[self zone]];
1563      [self->selection setValue:selArray inComponent:cmp];
1564      [selArray release];
1565    }
1566    // handle deselectAllCheckBoxes:
1567    else if ([[_ctx currentElementID] isEqualToString:@"_dsa"]) {
1568      [self->selection setValue:[NSMutableArray array] inComponent:cmp];
1569    }
1570    else
1571      result = [self->template invokeActionForRequest:_rq inContext:_ctx];
1572
1573    [_ctx deleteLastElementIDComponent]; // delete batchNumber
1574    [_ctx deleteLastElementIDComponent]; // delete "header"
1575
1576    [self _handleSortActionInContext:_ctx];
1577
1578    [_ctx removeObjectForKey:WETableView_SORTEDKEY];
1579    [_ctx removeObjectForKey:WETableView_ISDESCENDING];
1580  }
1581  else if ([eid isEqual:@"title"] || [eid isEqual:@"button"] ||
1582           [eid isEqual:@"footer"]) {
1583    [_ctx consumeElementID];
1584    [_ctx appendElementIDComponent:eid];
1585
1586    eid = [_ctx currentElementID];
1587
1588    // reset autoScrollHeight
1589    if ([eid isEqualToString:@"pp"])
1590      result = [self increaseAutoScrollHeightInContext:_ctx];
1591    else if ([eid isEqualToString:@"mm"])
1592      result = [self decreaseAutoScrollHeightInContext:_ctx];
1593    else
1594      result = [self->template invokeActionForRequest:_rq inContext:_ctx];
1595
1596    [_ctx deleteLastElementIDComponent];
1597  }
1598  else
1599    result = [self->template invokeActionForRequest:_rq inContext:_ctx];
1600
1601  return result;
1602}
1603
1604- (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
1605  /*
1606    The main HTML of the tableview are two tables:
1607    - a table with the: title+buttons, header and content
1608      this table has three rows:
1609      - a row for the title+buttons
1610      - some kind of padding row?
1611      - a row for the header and the content
1612        this is just a single cell with an embedded table with one row for
1613        the header and n-rows for the content
1614    - a separate table with the footer
1615  */
1616  WOComponent *cmp;
1617
1618  if ([_ctx isRenderingDisabled]) {
1619    [self->template appendToResponse:_response inContext:_ctx];
1620    return;
1621  }
1622
1623  [self updateStateInContext:_ctx];
1624  [self updateScriptIdInContext:_ctx];
1625  [self updateConfigInContext:_ctx];
1626
1627  cmp = [_ctx component];
1628
1629  /* open main tableView */
1630  // TODO: add CSS
1631  [_response appendContentString:
1632	       @"<table border='0' width='100%' "
1633	       @"cellpadding='0' cellspacing='0'>"];
1634
1635  /* append tableTitle + navigation */
1636  [_response appendContentString:@"<tr><td>"];
1637  [self _appendTitle:_response inContext:_ctx];
1638  [_response appendContentString:@"</td></tr>"];
1639
1640  [_response appendContentString:@"<tr><td></td></tr>"];
1641
1642  if (self->state->doScriptScrolling) {
1643    [self _appendTableContentAsScript:_response inContext:_ctx]; //close tables
1644  }
1645  else {
1646    /* open header + data area */
1647    [_response appendContentString:@"<tr><td>"];
1648
1649    if (self->state->doOverflow) {
1650      [_response appendContentString:
1651                 @"<p style=\"width:100%; height: "];
1652      [_response appendContentString:
1653                   [self->autoScroll stringValueInComponent:cmp]];
1654      [_response appendContentString:@"; overflow-y: auto\">"];
1655    }
1656
1657    [_response appendContentString:@"<table width='100%' border='"];
1658    [_response appendContentString:[self->border stringValueInComponent:cmp]];
1659    [_response appendContentString:@"' cellpadding='"];
1660    [_response appendContentString:
1661               [self->cellpadding stringValueInComponent:cmp]];
1662    [_response appendContentString:@"' cellspacing='"];
1663    [_response appendContentString:
1664               [self->cellspacing stringValueInComponent:cmp]];
1665    [_response appendContentString:@"'>"];
1666
1667
1668    self->state->showBatchResizeButtons =
1669      ([self->showBatchResizeButtons boolValueInComponent:cmp] &&
1670       (self->state->currentBatch < self->state->batchCount) &&
1671       !self->state->doOverflow);
1672
1673    [self _appendHeader:_response inContext:_ctx];
1674    [self _appendData:_response   inContext:_ctx];
1675
1676    [_response appendContentString:@"</table>"];
1677    if (self->state->doOverflow)
1678      [_response appendContentString:@"</p>"];
1679
1680    /* close header + data area */
1681    [_response appendContentString:@"</td></tr>"];
1682
1683    [_response appendContentString:@"</table>"];                  // </TABLE>
1684
1685    /* append footer */
1686    [self _appendFooter:_response inContext:_ctx];
1687  }
1688
1689  // close tableView
1690
1691
1692  if (self->state->doScriptScrolling)
1693    [self appendJavaScript:_response inContext:_ctx];
1694
1695  [self removeConfigInContext:_ctx];
1696}
1697
1698@end /* WETableView */
1699
1700@implementation WETableViewInfo
1701@end
1702
1703// --- JavaScript additions -------------------------------------------------
1704
1705@implementation WETableView(JavaScriptAdditions)
1706
1707- (void)_appendGroupCollapseScript:(WOResponse *)_resp
1708  inContext:(WOContext *)_ctx
1709{
1710  if ([_ctx objectForKey:WETableView_HasCollapseScript])
1711    return;
1712
1713  [_resp appendContentString:
1714           @"\n<script language=\"JavaScript\">\n"
1715           @"<!--\n"
1716           @"function toggleTableGroup()\n"
1717           @"{\n"
1718           @"   img = event.srcElement;\n"
1719           @"   visibility = img.isGroupVisible;\n"
1720           @"   visibility = (visibility != \"none\") ? \"none\" : \"\";\n"
1721           @"   img.isGroupVisible = visibility;\n"
1722           @"   img.src = (visibility == \"\") ? img.openImg : img.closeImg;\n"
1723           @"   groupName  = img.group;\n"
1724           @"   table  = img.parentNode.parentNode.parentNode;\n"
1725           @"   trList = table.getElementsByTagName(\"TR\");\n"
1726           @"   cnt    = trList.length;\n"
1727           @"   for (i=0; i<cnt; i++) {\n"
1728           @"     tr = trList[i];\n"
1729           @"     if (tr.groupName == groupName)\n"
1730           @"       tr.style.display = visibility;\n"
1731           @"   }\n"
1732           @"}\n"
1733           @"//-->\n"
1734           @"</script>\n"];
1735  [_ctx setObject:YesNumber forKey:WETableView_HasCollapseScript];
1736}
1737
1738- (void)jsButton:(WOResponse *)_resp ctx:(WOContext *)_ctx
1739  name:(NSString *)_name button:(NSString *)_button
1740{
1741  NSString *imgUri;
1742  NSString *n;
1743
1744  _button = [_button stringByAppendingString:@".gif"];
1745  imgUri  = WEUriOfResource(_button, _ctx);
1746  n       = [_name stringByAppendingString:self->scriptID];
1747
1748  n = [[StrClass alloc] initWithFormat:
1749          @"var %@ = new Image(); %@.src = \"%@\";\n", n, n, imgUri];
1750  [_resp appendContentString:n];
1751  [n release];
1752}
1753
1754- (void)appendJavaScript:(WOResponse *)_resp inContext:(WOContext *)_ctx {
1755  NSString *n;
1756
1757  [_resp appendContentString:@"<script language=\"JavaScript\">\n<!--\n"];
1758
1759  [self jsButton:_resp ctx:_ctx name:@"First"     button:@"first"];
1760  [self jsButton:_resp ctx:_ctx name:@"First2"    button:@"first_blind"];
1761  [self jsButton:_resp ctx:_ctx name:@"Previous"  button:@"previous"];
1762  [self jsButton:_resp ctx:_ctx name:@"Previous2" button:@"previous_blind"];
1763  [self jsButton:_resp ctx:_ctx name:@"Next"      button:@"next"];
1764  [self jsButton:_resp ctx:_ctx name:@"Next2"     button:@"next_blind"];
1765  [self jsButton:_resp ctx:_ctx name:@"Last"      button:@"last"];
1766  [self jsButton:_resp ctx:_ctx name:@"Last2"     button:@"last_blind"];
1767
1768  n = [[StrClass alloc] initWithFormat:
1769    @"function showPage%@() {\n"
1770    @"  for (var i=1; i< page%@.length; i++) {\n"
1771	@"    if (i == actualPage%@) {\n"
1772    @"      page%@[i][\"Div\"].style.display = \"\";\n"
1773    @"      footer%@[i][\"Div\"].style.display = \"\";\n"
1774    @"    }\n"
1775	@"    else {\n"
1776    @"      page%@[i][\"Div\"].style.display = \"none\";\n"
1777    @"      footer%@[i][\"Div\"].style.display = \"none\";\n"
1778    @"    }\n"
1779	@"	}\n"
1780	@"	flushImages%@();\n"
1781	@"}\n",
1782    self->scriptID, // showPage
1783    self->scriptID, // page.length
1784    self->scriptID, // actualPage
1785    self->scriptID, // page
1786    self->scriptID, // footer
1787    self->scriptID, // page
1788    self->scriptID, // footer
1789    self->scriptID  // flushImages
1790  ];
1791  [_resp appendContentString:n];
1792  [n release];
1793
1794  n = [[StrClass alloc] initWithFormat:
1795    @"function firstPage%@() {\n"
1796    @"  actualPage%@ = 1;\n"
1797    @"  showPage%@();\n"
1798    @"}\n",
1799    self->scriptID, // firstPage
1800    self->scriptID, // actualPage
1801    self->scriptID  // showPage
1802  ];
1803  [_resp appendContentString:n];
1804  [n release];
1805
1806  n = [[StrClass alloc] initWithFormat:
1807    @"function previousPage%@() {\n"
1808    @"	if (actualPage%@ > 1) {\n"
1809    @"    actualPage%@--;\n"
1810    @"    showPage%@();\n"
1811    @"  }\n"
1812	@"}\n",
1813    self->scriptID, // previousPage
1814    self->scriptID, // actualPage
1815    self->scriptID, // actualPage
1816    self->scriptID  // showPage
1817  ];
1818  [_resp appendContentString:n];
1819  [n release];
1820
1821  n = [[StrClass alloc] initWithFormat:
1822    @"function nextPage%@() {\n"
1823    @"  if (actualPage%@ < page%@.length - 1) {\n"
1824    @"    actualPage%@++;\n"
1825    @"    showPage%@();\n"
1826    @"	}\n"
1827	@"}\n",
1828    self->scriptID, // nextPage
1829    self->scriptID, // actualPage
1830    self->scriptID, // page
1831    self->scriptID, // actualPage
1832    self->scriptID  // showPage
1833  ];
1834  [_resp appendContentString:n];
1835  [n release];
1836
1837  n = [[StrClass alloc] initWithFormat:
1838    @"function lastPage%@() {\n"
1839    @"  actualPage%@ = page%@.length - 1;\n"
1840    @"  showPage%@();\n"
1841    @"}\n",
1842    self->scriptID, // lastPage
1843    self->scriptID, // actualPage
1844    self->scriptID, // page
1845    self->scriptID  // showPage
1846  ];
1847  [_resp appendContentString:n];
1848  [n release];
1849
1850  n = [[StrClass alloc] initWithFormat:
1851    @"function flushImages%@() {\n"
1852    @"  document.images[\"firstPageImg%@\"].src    = First%@.src;\n"
1853    @"  document.images[\"previousPageImg%@\"].src = Previous%@.src;\n"
1854    @"  document.images[\"nextPageImg%@\"].src     = Next%@.src;\n"
1855    @"  document.images[\"lastPageImg%@\"].src     = Last%@.src;\n"
1856    @"  if (actualPage%@ == 1) {\n"
1857    @"    document.images[\"firstPageImg%@\"].src    = First2%@.src;\n"
1858    @"    document.images[\"previousPageImg%@\"].src = Previous2%@.src;\n"
1859    @"  }\n"
1860
1861#if 0
1862        @"  if (actualPage%@ == 2) {\n"
1863        @"    document.images[\"firstPageImg%@\"].src = First2%@.src;\n"
1864        @"  }\n"
1865        @"  if (actualPage%@ == page%@.length -2) {\n"
1866        @"    document.images[\"lastPageImg%@\"].src = Last2%@.src;\n"
1867        @"  }\n"
1868#endif
1869
1870    @"  if (actualPage%@ == page%@.length - 1) {\n"
1871    @"    document.images[\"nextPageImg%@\"].src = Next2%@.src;\n"
1872    @"    document.images[\"lastPageImg%@\"].src = Last2%@.src;\n"
1873    @"  }\n"
1874    @"}\n",
1875    self->scriptID, // flushImages
1876    self->scriptID, // firstPageImg
1877    self->scriptID, // First
1878    self->scriptID, // previousPageImg
1879    self->scriptID, // Previous
1880    self->scriptID, // nextPageImg
1881    self->scriptID, // Next
1882    self->scriptID, // lastPageImg
1883    self->scriptID, // Last
1884    self->scriptID, // actualPage
1885    self->scriptID, // firstPageImg
1886    self->scriptID, // First2
1887    self->scriptID, // previousPageImg
1888    self->scriptID, // Previous2
1889
1890#if 0
1891    self->scriptID, // actualPage
1892    self->scriptID, // firstPageImg
1893    self->scriptID, // First2
1894
1895    self->scriptID, // actualPage
1896    self->scriptID, // page
1897    self->scriptID, // lastPageImg
1898    self->scriptID, // Last2
1899#endif
1900
1901    self->scriptID, // actualPage
1902    self->scriptID, // page
1903    self->scriptID, // nextPageImg,
1904    self->scriptID, // Next2
1905    self->scriptID, // lastPageImg
1906    self->scriptID  // Last2
1907  ];
1908  [_resp appendContentString:n];
1909  [n release];
1910
1911  n = [[StrClass alloc] initWithFormat:
1912    @"var page%@   = new Array();\n"
1913    @"var footer%@ = new Array();\n"
1914    @"var actualPage%@ = %d;",
1915    self->scriptID, // page
1916    self->scriptID, // footer
1917    self->scriptID, //actualPage
1918    self->state->currentBatch
1919  ];
1920  [_resp appendContentString:n];
1921  [n release];
1922
1923  {
1924    unsigned i;
1925
1926    for (i = 1; i <= self->state->batchCount; i++) {
1927      n = [[StrClass alloc] initWithFormat:
1928        @"page%@[%d] = new Array();\n"
1929        @"page%@[%d][\"Div\"] = page%dDiv%@;\n\n"
1930        @"footer%@[%d] = new Array();\n"
1931        @"footer%@[%d][\"Div\"] = footer%dDiv%@;\n\n",
1932        self->scriptID, // page
1933        i,              // page[i]
1934        self->scriptID, // page
1935        i,              // page[i]
1936        i,              // pageiDiv
1937        self->scriptID, // pageDiv
1938        self->scriptID, // footer
1939        i,              // footer[i]
1940        self->scriptID, // footer
1941        i,              // footer[i]
1942        i,              // footeriDiv
1943        self->scriptID  // footerDiv
1944      ];
1945      [_resp appendContentString:n];
1946      [n release];
1947    }
1948  }
1949  n = [[StrClass alloc] initWithFormat:@"showPage%@();", self->scriptID];
1950  [_resp appendContentString:n];
1951  [n release];
1952
1953  [_resp appendContentString:@"//-->\n</script>\n"];
1954}
1955
1956- (void)_appendTableContentAsScript:(WOResponse *)_resp
1957  inContext:(WOContext *)_ctx
1958{
1959  WOComponent *cmp;
1960  unsigned i, savedBatchIndex;
1961
1962  cmp = [_ctx component];
1963
1964  savedBatchIndex = self->state->currentBatch;
1965  /* open header + data area */
1966  [_resp appendContentString:@"<tr><td>"];
1967
1968  for (i = 1; i <= self->state->batchCount; i++) {
1969    NSString *s;
1970
1971    self->state->currentBatch = i;
1972    _applyState_(self, cmp);
1973    [self updateStateInContext:_ctx];
1974
1975    s = [[StrClass alloc] initWithFormat:         // <DIV...>
1976      @"<div id=\"page%dDiv%@\" style=\"display: ; \">", i, self->scriptID];
1977    [_resp appendContentString:s];
1978    [s release];
1979
1980    [_resp appendContentString:
1981           @"<table border='0' width='100%' cellpadding='1' cellspacing='0'>"];
1982
1983    [self _appendHeader:_resp inContext:_ctx];
1984    [self _appendData:_resp inContext:_ctx];
1985
1986    [_resp appendContentString:@"</table>"];
1987    [_resp appendContentString:@"</div>"];                        // </DIV>
1988
1989
1990    /* append footer */
1991    s = [[StrClass alloc] initWithFormat:        // <DIV...>
1992      @"<div id=\"footer%dDiv%@\" style=\"display: ; \">", i, self->scriptID];
1993    [_resp appendContentString:s];
1994    [s release];
1995
1996    [self _appendFooter:_resp inContext:_ctx];
1997
1998    [_resp appendContentString:@"</div>"];                        // </DIV>
1999  }
2000
2001  /* close header + data area */
2002  [_resp appendContentString:@"</td></tr>"];
2003  if (self->state->currentBatch != savedBatchIndex) {
2004    self->state->currentBatch = savedBatchIndex;
2005    _applyState_(self, cmp);
2006  }
2007  [self updateStateInContext:_ctx];
2008}
2009
2010- (void)_appendScriptLink:(WOResponse *)_response name:(NSString *)_name {
2011  NSString *s;
2012
2013  [_response appendContentString:@"JavaScript:"];
2014  [_response appendContentString:_name];
2015  s = [[StrClass alloc] initWithFormat:@"Page%@();", self->scriptID];
2016  [_response appendContentString:s];
2017  [s release];
2018}
2019
2020- (void)_appendScriptImgName:(WOResponse *)_response name:(NSString *)_name {
2021  [_response appendContentString:_name];
2022  [_response appendContentString:@"PageImg"];
2023  [_response appendContentString:self->scriptID];
2024}
2025
2026@end /* WETableView(JavaScriptAdditions) */
2027