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