2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
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"
30 #include <NGObjWeb/NGObjWeb.h>
31 #include <EOControl/EOSortOrdering.h>
32 #include "WEContextConditional.h"
34 @interface WETableView(JavaScriptAdditions)
36 - (void)_appendGroupCollapseScript:(WOResponse *)_resp
37 inContext:(WOContext *)_ctx;
38 - (void)jsButton:(WOResponse *)_resp ctx:(WOContext *)_ctx
39 name:(NSString *)_name button:(NSString *)_button;
41 - (void)appendJavaScript:(WOResponse *)_resp inContext:(WOContext *)_ctx;
42 - (void)_appendTableContentAsScript:(WOResponse *)_resp
43 inContext:(WOContext *)_ctx;
45 - (void)_appendScriptLink:(WOResponse *)_response name:(NSString *)_name;
46 - (void)_appendScriptImgName:(WOResponse *)_response name:(NSString *)_name;
50 #include <NGObjWeb/WEClientCapabilities.h>
52 @implementation WETableView
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;
61 return [super version] + 1 /* v3 */;
64 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
65 NSAssert2([super version] == 2,
66 @"invalid superclass (%@) version %i !",
67 NSStringFromClass([self superclass]), [super version]);
69 StrClass = [NSString class];
70 if (YesNumber == nil) YesNumber = [[NSNumber numberWithBool:YES] retain];
71 if (NoNumber == nil) NoNumber = [[NSNumber numberWithBool:NO] retain];
73 ShowNavigationAlways = [ud boolForKey:@"WETableView_showBlindNavigation"];
76 static NSString *retStrForInt(int i) {
88 case 10: return @"10";
89 // TODO: find useful count!
91 return [[StrClass alloc] initWithFormat:@"%i", i];
95 - (id)initWithName:(NSString *)_name
96 associations:(NSDictionary *)_config
97 template:(WOElement *)_c
99 if ((self = [super initWithName:_name associations:_config template:_c])) {
100 self->state = [[WETableViewState alloc] init];
102 self->list = WOExtGetProperty(_config, @"list");
103 self->currentBatch = WOExtGetProperty(_config, @"currentBatch");
104 self->batchSize = WOExtGetProperty(_config, @"batchSize");
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");
119 WOExtGetProperty(_config, @"indexOfFirstDisplayedObject");
121 WOExtGetProperty(_config, @"indexOfLastDisplayedObject");
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");
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");
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");
145 self->icons = [[WETableViewIconConfig alloc] initWithAssociations:_config];
146 self->groupOpenedIcon = WOExtGetProperty(_config, @"groupOpenedIcon");
147 self->groupClosedIcon = WOExtGetProperty(_config, @"groupClosedIcon");
151 [[WETableViewLabelConfig alloc] initWithAssociations:_config];
153 self->cellspacing = WOExtGetProperty(_config, @"cellspacing");
154 self->cellpadding = WOExtGetProperty(_config, @"cellpadding");
155 self->border = WOExtGetProperty(_config, @"border");
157 self->showGroupTitle = WOExtGetProperty(_config, @"showGroupTitle");
159 #define SetAssociationValue(_a_, _value_) \
161 _a_ = [[WOAssociation associationWithValue:_value_] retain];
163 SetAssociationValue(self->cellspacing, @"0");
164 SetAssociationValue(self->cellpadding, @"1");
165 SetAssociationValue(self->border, @"0");
167 #undef SetAssociationValue
169 if (self->list == nil) {
170 [self logWithFormat:@"ERROR: no 'list' binding is set!"];
175 self->state->doScriptScrolling = NO;
176 self->template = [_c retain];
182 [self->state release];
183 [self->colors release];
184 [self->labels release];
185 [self->icons release];
187 [self->list release];
188 [self->currentBatch release];
189 [self->batchSize release];
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];
202 [self->indexOfFirst release];
203 [self->indexOfLast release];
205 [self->collapseOnClient release];
206 [self->scrollOnClient release];
207 [self->autoScroll release];
208 [self->showBatchResizeButtons release];
210 [self->sortAction release];
211 [self->firstAction release];
212 [self->previousAction release];
213 [self->nextAction release];
214 [self->lastAction release];
216 [self->fontColor release];
217 [self->fontFace release];
218 [self->fontSize release];
220 [self->groupColor release];
221 [self->groupOpenedIcon release];
222 [self->groupClosedIcon release];
224 [self->allObjects release];
225 [self->scriptID release];
226 [self->template release];
228 [self->cellpadding release];
229 [self->cellspacing release];
230 [self->border release];
232 [self->showGroupTitle release];
238 _applyIdentifier(WETableView *self, WOComponent *comp, NSString *_idx)
241 count = [self->allObjects count];
246 /* find subelement for unique id */
248 for (i = 0; i < count; i++) {
252 [self->index setUnsignedIntValue:i inComponent:comp];
255 [self->item setValue:[self->allObjects objectAtIndex:i]
258 if ([self->previousItem isValueSettable]) {
259 [self->previousItem setValue:(i > self->state->firstIndex)
260 ? [self->allObjects objectAtIndex:i-1] : nil
263 if ([self->previousIndex isValueSettable])
264 [self->previousIndex setUnsignedIntValue:(i-1) inComponent:comp];
266 ident = [self->identifier stringValueInComponent:comp];
268 if ([ident isEqualToString:_idx]) {
269 /* found subelement with unique id */
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];
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]) {
290 value = (i > (int)self->state->firstIndex)
291 ? [self->allObjects objectAtIndex:(i - 1)]
293 [self->previousItem setValue:value inComponent:cmp];
295 if ([self->previousIndex isValueSettable])
296 [self->previousIndex setUnsignedIntValue:(i-1) inComponent:cmp];
299 static inline void _applyState_(WETableView *self, WOComponent *cmp) {
300 if ([self->currentBatch isValueSettable])
301 [self->currentBatch setUnsignedIntValue:self->state->currentBatch
305 - (void)updateStateInContext:(WOContext *)_ctx {
306 WEClientCapabilities *ccaps;
310 unsigned batchIdx, size, cnt;
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];
322 if ([[self->autoScroll valueInComponent:cmp] intValue] < 10)
324 else if (isAutoScroll)
325 self->state->doScriptScrolling = NO;
327 /* use JavaScript on InternetExplorer only */
328 self->state->doScriptScrolling = (self->state->doScriptScrolling)
329 ? (self->state->batchCount > 1)
332 self->state->doScriptScrolling = (self->state->doScriptScrolling)
333 ? [self->scrollOnClient boolValueInComponent:cmp]
336 array = [self->list valueInComponent:cmp];
337 batchIdx = [self->currentBatch unsignedIntValueInComponent:cmp];
338 size = [self->batchSize unsignedIntValueInComponent:cmp];
340 size = (isAutoScroll) ? cnt : size;
341 batchIdx = (batchIdx) ? batchIdx : 1;
342 batchIdx = ((batchIdx * size) < (cnt+size)) ? batchIdx : 1;
344 ASSIGN(self->allObjects, array);
346 self->state->currentBatch = batchIdx;
347 self->state->batchSize = size;
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))
354 : self->state->firstIndex + size - 1;
356 if ([self->indexOfFirst isValueSettable]) {
357 [self->indexOfFirst setUnsignedIntValue:self->state->firstIndex
361 if ([self->indexOfLast isValueSettable]) {
362 [self->indexOfLast setUnsignedIntValue:self->state->lastIndex
366 self->state->doCheckBoxes =
367 ([_ctx isInForm] && [self->selection isValueSettable]) ? YES : NO;
368 self->state->doOverflow = isAutoScroll;
371 - (void)updateScriptIdInContext:(WOContext *)_ctx {
375 tmp = [[_ctx elementID] componentsSeparatedByString:@"."];
376 str = [tmp componentsJoinedByString:@""];
378 ASSIGN(self->scriptID, str);
381 - (void)updateConfigInContext:(WOContext *)_ctx {
385 cmp = [_ctx component];
387 [self->colors updateConfigInContext:_ctx];
388 [self->icons updateConfigInContext:_ctx];
389 [self->labels updateConfigInContext:_ctx];
391 #define SetConfigInContext(_a_, _key_) \
392 if (_a_ && (tmp = [_a_ valueInComponent:cmp])) \
393 [_ctx setObject:tmp forKey:_key_]; \
395 SetConfigInContext(self->fontColor, WETableView_fontColor);
396 SetConfigInContext(self->fontFace, WETableView_fontFace);
397 SetConfigInContext(self->fontSize, WETableView_fontSize);
398 #undef SetConfigInContext
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];
411 [_ctx removeObjectForKey:WETableView_downwardIcon];
412 [_ctx removeObjectForKey:WETableView_upwardIcon];
413 [_ctx removeObjectForKey:WETableView_nonSortIcon];
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];
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];
436 - (NSArray *)_collectDataInContext:(WOContext *)_ctx
438 NSAutoreleasePool *pool;
439 NSMutableArray *matrix = nil;
440 NSMutableArray *headInfos = nil;
442 WOComponent *cmp = nil;
445 int sortedHeadIndex = -2;
447 pool = [[NSAutoreleasePool alloc] init];
448 cmp = [_ctx component];
449 k = [self->sortedKey stringValueInComponent:cmp];
451 first = self->state->firstIndex;
452 last = self->state->lastIndex;
453 matrix = [NSMutableArray arrayWithCapacity:last-first+1];
455 [_ctx setObject:YesNumber forKey:WETableView_CollectMode];
456 [_ctx setObject:k forKey:WETableView_SORTEDKEY];
458 self->state->groupCount = 0;
460 for (i=first; i<=last; i++) {
461 NSMutableArray *infos = nil;
464 _applyItems_(self, cmp, i);
466 [_ctx removeObjectForKey:WETableView_INFOS];
467 [self->template appendToResponse:nil inContext:_ctx];
468 infos = [_ctx objectForKey:WETableView_INFOS];
471 infos = [NSArray array];
473 NSAssert(infos != nil, @"Infos is nil.");
475 if (headInfos == nil) {
477 headInfos = [[NSMutableArray alloc] initWithArray:infos];
479 for (j=0, cnt=[headInfos count]; j<cnt; j++) {
480 WETableViewInfo *headInfo = [headInfos objectAtIndex:j];
482 headInfo->isEven = (((i-first) % 2) == 0) ? YES : NO;
491 if (sortedHeadIndex == -2) { // first time
492 for (j=0; j < cnt; j++) {
493 WETableViewInfo *info;
495 info = [infos objectAtIndex:j];
496 if (info->isSorted) {
501 sortedHeadIndex = (sortedHeadIndex < 0) ? -1 : sortedHeadIndex;
505 WETableViewInfo *headInfo;
506 WETableViewInfo *info;
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;
515 else { // sortedHeadIndex == -1 --> no column is sorted
516 headInfo = [headInfos lastObject];
517 isEven = !headInfo->isEven;
521 for (j = 0; j < cnt; j++) {
522 WETableViewInfo *info = [infos objectAtIndex:j];
523 WETableViewInfo *headInfo = [headInfos objectAtIndex:j];
525 if (!info->isGroup || ((int)j != sortedHeadIndex)) {
526 info->isEven = isEven;
528 [headInfos replaceObjectAtIndex:j withObject:info];
535 BOOL doGroupTitle = [self->showGroupTitle boolValueInComponent:cmp];
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];
547 [matrix addObject:infos];
549 [_ctx removeObjectForKey:WETableView_INFOS];
550 [_ctx removeObjectForKey:WETableView_CollectMode];
551 [_ctx removeObjectForKey:WETableView_SORTEDKEY];
553 matrix = [matrix retain];
557 return [matrix autorelease];
560 - (void)_appendNav:(NSString *)_nav isBlind:(BOOL)_isBlind
561 toResponse:(WOResponse *)_response inContext:(WOContext *)_ctx
565 BOOL doForm = [_ctx isInForm];
567 imgUri = [WETableView_ stringByAppendingString:_nav];
568 imgUri = [imgUri stringByAppendingString:(_isBlind) ? @"_blind" : @""];
569 imgUri = [_ctx objectForKey:imgUri];
570 imgUri = WEUriOfResource(imgUri,_ctx);
572 label = WETableLabelForKey(_nav, _ctx);
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>"];
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];
600 [_response appendContentString:[_ctx componentActionURL]];
601 [_response appendContentString:@"\">"];
604 [_response appendContentCharacter:'['];
605 [_response appendContentString:label];
606 [_response appendContentCharacter:']'];
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];
615 [_response appendContentString:@"\" alt=\""];
616 [_response appendContentString:label];
617 [_response appendContentString:@"\" title=\""];
618 [_response appendContentString:label];
619 [_response appendContentString:@"\" />"];
622 if (!_isBlind || self->state->doScriptScrolling) {
623 [_response appendContentString:@"</a>"];
624 [_ctx deleteLastElementIDComponent];
626 [_response appendContentString:@"</td>"];
629 - (void)_appendPreviousNav:(WOResponse *)_resp inContext:(WOContext *)_ctx {
630 int batch, batchCount;
631 BOOL isFirstBlind, isPreviousBlind, isNextBlind, isLastBlind;
633 batch = self->state->currentBatch;
634 batchCount = self->state->batchCount;
636 isFirstBlind = (batch < 2);
637 isPreviousBlind = (batch < 2);
638 isNextBlind = ((batchCount-1) < batch);
639 isLastBlind = ((batchCount-1) < batch);
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];
650 - (void)_appendNextNav:(WOResponse *)_resp inContext:(WOContext *)_ctx {
651 int batch, batchCount;
652 BOOL isFirstBlind, isPreviousBlind, isNextBlind, isLastBlind;
654 batch = self->state->currentBatch;
655 batchCount = self->state->batchCount;
657 isFirstBlind = (batch < 2);
658 isPreviousBlind = (batch < 2);
659 isNextBlind = ((batchCount-1) < batch);
660 isLastBlind = ((batchCount-1) < batch);
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];
671 - (void)_appendNavigation:(WOResponse *)_resp inContext:(WOContext *)_ctx {
672 [_resp appendContentString:
673 @"<table border='0' cellspacing='0' cellpadding='0'><tr>"];
675 [self _appendPreviousNav:_resp inContext:_ctx];
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>"];
686 [self _appendNextNav:_resp inContext:_ctx];
688 [_resp appendContentString:@"</tr></table>"];
691 - (void)_appendTitle:(WOResponse *)_response inContext:(WOContext *)_ctx {
694 bg = [_ctx objectForKey:WETableView_titleColor];
697 [_response appendContentString:
698 @"<table border='0' width='100%' cellpadding='0' cellspacing='0'>"
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];
710 /* append navigation + extra buttons */
711 WEAppendTD(_response, @"right", @"middle", bg); // <td..>
712 [self _appendNavigation:_response inContext:_ctx];
713 [_response appendContentString:@"</td>"]; // </td>
716 [_response appendContentString:@"</tr></table>"];
719 - (void)_appendHeader:(WOResponse *)_response inContext:(WOContext *)_ctx {
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>"];
729 [_ctx appendElementIDComponent:@"header"];
732 bn = retStrForInt(self->state->currentBatch);
733 [_ctx appendElementIDComponent:bn]; // append batchNumber
736 if (self->state->doCheckBoxes) {
741 selArray = [self->selection valueInComponent:[_ctx component]];
742 doSelectAll = ([selArray count] < ([self->allObjects count] / 2));
744 img = [_ctx objectForKey:(doSelectAll)
745 ? WETableView_select_all
746 : WETableView_deselect_all];
748 img = WEUriOfResource(img, _ctx);
750 [_response appendContentString:
751 @"<td align=\"center\" bgcolor=\""];
752 [_response appendContentString:
753 [_ctx objectForKey:WETableView_headerColor]];
754 [_response appendContentString:@"\">"];
757 [_ctx appendElementIDComponent:@"_sa"]; // select all
759 [_ctx appendElementIDComponent:@"_dsa"]; // deselect all
761 [_response appendContentString:@"<a href=\""];
762 [_response appendContentString:[_ctx componentActionURL]];
763 [_response appendContentString:@"\">"];
766 [_response appendContentString:@"<img border=\"0\" src=\""];
767 [_response appendContentString:img];
768 [_response appendContentString:@"\" alt=\""];
769 [_response appendContentString:(doSelectAll)
772 [_response appendContentString:@"\" title=\""];
773 [_response appendContentString:(doSelectAll)
776 [_response appendContentString:@"\" />"];
779 [_response appendContentString:(doSelectAll) ? @"[+]" : @"[-]"];
781 [_response appendContentString:@"</a>"];
782 [_ctx deleteLastElementIDComponent]; // (de)select all
783 [_response appendContentString:@"</td>"];
785 [self->template appendToResponse:_response inContext:_ctx];
787 [_ctx deleteLastElementIDComponent]; // delete batchNumber
788 [_ctx deleteLastElementIDComponent]; // delete "header"
790 if (self->state->showBatchResizeButtons) {
793 cnt = (self->state->lastIndex - self->state->firstIndex + 1);
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:@"\""];
803 [_response appendContentString:@"></td>"];
807 [_response appendContentString:@"</tr>"];
808 [_ctx removeObjectForKey:WETableView_HeaderMode];
809 [_ctx removeObjectForKey:WETableView_SORTEDKEY];
810 [_ctx removeObjectForKey:WETableView_ISDESCENDING];
813 - (void)_appendResizeButtons:(WOResponse *)_response
814 actionUrl:(NSString *)_actionUrl
815 inContext:(WOContext *)_ctx
820 // append batchSize--Button
821 img = [self->icons->minusResizeIcon stringValueInComponent:[_ctx component]];
822 img = WEUriOfResource(img, _ctx);
823 uri = [_actionUrl stringByAppendingString:@".mm"];
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\" />"];
835 [_response appendContentString:@"<a href=\""];
836 [_response appendContentString:uri];
837 [_response appendContentString:@"\">"];
840 if (img && ![_ctx isInForm]) {
841 [_response appendContentString:@"<img border=\"0\" src=\""];
842 [_response appendContentString:img];
843 [_response appendContentString:@"\" alt=\"minus\" title=\"minus\" />"];
846 [_response appendContentString:@"-"];
848 if (!(img && [_ctx isInForm]))
849 [_response appendContentString:@"</a>"];
851 // append batchSize--Button
852 img = [self->icons->plusResizeIcon stringValueInComponent:[_ctx component]];
853 img = WEUriOfResource(img, _ctx);
854 uri = [_actionUrl stringByAppendingString:@".pp"] ;
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\" />"];
866 [_response appendContentString:@"<a href=\""];
867 [_response appendContentString:uri];
868 [_response appendContentString:@"\">"];
871 if (img && ![_ctx isInForm]) {
872 [_response appendContentString:@"<img border=\"0\" src=\""];
873 [_response appendContentString:img];
874 [_response appendContentString:@"\" alt=\"plus\" title=\"plus\" />"];
877 [_response appendContentString:@"+"];
879 if (!(img && [_ctx isInForm]))
880 [_response appendContentString:@"</a>"];
884 - (void)_appendBatchResizeButtons:(WOResponse *)_response
885 rowSpan:(unsigned int)_rowSpan
886 actionUrl:(NSString *)_actionUrl
887 inContext:(WOContext *)_ctx
892 s = [[StrClass alloc] initWithFormat:
893 @"<td align='center' valign='bottom' width='5' rowspan='%d'", _rowSpan];
894 [_response appendContentString:s];
897 if ([_ctx objectForKey:WETableView_footerColor]) {
898 [_response appendContentString:@" bgcolor='"];
899 [_response appendContentString:
900 [_ctx objectForKey:WETableView_footerColor]];
901 [_response appendContentCharacter:'\''];
903 [_response appendContentCharacter:'>'];
905 // apppend resize buttons
906 [self _appendResizeButtons:_response
910 [_response appendContentString:@"</td>"];
913 - (void)_appendData:(WOResponse *)_response inContext:(WOContext *)_ctx {
916 NSString *batchSizeUrl = nil;
917 NSArray *selArray = nil;
918 NSString *groupId = nil;
919 BOOL hideObject = NO;
920 unsigned i, cnt, first;
922 comp = [_ctx component];
923 matrix = [self _collectDataInContext:_ctx];
924 first = self->state->firstIndex;
925 cnt = [matrix count];
927 if (matrix == nil || cnt == 0)
930 if (self->state->doCheckBoxes)
931 selArray = [self->selection valueInComponent:comp];
933 if (self->state->doScriptCollapsing)
934 [self _appendGroupCollapseScript:_response inContext:_ctx];
936 [_ctx appendElementIDComponent:@"data"];
939 bn = retStrForInt(self->state->currentBatch);
940 [_ctx appendElementIDComponent:bn]; // append batchNumber
944 batchSizeUrl = [_ctx componentActionURL];
946 if (self->identifier == nil) {
949 s = retStrForInt(first);
950 [_ctx appendElementIDComponent:s];
954 for (i = 0; i < cnt; i++) {
955 NSMutableArray *infos = nil;
957 _applyItems_(self, comp, first+i);
958 if (self->identifier) {
961 ident = [self->identifier stringValueInComponent:comp];
962 [_ctx appendElementIDComponent:ident];
965 infos = [matrix objectAtIndex:i];
967 if ([[infos lastObject] isEqual:WETableView_GroupMode]) {
970 groupId = [StrClass stringWithFormat:@"group%d", i];
972 rowSpan = ((i==0) && self->state->showBatchResizeButtons)
973 ? cnt+self->state->groupCount
975 [self _appendGroupTitle:_response
978 actionUrl:batchSizeUrl
982 if ((self->state->groupCount > 0) && !self->state->doScriptCollapsing &&
983 (self->showGroup) && ![self->showGroup boolValueInComponent:comp])
989 [_ctx setObject:infos forKey:WETableView_INFOS];
992 if (self->identifier == nil)
993 [_ctx incrementLastElementIDComponent];
995 [_ctx deleteLastElementIDComponent]; // delete identifier
999 [_response appendContentString:@"<tr"];
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;\""];
1008 [_response appendContentCharacter:'>'];
1010 [_ctx setObject:YesNumber forKey:WETableView_DataMode];
1012 if (self->state->doCheckBoxes) {
1013 WETableViewInfo *info = nil;
1017 info = ([infos count]) ? [infos objectAtIndex:0] : nil;
1019 bg = (info && info->isEven)
1020 ? [_ctx objectForKey:WETableView_evenColor]
1021 : [_ctx objectForKey:WETableView_oddColor];
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];
1033 [_response appendContentCharacter:'"'];
1035 if ([selArray containsObject:[self->allObjects objectAtIndex:first+i]])
1036 [_response appendContentString:@" checked=\"checked\""];
1038 [_response appendContentString:@" />"];
1039 [_response appendContentString:@"</td>"];
1041 [_ctx deleteLastElementIDComponent]; // delete "cb"
1044 [self->template appendToResponse:_response inContext:_ctx];
1046 if (!i && self->state->showBatchResizeButtons &&!self->state->groupCount) {
1047 [self _appendBatchResizeButtons:_response
1048 rowSpan:cnt+self->state->groupCount
1049 actionUrl:batchSizeUrl
1053 [_response appendContentString:@"</tr>"];
1055 if (self->identifier == nil)
1056 [_ctx incrementLastElementIDComponent];
1058 [_ctx deleteLastElementIDComponent]; // delete identifier
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];
1067 - (void)_appendFooter:(WOResponse *)_response inContext:(WOContext *)_ctx {
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;
1076 first = (count) ? first : 0;
1077 batchCnt = (batchCnt) ? batchCnt : 1;
1078 bg = [_ctx objectForKey:WETableView_footerColor];
1080 [_response appendContentString:
1081 @"<table border='0' width='100%' cellpadding='0' cellspacing='0'>"];
1082 [_response appendContentString:@"<tr>"]; // <TR>
1084 WEAppendTD(_response, @"left", nil, bg); // <TD...>
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];
1092 [_response appendContentString:@"<small>"];
1093 if (!self->state->doOverflow) {
1094 s = [[StrClass alloc] initWithFormat:@"%d ", first];
1095 [_response appendContentString:s];
1097 [_response appendContentString:WETableLabelForKey(@"to", _ctx)];
1098 s = [[StrClass alloc] initWithFormat:@" %d ", last];
1099 [_response appendContentString:s];
1101 [_response appendContentString:WETableLabelForKey(@"of", _ctx)];
1103 s = [[StrClass alloc] initWithFormat:@" %d", count];
1104 [_response appendContentString:s];
1106 [_response appendContentString:@"</small>"];
1108 [_response appendContentString:@"</td>"]; // </td>
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...>
1118 [_response appendContentString:@"<small>"];
1119 [_response appendContentString:@" "];
1120 [_response appendContentString:WETableLabelForKey(@"page", _ctx)];
1121 s = [[StrClass alloc] initWithFormat:@": %d ", batch];
1122 [_response appendContentString:s];
1124 [_response appendContentString:WETableLabelForKey(@"of", _ctx)];
1125 s = [[StrClass alloc] initWithFormat:@" %d", batchCnt];
1126 [_response appendContentString:s];
1128 [_response appendContentString:@" "];
1129 [_response appendContentString:@"</small>"];
1130 if (ShowNavigationInFooter) {
1131 [_response appendContentString:@"</td>"];
1132 [self _appendNextNav:_response inContext:_ctx];
1133 [_response appendContentString:@"</tr></table>"];
1137 [self _appendResizeButtons:_response
1138 actionUrl:[_ctx componentActionURL]
1142 [_response appendContentString:@"</td></tr>"]; // </td></tr>
1143 [_response appendContentString:@"</table>"]; // </table>
1146 // --- action handler -----------------------------------------------------
1148 - (void)_handleSortActionInContext:(WOContext *)_ctx {
1155 if (self->sortedKey == nil || self->isDescending == nil)
1156 return; // nothing to do
1158 if ([_ctx objectForKey:WETableView_SORTEDKEY] == nil ||
1159 [_ctx objectForKey:WETableView_ISDESCENDING] == nil)
1160 return; // nothing to do
1162 cmp = [_ctx component];
1163 key = [_ctx objectForKey:WETableView_SORTEDKEY];
1164 isDesc = [[_ctx objectForKey:WETableView_ISDESCENDING] boolValue];
1166 oldIsDesc = [self->isDescending boolValueInComponent:cmp];
1167 oldKey = [self->sortedKey stringValueInComponent:cmp];
1169 if ([oldKey isEqual:key] && oldIsDesc == isDesc)
1170 return; // nothing to do
1172 if ([self->isDescending isValueSettable])
1173 [self->isDescending setBoolValue:isDesc inComponent:cmp];
1174 if ([self->sortedKey isValueSettable])
1175 [self->sortedKey setStringValue:key inComponent:cmp];
1177 if (self->sortAction == nil && key != nil) {
1183 sel = (isDesc) ? EOCompareDescending : EOCompareAscending;
1184 so = [EOSortOrdering sortOrderingWithKey:key selector:sel];
1186 soArray = [[NSArray alloc] initWithObjects:&so count:1];
1187 tmp = [self->allObjects sortedArrayUsingKeyOrderArray:soArray];
1190 if ([self->list isValueSettable])
1191 [self->list setValue:tmp inComponent:[_ctx component]];
1193 [[_ctx component] debugWithFormat:
1194 @"couldn't set sorted list on 'list' binding"];
1197 else if (self->sortAction)
1198 [self->sortAction valueInComponent:cmp];
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]);
1210 - (void)_handlePreviousButtonInContext:(WOContext *)_ctx {
1211 if (self->previousAction)
1212 [self->previousAction valueInComponent:[_ctx component]];
1214 unsigned batch = self->state->currentBatch;
1216 self->state->currentBatch = ((batch -1) > 0) ? batch - 1 : 1;
1217 _applyState_(self, [_ctx component]);
1221 - (void)_handleNextButtonInContext:(WOContext *)_ctx {
1222 if (self->nextAction)
1223 [self->nextAction valueInComponent:[_ctx component]];
1225 unsigned batch = self->state->currentBatch;
1226 unsigned cnt = self->state->batchCount;
1228 self->state->currentBatch = ((batch +1) < cnt) ? batch + 1 : cnt;
1229 _applyState_(self, [_ctx component]);
1233 - (void)_handleLastButtonInContext:(WOContext *)_ctx {
1234 if (self->lastAction)
1235 [self->lastAction valueInComponent:[_ctx component]];
1237 self->state->currentBatch = self->state->batchCount;
1238 _applyState_(self, [_ctx component]);
1242 // --- main methodes -------------------------------------------------------
1244 - (void)takeValuesFromRequest:(WORequest *)_request
1245 forBatch:(int)_batch
1246 selections:(NSMutableArray *)_selArray
1247 inContext:(WOContext *)_ctx
1252 first = self->state->firstIndex;
1253 last = self->state->lastIndex;
1257 bn = retStrForInt(self->state->currentBatch);
1258 [_ctx appendElementIDComponent:bn]; // append batchNumber
1262 eid = [_ctx elementID];
1264 if ([_request formValueForKey:[eid stringByAppendingString:@".pp.x"]]) {
1265 [_ctx addActiveFormElement:self];
1266 [_ctx setRequestSenderID:
1267 [[_ctx senderID] stringByAppendingString:@".pp"]];
1269 else if ([_request formValueForKey:[eid stringByAppendingString:@".mm.x"]]) {
1270 [_ctx addActiveFormElement:self];
1271 [_ctx setRequestSenderID:
1272 [[_ctx senderID] stringByAppendingString:@".mm"]];
1275 if (self->identifier == nil) { // append index
1276 s = retStrForInt(first);
1277 [_ctx appendElementIDComponent:s];
1281 for (i = first; i <= last; i++) {
1282 _applyItems_(self, [_ctx component], i);
1283 if (self->identifier) {
1286 s = [self->identifier stringValueInComponent:[_ctx component]];
1287 [_ctx appendElementIDComponent:s];
1291 NSString *cbID; // checkBoxID
1295 cbID = [[_ctx elementID] stringByAppendingString:@".cb"];
1296 obj = [self->item valueInComponent:[_ctx component]];
1299 if ((formValue = [_request formValueForKey:cbID])) {
1300 if (![_selArray containsObject:obj])
1301 [_selArray addObject:obj];
1303 else if ([_selArray containsObject:obj])
1304 [_selArray removeObject:obj];
1307 [self->template takeValuesFromRequest:_request inContext:_ctx];
1309 if (self->identifier == nil)
1310 [_ctx incrementLastElementIDComponent];
1312 [_ctx deleteLastElementIDComponent]; // delete identifier
1314 if (self->identifier == nil)
1315 [_ctx deleteLastElementIDComponent]; // delete index
1317 [_ctx deleteLastElementIDComponent]; // delete batchNumber
1320 - (void)takeValuesFromRequest:(WORequest *)_request inContext:(WOContext *)_ctx {
1321 int i, firstBatch, lastBatch, savedCurrentBatch;
1323 NSMutableArray *selArray = nil;
1325 [self updateStateInContext:_ctx];
1327 eid = [_ctx elementID];
1329 /* handle "data" section */
1331 if (self->state->doCheckBoxes) {
1332 selArray = [self->selection valueInComponent:[_ctx component]];
1333 selArray = [selArray mutableCopyWithZone:[self zone]];
1336 firstBatch = (self->state->doScriptScrolling) ? 1 : self->state->currentBatch;
1338 lastBatch = (self->state->doScriptScrolling)
1339 ? self->state->batchCount
1340 : self->state->currentBatch;
1343 [_ctx appendElementIDComponent:@"data"];
1344 [_ctx setObject:YesNumber forKey:WETableView_DataMode];
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];
1352 [self takeValuesFromRequest:_request
1357 [_ctx removeObjectForKey:WETableView_DataMode];
1359 if (self->state->currentBatch != (unsigned)savedCurrentBatch) {
1360 self->state->currentBatch = savedCurrentBatch;
1361 _applyState_(self, [_ctx component]);
1364 [_ctx deleteLastElementIDComponent]; // delete "data"
1366 if (self->state->doCheckBoxes) {
1367 [self->selection setValue:selArray inComponent:[_ctx component]];
1371 // handle header (sort buttons, ...)
1372 [_ctx setObject:YesNumber forKey:WETableView_HeaderMode];
1373 [_ctx appendElementIDComponent:@"header"];
1375 for (i = 1; i <= (int)self->state->batchCount; i++) {
1379 s = retStrForInt(i);
1380 [_ctx appendElementIDComponent:s];
1383 [self->template takeValuesFromRequest:_request inContext:_ctx];
1384 [_ctx deleteLastElementIDComponent]; // delete batchNumber
1387 [_ctx deleteLastElementIDComponent]; // delete "header"
1388 [_ctx removeObjectForKey:WETableView_HeaderMode];
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];
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];
1405 [_ctx setObject:YesNumber forKey:WETableView_FooterMode];
1406 [_ctx appendElementIDComponent:@"footer"];
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"]];
1415 else if ([_request formValueForKey:
1416 [eid stringByAppendingString:@".footer.mm.x"]]) {
1417 [_ctx addActiveFormElement:self];
1418 [_ctx setRequestSenderID:
1419 [[_ctx senderID] stringByAppendingString:@".mm"]];
1422 [self->template takeValuesFromRequest:_request inContext:_ctx];
1423 [_ctx deleteLastElementIDComponent]; // delete "footer"
1424 [_ctx removeObjectForKey:WETableView_FooterMode];
1426 if ([_request formValueForKey:[eid stringByAppendingString:@".first.x"]]) {
1427 [_ctx addActiveFormElement:self];
1428 [_ctx setRequestSenderID:
1429 [[_ctx senderID] stringByAppendingString:@".first"]];
1431 if ([_request formValueForKey:[eid stringByAppendingString:@".next.x"]]) {
1432 [_ctx addActiveFormElement:self];
1433 [_ctx setRequestSenderID:
1434 [[_ctx senderID] stringByAppendingString:@".next"]];
1436 if ([_request formValueForKey:[eid stringByAppendingString:@".last.x"]]) {
1437 [_ctx addActiveFormElement:self];
1438 [_ctx setRequestSenderID:
1439 [[_ctx senderID] stringByAppendingString:@".last"]];
1441 if ([_request formValueForKey:[eid stringByAppendingString:@".previous.x"]]) {
1442 [_ctx addActiveFormElement:self];
1443 [_ctx setRequestSenderID:
1444 [[_ctx senderID] stringByAppendingString:@".previous"]];
1448 - (id)increaseAutoScrollHeightInContext:(WOContext *)_ctx {
1449 if ([self->autoScroll isValueSettable]) {
1450 int sh; // scrollHeight
1452 sh = [self->autoScroll intValueInComponent:[_ctx component]] + 20;
1453 [self->autoScroll setIntValue:sh inComponent:[_ctx component]];
1458 - (id)decreaseAutoScrollHeightInContext:(WOContext *)_ctx {
1459 if ([self->autoScroll isValueSettable]) {
1460 int sh; // scrollHeight
1462 sh = [self->autoScroll intValueInComponent:[_ctx component]] - 20;
1464 [self->autoScroll setIntValue:sh inComponent:[_ctx component]];
1470 - (id)increaseBatchSizeInContext:(WOContext *)_ctx {
1471 if ([self->batchSize isValueSettable]) {
1474 bs = [self->batchSize intValueInComponent:[_ctx component]] + 1;
1475 [self->batchSize setIntValue:bs inComponent:[_ctx component]];
1480 - (id)decreaseBatchSizeInContext:(WOContext *)_ctx {
1481 if ([self->batchSize isValueSettable]) {
1484 bs = [self->batchSize intValueInComponent:[_ctx component]] - 1;
1486 [self->batchSize setIntValue:bs inComponent:[_ctx component]];
1491 - (id)invokeActionForRequest:(WORequest *)_request inContext:(WOContext *)_ctx {
1496 [self updateStateInContext:_ctx];
1498 eid = [_ctx currentElementID];
1499 cmp = [_ctx component];
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"]) {
1512 [_ctx consumeElementID]; // consume "data"
1513 [_ctx appendElementIDComponent:eid]; // append "data"
1518 bn = [_ctx currentElementID];
1519 if ([self->currentBatch isValueSettable])
1520 [self->currentBatch setIntValue:[bn intValue] inComponent:cmp];
1522 [_ctx consumeElementID]; // consume batchNumber
1523 [_ctx appendElementIDComponent:bn]; // append batch
1526 if ((idxId = [_ctx currentElementID])) {
1527 [_ctx consumeElementID]; // consume index-id
1528 [_ctx appendElementIDComponent:idxId]; // append index-id
1531 if ([idxId isEqualToString:@"pp"])
1532 result = [self increaseBatchSizeInContext:_ctx];
1533 else if ([idxId isEqualToString:@"mm"])
1534 result = [self decreaseBatchSizeInContext:_ctx];
1536 if (self->identifier == nil) {
1539 idx = [idxId unsignedIntValue];
1540 if (idx < [self->allObjects count] && idx >= 0) {
1541 _applyItems_(self, cmp, idx);
1544 NSLog(@"WETableView: index is out of range!");
1547 _applyIdentifier(self, cmp, idxId);
1549 result = [self invokeGrouping:_request inContext:_ctx];
1551 [_ctx deleteLastElementIDComponent]; // delete index-id
1553 [_ctx deleteLastElementIDComponent]; // delete batchNumber
1554 [_ctx deleteLastElementIDComponent]; // delete "data"
1556 else if ([eid isEqual:@"header"]) {
1557 [_ctx consumeElementID]; // consume "header"
1558 [_ctx appendElementIDComponent:eid]; // append "header"
1560 if ([self->currentBatch isValueSettable]) {
1561 int bn = [[_ctx currentElementID] intValue];
1562 [self->currentBatch setIntValue:bn inComponent:cmp];
1564 [_ctx appendElementIDComponent:[_ctx currentElementID]]; // batchNumber
1565 [_ctx consumeElementID]; // consume batchNumber
1567 // handle selectAllCheckBoxes:
1568 if ([[_ctx currentElementID] isEqualToString:@"_sa"]) {
1569 NSMutableArray *selArray;
1571 selArray = [self->allObjects mutableCopyWithZone:[self zone]];
1572 [self->selection setValue:selArray inComponent:cmp];
1575 // handle deselectAllCheckBoxes:
1576 else if ([[_ctx currentElementID] isEqualToString:@"_dsa"]) {
1577 [self->selection setValue:[NSMutableArray array] inComponent:cmp];
1580 result = [self->template invokeActionForRequest:_request inContext:_ctx];
1582 [_ctx deleteLastElementIDComponent]; // delete batchNumber
1583 [_ctx deleteLastElementIDComponent]; // delete "header"
1585 [self _handleSortActionInContext:_ctx];
1587 [_ctx removeObjectForKey:WETableView_SORTEDKEY];
1588 [_ctx removeObjectForKey:WETableView_ISDESCENDING];
1590 else if ([eid isEqual:@"title"] || [eid isEqual:@"button"] ||
1591 [eid isEqual:@"footer"]) {
1592 [_ctx consumeElementID];
1593 [_ctx appendElementIDComponent:eid];
1595 eid = [_ctx currentElementID];
1597 // reset autoScrollHeight
1598 if ([eid isEqualToString:@"pp"])
1599 result = [self increaseAutoScrollHeightInContext:_ctx];
1600 else if ([eid isEqualToString:@"mm"])
1601 result = [self decreaseAutoScrollHeightInContext:_ctx];
1603 result = [self->template invokeActionForRequest:_request inContext:_ctx];
1605 [_ctx deleteLastElementIDComponent];
1608 result = [self->template invokeActionForRequest:_request inContext:_ctx];
1613 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
1616 [self updateStateInContext:_ctx];
1617 [self updateScriptIdInContext:_ctx];
1618 [self updateConfigInContext:_ctx];
1620 cmp = [_ctx component];
1622 /* open tableView */
1623 [_response appendContentString:
1624 @"<table border='0' width='100%' cellpadding='0' cellspacing='0'>"];
1626 /* append tableTitle + navigation */
1627 [_response appendContentString:@"<tr><td>"];
1628 [self _appendTitle:_response inContext:_ctx];
1629 [_response appendContentString:@"</td></tr>"];
1631 [_response appendContentString:@"<tr><td></td></tr>"];
1633 if (self->state->doScriptScrolling) {
1634 [self _appendTableContentAsScript:_response inContext:_ctx]; //close tables
1637 /* open header + data area */
1638 [_response appendContentString:@"<tr><td>"];
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\">"];
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:@"'>"];
1659 self->state->showBatchResizeButtons =
1660 ([self->showBatchResizeButtons boolValueInComponent:cmp] &&
1661 (self->state->currentBatch < self->state->batchCount) &&
1662 !self->state->doOverflow);
1664 [self _appendHeader:_response inContext:_ctx];
1665 [self _appendData:_response inContext:_ctx];
1667 [_response appendContentString:@"</table>"];
1668 if (self->state->doOverflow)
1669 [_response appendContentString:@"</p>"];
1671 /* close header + data area */
1672 [_response appendContentString:@"</td></tr>"];
1674 [_response appendContentString:@"</table>"]; // </TABLE>
1677 [self _appendFooter:_response inContext:_ctx];
1683 if (self->state->doScriptScrolling)
1684 [self appendJavaScript:_response inContext:_ctx];
1686 [self removeConfigInContext:_ctx];
1689 @end /* WETableView */
1691 @implementation WETableViewInfo
1694 // --- JavaScript additions -------------------------------------------------
1696 @implementation WETableView(JavaScriptAdditions)
1698 - (void)_appendGroupCollapseScript:(WOResponse *)_resp
1699 inContext:(WOContext *)_ctx
1701 if ([_ctx objectForKey:WETableView_HasCollapseScript])
1704 [_resp appendContentString:
1705 @"\n<script language=\"JavaScript\">\n"
1707 @"function toggleTableGroup()\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"
1726 [_ctx setObject:YesNumber forKey:WETableView_HasCollapseScript];
1729 - (void)jsButton:(WOResponse *)_resp ctx:(WOContext *)_ctx
1730 name:(NSString *)_name button:(NSString *)_button
1735 _button = [_button stringByAppendingString:@".gif"];
1736 imgUri = WEUriOfResource(_button, _ctx);
1737 n = [_name stringByAppendingString:self->scriptID];
1739 n = [[StrClass alloc] initWithFormat:
1740 @"var %@ = new Image(); %@.src = \"%@\";\n", n, n, imgUri];
1741 [_resp appendContentString:n];
1745 - (void)appendJavaScript:(WOResponse *)_resp inContext:(WOContext *)_ctx {
1748 [_resp appendContentString:@"<script language=\"JavaScript\">\n<!--\n"];
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"];
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"
1767 @" page%@[i][\"Div\"].style.display = \"none\";\n"
1768 @" footer%@[i][\"Div\"].style.display = \"none\";\n"
1771 @" flushImages%@();\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
1782 [_resp appendContentString:n];
1785 n = [[StrClass alloc] initWithFormat:
1786 @"function firstPage%@() {\n"
1787 @" actualPage%@ = 1;\n"
1790 self->scriptID, // firstPage
1791 self->scriptID, // actualPage
1792 self->scriptID // showPage
1794 [_resp appendContentString:n];
1797 n = [[StrClass alloc] initWithFormat:
1798 @"function previousPage%@() {\n"
1799 @" if (actualPage%@ > 1) {\n"
1800 @" actualPage%@--;\n"
1804 self->scriptID, // previousPage
1805 self->scriptID, // actualPage
1806 self->scriptID, // actualPage
1807 self->scriptID // showPage
1809 [_resp appendContentString:n];
1812 n = [[StrClass alloc] initWithFormat:
1813 @"function nextPage%@() {\n"
1814 @" if (actualPage%@ < page%@.length - 1) {\n"
1815 @" actualPage%@++;\n"
1819 self->scriptID, // nextPage
1820 self->scriptID, // actualPage
1821 self->scriptID, // page
1822 self->scriptID, // actualPage
1823 self->scriptID // showPage
1825 [_resp appendContentString:n];
1828 n = [[StrClass alloc] initWithFormat:
1829 @"function lastPage%@() {\n"
1830 @" actualPage%@ = page%@.length - 1;\n"
1833 self->scriptID, // lastPage
1834 self->scriptID, // actualPage
1835 self->scriptID, // page
1836 self->scriptID // showPage
1838 [_resp appendContentString:n];
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"
1853 @" if (actualPage%@ == 2) {\n"
1854 @" document.images[\"firstPageImg%@\"].src = First2%@.src;\n"
1856 @" if (actualPage%@ == page%@.length -2) {\n"
1857 @" document.images[\"lastPageImg%@\"].src = Last2%@.src;\n"
1861 @" if (actualPage%@ == page%@.length - 1) {\n"
1862 @" document.images[\"nextPageImg%@\"].src = Next2%@.src;\n"
1863 @" document.images[\"lastPageImg%@\"].src = Last2%@.src;\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
1882 self->scriptID, // actualPage
1883 self->scriptID, // firstPageImg
1884 self->scriptID, // First2
1886 self->scriptID, // actualPage
1887 self->scriptID, // page
1888 self->scriptID, // lastPageImg
1889 self->scriptID, // Last2
1892 self->scriptID, // actualPage
1893 self->scriptID, // page
1894 self->scriptID, // nextPageImg,
1895 self->scriptID, // Next2
1896 self->scriptID, // lastPageImg
1897 self->scriptID // Last2
1899 [_resp appendContentString:n];
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
1911 [_resp appendContentString:n];
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
1925 self->scriptID, // page
1928 self->scriptID, // pageDiv
1929 self->scriptID, // footer
1931 self->scriptID, // footer
1934 self->scriptID // footerDiv
1936 [_resp appendContentString:n];
1940 n = [[StrClass alloc] initWithFormat:@"showPage%@();", self->scriptID];
1941 [_resp appendContentString:n];
1944 [_resp appendContentString:@"//-->\n</script>\n"];
1947 - (void)_appendTableContentAsScript:(WOResponse *)_resp
1948 inContext:(WOContext *)_ctx
1951 unsigned i, savedBatchIndex;
1953 cmp = [_ctx component];
1955 savedBatchIndex = self->state->currentBatch;
1956 /* open header + data area */
1957 [_resp appendContentString:@"<tr><td>"];
1959 for (i = 1; i <= self->state->batchCount; i++) {
1962 self->state->currentBatch = i;
1963 _applyState_(self, cmp);
1964 [self updateStateInContext:_ctx];
1966 s = [[StrClass alloc] initWithFormat: // <DIV...>
1967 @"<div id=\"page%dDiv%@\" style=\"display: ; \">", i, self->scriptID];
1968 [_resp appendContentString:s];
1971 [_resp appendContentString:
1972 @"<table border='0' width='100%' cellpadding='1' cellspacing='0'>"];
1974 [self _appendHeader:_resp inContext:_ctx];
1975 [self _appendData:_resp inContext:_ctx];
1977 [_resp appendContentString:@"</table>"];
1978 [_resp appendContentString:@"</div>"]; // </DIV>
1982 s = [[StrClass alloc] initWithFormat: // <DIV...>
1983 @"<div id=\"footer%dDiv%@\" style=\"display: ; \">", i, self->scriptID];
1984 [_resp appendContentString:s];
1987 [self _appendFooter:_resp inContext:_ctx];
1989 [_resp appendContentString:@"</div>"]; // </DIV>
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);
1998 [self updateStateInContext:_ctx];
2001 - (void)_appendScriptLink:(WOResponse *)_response name:(NSString *)_name {
2004 [_response appendContentString:@"JavaScript:"];
2005 [_response appendContentString:_name];
2006 s = [[StrClass alloc] initWithFormat:@"Page%@();", self->scriptID];
2007 [_response appendContentString:s];
2011 - (void)_appendScriptImgName:(WOResponse *)_response name:(NSString *)_name {
2012 [_response appendContentString:_name];
2013 [_response appendContentString:@"PageImg"];
2014 [_response appendContentString:self->scriptID];
2017 @end /* WETableView(JavaScriptAdditions) */