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