2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
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"
31 #include <NGObjWeb/NGObjWeb.h>
32 #include <EOControl/EOSortOrdering.h>
33 #include "WEContextConditional.h"
35 @interface WETableView(JavaScriptAdditions)
37 - (void)_appendGroupCollapseScript:(WOResponse *)_resp
38 inContext:(WOContext *)_ctx;
39 - (void)jsButton:(WOResponse *)_resp ctx:(WOContext *)_ctx
40 name:(NSString *)_name button:(NSString *)_button;
42 - (void)appendJavaScript:(WOResponse *)_resp inContext:(WOContext *)_ctx;
43 - (void)_appendTableContentAsScript:(WOResponse *)_resp
44 inContext:(WOContext *)_ctx;
46 - (void)_appendScriptLink:(WOResponse *)_response name:(NSString *)_name;
47 - (void)_appendScriptImgName:(WOResponse *)_response name:(NSString *)_name;
51 #include "WEClientCapabilities.h"
53 @implementation WETableView
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;
62 return [super version] + 1 /* v3 */;
65 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
66 NSAssert2([super version] == 2,
67 @"invalid superclass (%@) version %i !",
68 NSStringFromClass([self superclass]), [super version]);
70 StrClass = [NSString class];
71 if (YesNumber == nil) YesNumber = [[NSNumber numberWithBool:YES] retain];
72 if (NoNumber == nil) NoNumber = [[NSNumber numberWithBool:NO] retain];
74 ShowNavigationAlways = [ud boolForKey:@"WETableView_showBlindNavigation"];
77 static NSString *retStrForInt(int i) {
89 case 10: return @"10";
90 // TODO: find useful count!
92 return [[StrClass alloc] initWithFormat:@"%i", i];
96 - (id)initWithName:(NSString *)_name
97 associations:(NSDictionary *)_config
98 template:(WOElement *)_c
100 if ((self = [super initWithName:_name associations:_config template:_c])) {
101 self->state = [[WETableViewState alloc] init];
103 self->list = WOExtGetProperty(_config, @"list");
104 self->currentBatch = WOExtGetProperty(_config, @"currentBatch");
105 self->batchSize = WOExtGetProperty(_config, @"batchSize");
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");
120 WOExtGetProperty(_config, @"indexOfFirstDisplayedObject");
122 WOExtGetProperty(_config, @"indexOfLastDisplayedObject");
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");
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");
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");
146 self->icons = [[WETableViewIconConfig alloc] initWithAssociations:_config];
147 self->groupOpenedIcon = WOExtGetProperty(_config, @"groupOpenedIcon");
148 self->groupClosedIcon = WOExtGetProperty(_config, @"groupClosedIcon");
152 [[WETableViewLabelConfig alloc] initWithAssociations:_config];
154 self->cellspacing = WOExtGetProperty(_config, @"cellspacing");
155 self->cellpadding = WOExtGetProperty(_config, @"cellpadding");
156 self->border = WOExtGetProperty(_config, @"border");
158 self->showGroupTitle = WOExtGetProperty(_config, @"showGroupTitle");
160 #define SetAssociationValue(_a_, _value_) \
162 _a_ = [[WOAssociation associationWithValue:_value_] retain];
164 SetAssociationValue(self->cellspacing, @"0");
165 SetAssociationValue(self->cellpadding, @"1");
166 SetAssociationValue(self->border, @"0");
168 #undef SetAssociationValue
170 if (self->list == nil) {
171 [self logWithFormat:@"ERROR: no 'list' binding is set!"];
176 self->state->doScriptScrolling = NO;
177 self->template = [_c retain];
183 [self->state release];
184 [self->colors release];
185 [self->labels release];
186 [self->icons release];
188 [self->list release];
189 [self->currentBatch release];
190 [self->batchSize release];
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];
203 [self->indexOfFirst release];
204 [self->indexOfLast release];
206 [self->collapseOnClient release];
207 [self->scrollOnClient release];
208 [self->autoScroll release];
209 [self->showBatchResizeButtons release];
211 [self->sortAction release];
212 [self->firstAction release];
213 [self->previousAction release];
214 [self->nextAction release];
215 [self->lastAction release];
217 [self->fontColor release];
218 [self->fontFace release];
219 [self->fontSize release];
221 [self->groupColor release];
222 [self->groupOpenedIcon release];
223 [self->groupClosedIcon release];
225 [self->allObjects release];
226 [self->scriptID release];
227 [self->template release];
229 [self->cellpadding release];
230 [self->cellspacing release];
231 [self->border release];
233 [self->showGroupTitle release];
239 _applyIdentifier(WETableView *self, WOComponent *comp, NSString *_idx)
242 count = [self->allObjects count];
247 /* find subelement for unique id */
249 for (i = 0; i < count; i++) {
253 [self->index setUnsignedIntValue:i inComponent:comp];
256 [self->item setValue:[self->allObjects objectAtIndex:i]
259 if ([self->previousItem isValueSettable]) {
260 [self->previousItem setValue:(i > self->state->firstIndex)
261 ? [self->allObjects objectAtIndex:i-1] : nil
264 if ([self->previousIndex isValueSettable])
265 [self->previousIndex setUnsignedIntValue:(i-1) inComponent:comp];
267 ident = [self->identifier stringValueInComponent:comp];
269 if ([ident isEqualToString:_idx]) {
270 /* found subelement with unique id */
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];
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]) {
291 value = (i > (int)self->state->firstIndex)
292 ? [self->allObjects objectAtIndex:(i - 1)]
294 [self->previousItem setValue:value inComponent:cmp];
296 if ([self->previousIndex isValueSettable])
297 [self->previousIndex setUnsignedIntValue:(i-1) inComponent:cmp];
300 static inline void _applyState_(WETableView *self, WOComponent *cmp) {
301 if ([self->currentBatch isValueSettable])
302 [self->currentBatch setUnsignedIntValue:self->state->currentBatch
306 - (void)updateStateInContext:(WOContext *)_ctx {
307 WEClientCapabilities *ccaps;
311 unsigned batchIdx, size, cnt;
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];
323 if ([[self->autoScroll valueInComponent:cmp] intValue] < 10)
325 else if (isAutoScroll)
326 self->state->doScriptScrolling = NO;
328 /* use JavaScript on InternetExplorer only */
329 self->state->doScriptScrolling = (self->state->doScriptScrolling)
330 ? (self->state->batchCount > 1)
333 self->state->doScriptScrolling = (self->state->doScriptScrolling)
334 ? [self->scrollOnClient boolValueInComponent:cmp]
337 array = [self->list valueInComponent:cmp];
338 batchIdx = [self->currentBatch unsignedIntValueInComponent:cmp];
339 size = [self->batchSize unsignedIntValueInComponent:cmp];
341 size = (isAutoScroll) ? cnt : size;
342 batchIdx = (batchIdx) ? batchIdx : 1;
343 batchIdx = ((batchIdx * size) < (cnt+size)) ? batchIdx : 1;
345 ASSIGN(self->allObjects, array);
347 self->state->currentBatch = batchIdx;
348 self->state->batchSize = size;
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))
355 : self->state->firstIndex + size - 1;
357 if ([self->indexOfFirst isValueSettable]) {
358 [self->indexOfFirst setUnsignedIntValue:self->state->firstIndex
362 if ([self->indexOfLast isValueSettable]) {
363 [self->indexOfLast setUnsignedIntValue:self->state->lastIndex
367 self->state->doCheckBoxes =
368 ([_ctx isInForm] && [self->selection isValueSettable]) ? YES : NO;
369 self->state->doOverflow = isAutoScroll;
372 - (void)updateScriptIdInContext:(WOContext *)_ctx {
376 tmp = [[_ctx elementID] componentsSeparatedByString:@"."];
377 str = [tmp componentsJoinedByString:@""];
379 ASSIGN(self->scriptID, str);
382 - (void)updateConfigInContext:(WOContext *)_ctx {
386 cmp = [_ctx component];
388 [self->colors updateConfigInContext:_ctx];
389 [self->icons updateConfigInContext:_ctx];
390 [self->labels updateConfigInContext:_ctx];
392 #define SetConfigInContext(_a_, _key_) \
393 if (_a_ && (tmp = [_a_ valueInComponent:cmp])) \
394 [_ctx setObject:tmp forKey:_key_]; \
396 SetConfigInContext(self->fontColor, WETableView_fontColor);
397 SetConfigInContext(self->fontFace, WETableView_fontFace);
398 SetConfigInContext(self->fontSize, WETableView_fontSize);
399 #undef SetConfigInContext
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];
412 [_ctx removeObjectForKey:WETableView_downwardIcon];
413 [_ctx removeObjectForKey:WETableView_upwardIcon];
414 [_ctx removeObjectForKey:WETableView_nonSortIcon];
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];
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];
437 - (NSArray *)_collectDataInContext:(WOContext *)_ctx
439 NSAutoreleasePool *pool;
440 NSMutableArray *matrix = nil;
441 NSMutableArray *headInfos = nil;
443 WOComponent *cmp = nil;
446 int sortedHeadIndex = -2;
448 pool = [[NSAutoreleasePool alloc] init];
449 cmp = [_ctx component];
450 k = [self->sortedKey stringValueInComponent:cmp];
452 first = self->state->firstIndex;
453 last = self->state->lastIndex;
454 matrix = [NSMutableArray arrayWithCapacity:last-first+1];
456 [_ctx setObject:YesNumber forKey:WETableView_CollectMode];
457 [_ctx setObject:k forKey:WETableView_SORTEDKEY];
459 self->state->groupCount = 0;
461 for (i=first; i<=last; i++) {
462 NSMutableArray *infos = nil;
465 _applyItems_(self, cmp, i);
467 [_ctx removeObjectForKey:WETableView_INFOS];
468 [self->template appendToResponse:nil inContext:_ctx];
469 infos = [_ctx objectForKey:WETableView_INFOS];
472 infos = [NSArray array];
474 NSAssert(infos != nil, @"Infos is nil.");
476 if (headInfos == nil) {
478 headInfos = [[NSMutableArray alloc] initWithArray:infos];
480 for (j=0, cnt=[headInfos count]; j<cnt; j++) {
481 WETableViewInfo *headInfo = [headInfos objectAtIndex:j];
483 headInfo->isEven = (((i-first) % 2) == 0) ? YES : NO;
492 if (sortedHeadIndex == -2) { // first time
493 for (j=0; j < cnt; j++) {
494 WETableViewInfo *info;
496 info = [infos objectAtIndex:j];
497 if (info->isSorted) {
502 sortedHeadIndex = (sortedHeadIndex < 0) ? -1 : sortedHeadIndex;
506 WETableViewInfo *headInfo;
507 WETableViewInfo *info;
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;
516 else { // sortedHeadIndex == -1 --> no column is sorted
517 headInfo = [headInfos lastObject];
518 isEven = !headInfo->isEven;
522 for (j = 0; j < cnt; j++) {
523 WETableViewInfo *info = [infos objectAtIndex:j];
524 WETableViewInfo *headInfo = [headInfos objectAtIndex:j];
526 if (!info->isGroup || ((int)j != sortedHeadIndex)) {
527 info->isEven = isEven;
529 [headInfos replaceObjectAtIndex:j withObject:info];
536 BOOL doGroupTitle = [self->showGroupTitle boolValueInComponent:cmp];
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];
548 [matrix addObject:infos];
550 [_ctx removeObjectForKey:WETableView_INFOS];
551 [_ctx removeObjectForKey:WETableView_CollectMode];
552 [_ctx removeObjectForKey:WETableView_SORTEDKEY];
554 matrix = [matrix retain];
558 return [matrix autorelease];
561 - (void)_appendNav:(NSString *)_nav isBlind:(BOOL)_isBlind
562 toResponse:(WOResponse *)_response inContext:(WOContext *)_ctx
566 BOOL doForm = [_ctx isInForm];
568 imgUri = [WETableView_ stringByAppendingString:_nav];
569 imgUri = [imgUri stringByAppendingString:(_isBlind) ? @"_blind" : @""];
570 imgUri = [_ctx objectForKey:imgUri];
571 imgUri = WEUriOfResource(imgUri,_ctx);
573 label = WETableLabelForKey(_nav, _ctx);
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>"];
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];
601 [_response appendContentString:[_ctx componentActionURL]];
602 [_response appendContentString:@"\">"];
605 [_response appendContentCharacter:'['];
606 [_response appendContentString:label];
607 [_response appendContentCharacter:']'];
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];
616 [_response appendContentString:@"\" alt=\""];
617 [_response appendContentString:label];
618 [_response appendContentString:@"\" title=\""];
619 [_response appendContentString:label];
620 [_response appendContentString:@"\" />"];
623 if (!_isBlind || self->state->doScriptScrolling) {
624 [_response appendContentString:@"</a>"];
625 [_ctx deleteLastElementIDComponent];
627 [_response appendContentString:@"</td>"];
630 - (void)_appendPreviousNav:(WOResponse *)_resp inContext:(WOContext *)_ctx {
631 int batch, batchCount;
632 BOOL isFirstBlind, isPreviousBlind, isNextBlind, isLastBlind;
634 batch = self->state->currentBatch;
635 batchCount = self->state->batchCount;
637 isFirstBlind = (batch < 2);
638 isPreviousBlind = (batch < 2);
639 isNextBlind = ((batchCount-1) < batch);
640 isLastBlind = ((batchCount-1) < batch);
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];
651 - (void)_appendNextNav:(WOResponse *)_resp inContext:(WOContext *)_ctx {
652 int batch, batchCount;
653 BOOL isFirstBlind, isPreviousBlind, isNextBlind, isLastBlind;
655 batch = self->state->currentBatch;
656 batchCount = self->state->batchCount;
658 isFirstBlind = (batch < 2);
659 isPreviousBlind = (batch < 2);
660 isNextBlind = ((batchCount-1) < batch);
661 isLastBlind = ((batchCount-1) < batch);
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];
672 - (void)_appendNavigation:(WOResponse *)_resp inContext:(WOContext *)_ctx {
673 [_resp appendContentString:
674 @"<table border='0' cellspacing='0' cellpadding='0'><tr>"];
676 [self _appendPreviousNav:_resp inContext:_ctx];
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>"];
687 [self _appendNextNav:_resp inContext:_ctx];
689 [_resp appendContentString:@"</tr></table>"];
692 - (void)_appendTitle:(WOResponse *)_response inContext:(WOContext *)_ctx {
695 bg = [_ctx objectForKey:WETableView_titleColor];
698 [_response appendContentString:
699 @"<table border='0' width='100%' cellpadding='0' cellspacing='0'>"
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];
711 /* append navigation + extra buttons */
712 WEAppendTD(_response, @"right", @"middle", bg); // <td..>
713 [self _appendNavigation:_response inContext:_ctx];
714 [_response appendContentString:@"</td>"]; // </td>
717 [_response appendContentString:@"</tr></table>"];
720 - (void)_appendHeader:(WOResponse *)_response inContext:(WOContext *)_ctx {
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>"];
730 [_ctx appendElementIDComponent:@"header"];
733 bn = retStrForInt(self->state->currentBatch);
734 [_ctx appendElementIDComponent:bn]; // append batchNumber
737 if (self->state->doCheckBoxes) {
742 selArray = [self->selection valueInComponent:[_ctx component]];
743 doSelectAll = ([selArray count] < ([self->allObjects count] / 2));
745 img = [_ctx objectForKey:(doSelectAll)
746 ? WETableView_select_all
747 : WETableView_deselect_all];
749 img = WEUriOfResource(img, _ctx);
751 [_response appendContentString:
752 @"<td align=\"center\" bgcolor=\""];
753 [_response appendContentString:
754 [_ctx objectForKey:WETableView_headerColor]];
755 [_response appendContentString:@"\">"];
758 [_ctx appendElementIDComponent:@"_sa"]; // select all
760 [_ctx appendElementIDComponent:@"_dsa"]; // deselect all
762 [_response appendContentString:@"<a href=\""];
763 [_response appendContentString:[_ctx componentActionURL]];
764 [_response appendContentString:@"\">"];
767 [_response appendContentString:@"<img border=\"0\" src=\""];
768 [_response appendContentString:img];
769 [_response appendContentString:@"\" alt=\""];
770 [_response appendContentString:(doSelectAll)
773 [_response appendContentString:@"\" title=\""];
774 [_response appendContentString:(doSelectAll)
777 [_response appendContentString:@"\" />"];
780 [_response appendContentString:(doSelectAll) ? @"[+]" : @"[-]"];
782 [_response appendContentString:@"</a>"];
783 [_ctx deleteLastElementIDComponent]; // (de)select all
784 [_response appendContentString:@"</td>"];
786 [self->template appendToResponse:_response inContext:_ctx];
788 [_ctx deleteLastElementIDComponent]; // delete batchNumber
789 [_ctx deleteLastElementIDComponent]; // delete "header"
791 if (self->state->showBatchResizeButtons) {
794 cnt = (self->state->lastIndex - self->state->firstIndex + 1);
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:@"\""];
804 [_response appendContentString:@"></td>"];
808 [_response appendContentString:@"</tr>"];
809 [_ctx removeObjectForKey:WETableView_HeaderMode];
810 [_ctx removeObjectForKey:WETableView_SORTEDKEY];
811 [_ctx removeObjectForKey:WETableView_ISDESCENDING];
814 - (void)_appendResizeButtons:(WOResponse *)_response
815 actionUrl:(NSString *)_actionUrl
816 inContext:(WOContext *)_ctx
821 // append batchSize--Button
822 img = [self->icons->minusResizeIcon stringValueInComponent:[_ctx component]];
823 img = WEUriOfResource(img, _ctx);
824 uri = [_actionUrl stringByAppendingString:@".mm"];
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\" />"];
836 [_response appendContentString:@"<a href=\""];
837 [_response appendContentString:uri];
838 [_response appendContentString:@"\">"];
841 if (img && ![_ctx isInForm]) {
842 [_response appendContentString:@"<img border=\"0\" src=\""];
843 [_response appendContentString:img];
844 [_response appendContentString:@"\" alt=\"minus\" title=\"minus\" />"];
847 [_response appendContentString:@"-"];
849 if (!(img && [_ctx isInForm]))
850 [_response appendContentString:@"</a>"];
852 // append batchSize--Button
853 img = [self->icons->plusResizeIcon stringValueInComponent:[_ctx component]];
854 img = WEUriOfResource(img, _ctx);
855 uri = [_actionUrl stringByAppendingString:@".pp"] ;
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\" />"];
867 [_response appendContentString:@"<a href=\""];
868 [_response appendContentString:uri];
869 [_response appendContentString:@"\">"];
872 if (img && ![_ctx isInForm]) {
873 [_response appendContentString:@"<img border=\"0\" src=\""];
874 [_response appendContentString:img];
875 [_response appendContentString:@"\" alt=\"plus\" title=\"plus\" />"];
878 [_response appendContentString:@"+"];
880 if (!(img && [_ctx isInForm]))
881 [_response appendContentString:@"</a>"];
885 - (void)_appendBatchResizeButtons:(WOResponse *)_response
886 rowSpan:(unsigned int)_rowSpan
887 actionUrl:(NSString *)_actionUrl
888 inContext:(WOContext *)_ctx
893 s = [[StrClass alloc] initWithFormat:
894 @"<td align='center' valign='bottom' width='5' rowspan='%d'", _rowSpan];
895 [_response appendContentString:s];
898 if ([_ctx objectForKey:WETableView_footerColor]) {
899 [_response appendContentString:@" bgcolor='"];
900 [_response appendContentString:
901 [_ctx objectForKey:WETableView_footerColor]];
902 [_response appendContentCharacter:'\''];
904 [_response appendContentCharacter:'>'];
906 // apppend resize buttons
907 [self _appendResizeButtons:_response
911 [_response appendContentString:@"</td>"];
914 - (void)_appendData:(WOResponse *)_response inContext:(WOContext *)_ctx {
917 NSString *batchSizeUrl = nil;
918 NSArray *selArray = nil;
919 NSString *groupId = nil;
920 BOOL hideObject = NO;
921 unsigned i, cnt, first;
923 comp = [_ctx component];
924 matrix = [self _collectDataInContext:_ctx];
925 first = self->state->firstIndex;
926 cnt = [matrix count];
928 if (matrix == nil || cnt == 0)
931 if (self->state->doCheckBoxes)
932 selArray = [self->selection valueInComponent:comp];
934 if (self->state->doScriptCollapsing)
935 [self _appendGroupCollapseScript:_response inContext:_ctx];
937 [_ctx appendElementIDComponent:@"data"];
940 bn = retStrForInt(self->state->currentBatch);
941 [_ctx appendElementIDComponent:bn]; // append batchNumber
945 batchSizeUrl = [_ctx componentActionURL];
947 if (self->identifier == nil) {
950 s = retStrForInt(first);
951 [_ctx appendElementIDComponent:s];
955 for (i = 0; i < cnt; i++) {
956 NSMutableArray *infos = nil;
958 _applyItems_(self, comp, first+i);
959 if (self->identifier) {
962 ident = [self->identifier stringValueInComponent:comp];
963 [_ctx appendElementIDComponent:ident];
966 infos = [matrix objectAtIndex:i];
968 if ([[infos lastObject] isEqual:WETableView_GroupMode]) {
971 groupId = [StrClass stringWithFormat:@"group%d", i];
973 rowSpan = ((i==0) && self->state->showBatchResizeButtons)
974 ? cnt+self->state->groupCount
976 [self _appendGroupTitle:_response
979 actionUrl:batchSizeUrl
983 if ((self->state->groupCount > 0) && !self->state->doScriptCollapsing &&
984 (self->showGroup) && ![self->showGroup boolValueInComponent:comp])
990 [_ctx setObject:infos forKey:WETableView_INFOS];
993 if (self->identifier == nil)
994 [_ctx incrementLastElementIDComponent];
996 [_ctx deleteLastElementIDComponent]; // delete identifier
1000 [_response appendContentString:@"<tr"];
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;\""];
1009 [_response appendContentCharacter:'>'];
1011 [_ctx setObject:YesNumber forKey:WETableView_DataMode];
1013 if (self->state->doCheckBoxes) {
1014 WETableViewInfo *info = nil;
1018 info = ([infos count]) ? [infos objectAtIndex:0] : nil;
1020 bg = (info && info->isEven)
1021 ? [_ctx objectForKey:WETableView_evenColor]
1022 : [_ctx objectForKey:WETableView_oddColor];
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];
1034 [_response appendContentCharacter:'"'];
1036 if ([selArray containsObject:[self->allObjects objectAtIndex:first+i]])
1037 [_response appendContentString:@" checked=\"checked\""];
1039 [_response appendContentString:@" />"];
1040 [_response appendContentString:@"</td>"];
1042 [_ctx deleteLastElementIDComponent]; // delete "cb"
1045 [self->template appendToResponse:_response inContext:_ctx];
1047 if (!i && self->state->showBatchResizeButtons &&!self->state->groupCount) {
1048 [self _appendBatchResizeButtons:_response
1049 rowSpan:cnt+self->state->groupCount
1050 actionUrl:batchSizeUrl
1054 [_response appendContentString:@"</tr>"];
1056 if (self->identifier == nil)
1057 [_ctx incrementLastElementIDComponent];
1059 [_ctx deleteLastElementIDComponent]; // delete identifier
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];
1068 - (void)_appendFooter:(WOResponse *)_response inContext:(WOContext *)_ctx {
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;
1077 first = (count) ? first : 0;
1078 batchCnt = (batchCnt) ? batchCnt : 1;
1079 bg = [_ctx objectForKey:WETableView_footerColor];
1081 [_response appendContentString:
1082 @"<table border='0' width='100%' cellpadding='0' cellspacing='0'>"];
1083 [_response appendContentString:@"<tr>"]; // <TR>
1085 WEAppendTD(_response, @"left", nil, bg); // <TD...>
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];
1093 [_response appendContentString:@"<small>"];
1094 if (!self->state->doOverflow) {
1095 s = [[StrClass alloc] initWithFormat:@"%d ", first];
1096 [_response appendContentString:s];
1098 [_response appendContentString:WETableLabelForKey(@"to", _ctx)];
1099 s = [[StrClass alloc] initWithFormat:@" %d ", last];
1100 [_response appendContentString:s];
1102 [_response appendContentString:WETableLabelForKey(@"of", _ctx)];
1104 s = [[StrClass alloc] initWithFormat:@" %d", count];
1105 [_response appendContentString:s];
1107 [_response appendContentString:@"</small>"];
1109 [_response appendContentString:@"</td>"]; // </td>
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...>
1119 [_response appendContentString:@"<small>"];
1120 [_response appendContentString:@" "];
1121 [_response appendContentString:WETableLabelForKey(@"page", _ctx)];
1122 s = [[StrClass alloc] initWithFormat:@": %d ", batch];
1123 [_response appendContentString:s];
1125 [_response appendContentString:WETableLabelForKey(@"of", _ctx)];
1126 s = [[StrClass alloc] initWithFormat:@" %d", batchCnt];
1127 [_response appendContentString:s];
1129 [_response appendContentString:@" "];
1130 [_response appendContentString:@"</small>"];
1131 if (ShowNavigationInFooter) {
1132 [_response appendContentString:@"</td>"];
1133 [self _appendNextNav:_response inContext:_ctx];
1134 [_response appendContentString:@"</tr></table>"];
1138 [self _appendResizeButtons:_response
1139 actionUrl:[_ctx componentActionURL]
1143 [_response appendContentString:@"</td></tr>"]; // </td></tr>
1144 [_response appendContentString:@"</table>"]; // </table>
1147 // --- action handler -----------------------------------------------------
1149 - (void)_handleSortActionInContext:(WOContext *)_ctx {
1156 if (self->sortedKey == nil || self->isDescending == nil)
1157 return; // nothing to do
1159 if ([_ctx objectForKey:WETableView_SORTEDKEY] == nil ||
1160 [_ctx objectForKey:WETableView_ISDESCENDING] == nil)
1161 return; // nothing to do
1163 cmp = [_ctx component];
1164 key = [_ctx objectForKey:WETableView_SORTEDKEY];
1165 isDesc = [[_ctx objectForKey:WETableView_ISDESCENDING] boolValue];
1167 oldIsDesc = [self->isDescending boolValueInComponent:cmp];
1168 oldKey = [self->sortedKey stringValueInComponent:cmp];
1170 if ([oldKey isEqual:key] && oldIsDesc == isDesc)
1171 return; // nothing to do
1173 if ([self->isDescending isValueSettable])
1174 [self->isDescending setBoolValue:isDesc inComponent:cmp];
1175 if ([self->sortedKey isValueSettable])
1176 [self->sortedKey setStringValue:key inComponent:cmp];
1178 if (self->sortAction == nil && key != nil) {
1184 sel = (isDesc) ? EOCompareDescending : EOCompareAscending;
1185 so = [EOSortOrdering sortOrderingWithKey:key selector:sel];
1187 soArray = [[NSArray alloc] initWithObjects:&so count:1];
1188 tmp = [self->allObjects sortedArrayUsingKeyOrderArray:soArray];
1191 if ([self->list isValueSettable])
1192 [self->list setValue:tmp inComponent:[_ctx component]];
1194 [[_ctx component] debugWithFormat:
1195 @"couldn't set sorted list on 'list' binding"];
1198 else if (self->sortAction)
1199 [self->sortAction valueInComponent:cmp];
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]);
1211 - (void)_handlePreviousButtonInContext:(WOContext *)_ctx {
1212 if (self->previousAction)
1213 [self->previousAction valueInComponent:[_ctx component]];
1215 unsigned batch = self->state->currentBatch;
1217 self->state->currentBatch = ((batch -1) > 0) ? batch - 1 : 1;
1218 _applyState_(self, [_ctx component]);
1222 - (void)_handleNextButtonInContext:(WOContext *)_ctx {
1223 if (self->nextAction)
1224 [self->nextAction valueInComponent:[_ctx component]];
1226 unsigned batch = self->state->currentBatch;
1227 unsigned cnt = self->state->batchCount;
1229 self->state->currentBatch = ((batch +1) < cnt) ? batch + 1 : cnt;
1230 _applyState_(self, [_ctx component]);
1234 - (void)_handleLastButtonInContext:(WOContext *)_ctx {
1235 if (self->lastAction)
1236 [self->lastAction valueInComponent:[_ctx component]];
1238 self->state->currentBatch = self->state->batchCount;
1239 _applyState_(self, [_ctx component]);
1243 // --- main methodes -------------------------------------------------------
1245 - (void)takeValuesFromRequest:(WORequest *)_request
1246 forBatch:(int)_batch
1247 selections:(NSMutableArray *)_selArray
1248 inContext:(WOContext *)_ctx
1253 first = self->state->firstIndex;
1254 last = self->state->lastIndex;
1258 bn = retStrForInt(self->state->currentBatch);
1259 [_ctx appendElementIDComponent:bn]; // append batchNumber
1263 eid = [_ctx elementID];
1265 if ([_request formValueForKey:[eid stringByAppendingString:@".pp.x"]]) {
1266 [_ctx addActiveFormElement:self];
1267 [_ctx setRequestSenderID:
1268 [[_ctx senderID] stringByAppendingString:@".pp"]];
1270 else if ([_request formValueForKey:[eid stringByAppendingString:@".mm.x"]]) {
1271 [_ctx addActiveFormElement:self];
1272 [_ctx setRequestSenderID:
1273 [[_ctx senderID] stringByAppendingString:@".mm"]];
1276 if (self->identifier == nil) { // append index
1277 s = retStrForInt(first);
1278 [_ctx appendElementIDComponent:s];
1282 for (i = first; i <= last; i++) {
1283 _applyItems_(self, [_ctx component], i);
1284 if (self->identifier) {
1287 s = [self->identifier stringValueInComponent:[_ctx component]];
1288 [_ctx appendElementIDComponent:s];
1292 NSString *cbID; // checkBoxID
1296 cbID = [[_ctx elementID] stringByAppendingString:@".cb"];
1297 obj = [self->item valueInComponent:[_ctx component]];
1300 if ((formValue = [_request formValueForKey:cbID])) {
1301 if (![_selArray containsObject:obj])
1302 [_selArray addObject:obj];
1304 else if ([_selArray containsObject:obj])
1305 [_selArray removeObject:obj];
1308 [self->template takeValuesFromRequest:_request inContext:_ctx];
1310 if (self->identifier == nil)
1311 [_ctx incrementLastElementIDComponent];
1313 [_ctx deleteLastElementIDComponent]; // delete identifier
1315 if (self->identifier == nil)
1316 [_ctx deleteLastElementIDComponent]; // delete index
1318 [_ctx deleteLastElementIDComponent]; // delete batchNumber
1321 - (void)takeValuesFromRequest:(WORequest *)_request inContext:(WOContext *)_ctx {
1322 int i, firstBatch, lastBatch, savedCurrentBatch;
1324 NSMutableArray *selArray = nil;
1326 [self updateStateInContext:_ctx];
1328 eid = [_ctx elementID];
1330 /* handle "data" section */
1332 if (self->state->doCheckBoxes) {
1333 selArray = [self->selection valueInComponent:[_ctx component]];
1334 selArray = [selArray mutableCopyWithZone:[self zone]];
1337 firstBatch = (self->state->doScriptScrolling) ? 1 : self->state->currentBatch;
1339 lastBatch = (self->state->doScriptScrolling)
1340 ? self->state->batchCount
1341 : self->state->currentBatch;
1344 [_ctx appendElementIDComponent:@"data"];
1345 [_ctx setObject:YesNumber forKey:WETableView_DataMode];
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];
1353 [self takeValuesFromRequest:_request
1358 [_ctx removeObjectForKey:WETableView_DataMode];
1360 if (self->state->currentBatch != (unsigned)savedCurrentBatch) {
1361 self->state->currentBatch = savedCurrentBatch;
1362 _applyState_(self, [_ctx component]);
1365 [_ctx deleteLastElementIDComponent]; // delete "data"
1367 if (self->state->doCheckBoxes) {
1368 [self->selection setValue:selArray inComponent:[_ctx component]];
1372 // handle header (sort buttons, ...)
1373 [_ctx setObject:YesNumber forKey:WETableView_HeaderMode];
1374 [_ctx appendElementIDComponent:@"header"];
1376 for (i = 1; i <= (int)self->state->batchCount; i++) {
1380 s = retStrForInt(i);
1381 [_ctx appendElementIDComponent:s];
1384 [self->template takeValuesFromRequest:_request inContext:_ctx];
1385 [_ctx deleteLastElementIDComponent]; // delete batchNumber
1388 [_ctx deleteLastElementIDComponent]; // delete "header"
1389 [_ctx removeObjectForKey:WETableView_HeaderMode];
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];
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];
1406 [_ctx setObject:YesNumber forKey:WETableView_FooterMode];
1407 [_ctx appendElementIDComponent:@"footer"];
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"]];
1416 else if ([_request formValueForKey:
1417 [eid stringByAppendingString:@".footer.mm.x"]]) {
1418 [_ctx addActiveFormElement:self];
1419 [_ctx setRequestSenderID:
1420 [[_ctx senderID] stringByAppendingString:@".mm"]];
1423 [self->template takeValuesFromRequest:_request inContext:_ctx];
1424 [_ctx deleteLastElementIDComponent]; // delete "footer"
1425 [_ctx removeObjectForKey:WETableView_FooterMode];
1427 if ([_request formValueForKey:[eid stringByAppendingString:@".first.x"]]) {
1428 [_ctx addActiveFormElement:self];
1429 [_ctx setRequestSenderID:
1430 [[_ctx senderID] stringByAppendingString:@".first"]];
1432 if ([_request formValueForKey:[eid stringByAppendingString:@".next.x"]]) {
1433 [_ctx addActiveFormElement:self];
1434 [_ctx setRequestSenderID:
1435 [[_ctx senderID] stringByAppendingString:@".next"]];
1437 if ([_request formValueForKey:[eid stringByAppendingString:@".last.x"]]) {
1438 [_ctx addActiveFormElement:self];
1439 [_ctx setRequestSenderID:
1440 [[_ctx senderID] stringByAppendingString:@".last"]];
1442 if ([_request formValueForKey:[eid stringByAppendingString:@".previous.x"]]) {
1443 [_ctx addActiveFormElement:self];
1444 [_ctx setRequestSenderID:
1445 [[_ctx senderID] stringByAppendingString:@".previous"]];
1449 - (id)increaseAutoScrollHeightInContext:(WOContext *)_ctx {
1450 if ([self->autoScroll isValueSettable]) {
1451 int sh; // scrollHeight
1453 sh = [self->autoScroll intValueInComponent:[_ctx component]] + 20;
1454 [self->autoScroll setIntValue:sh inComponent:[_ctx component]];
1459 - (id)decreaseAutoScrollHeightInContext:(WOContext *)_ctx {
1460 if ([self->autoScroll isValueSettable]) {
1461 int sh; // scrollHeight
1463 sh = [self->autoScroll intValueInComponent:[_ctx component]] - 20;
1465 [self->autoScroll setIntValue:sh inComponent:[_ctx component]];
1471 - (id)increaseBatchSizeInContext:(WOContext *)_ctx {
1472 if ([self->batchSize isValueSettable]) {
1475 bs = [self->batchSize intValueInComponent:[_ctx component]] + 1;
1476 [self->batchSize setIntValue:bs inComponent:[_ctx component]];
1481 - (id)decreaseBatchSizeInContext:(WOContext *)_ctx {
1482 if ([self->batchSize isValueSettable]) {
1485 bs = [self->batchSize intValueInComponent:[_ctx component]] - 1;
1487 [self->batchSize setIntValue:bs inComponent:[_ctx component]];
1492 - (id)invokeActionForRequest:(WORequest *)_request inContext:(WOContext *)_ctx {
1497 [self updateStateInContext:_ctx];
1499 eid = [_ctx currentElementID];
1500 cmp = [_ctx component];
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"]) {
1513 [_ctx consumeElementID]; // consume "data"
1514 [_ctx appendElementIDComponent:eid]; // append "data"
1519 bn = [_ctx currentElementID];
1520 if ([self->currentBatch isValueSettable])
1521 [self->currentBatch setIntValue:[bn intValue] inComponent:cmp];
1523 [_ctx consumeElementID]; // consume batchNumber
1524 [_ctx appendElementIDComponent:bn]; // append batch
1527 if ((idxId = [_ctx currentElementID])) {
1528 [_ctx consumeElementID]; // consume index-id
1529 [_ctx appendElementIDComponent:idxId]; // append index-id
1532 if ([idxId isEqualToString:@"pp"])
1533 result = [self increaseBatchSizeInContext:_ctx];
1534 else if ([idxId isEqualToString:@"mm"])
1535 result = [self decreaseBatchSizeInContext:_ctx];
1537 if (self->identifier == nil) {
1540 idx = [idxId unsignedIntValue];
1541 if (idx < [self->allObjects count] && idx >= 0) {
1542 _applyItems_(self, cmp, idx);
1545 NSLog(@"WETableView: index is out of range!");
1548 _applyIdentifier(self, cmp, idxId);
1550 result = [self invokeGrouping:_request inContext:_ctx];
1552 [_ctx deleteLastElementIDComponent]; // delete index-id
1554 [_ctx deleteLastElementIDComponent]; // delete batchNumber
1555 [_ctx deleteLastElementIDComponent]; // delete "data"
1557 else if ([eid isEqual:@"header"]) {
1558 [_ctx consumeElementID]; // consume "header"
1559 [_ctx appendElementIDComponent:eid]; // append "header"
1561 if ([self->currentBatch isValueSettable]) {
1562 int bn = [[_ctx currentElementID] intValue];
1563 [self->currentBatch setIntValue:bn inComponent:cmp];
1565 [_ctx appendElementIDComponent:[_ctx currentElementID]]; // batchNumber
1566 [_ctx consumeElementID]; // consume batchNumber
1568 // handle selectAllCheckBoxes:
1569 if ([[_ctx currentElementID] isEqualToString:@"_sa"]) {
1570 NSMutableArray *selArray;
1572 selArray = [self->allObjects mutableCopyWithZone:[self zone]];
1573 [self->selection setValue:selArray inComponent:cmp];
1576 // handle deselectAllCheckBoxes:
1577 else if ([[_ctx currentElementID] isEqualToString:@"_dsa"]) {
1578 [self->selection setValue:[NSMutableArray array] inComponent:cmp];
1581 result = [self->template invokeActionForRequest:_request inContext:_ctx];
1583 [_ctx deleteLastElementIDComponent]; // delete batchNumber
1584 [_ctx deleteLastElementIDComponent]; // delete "header"
1586 [self _handleSortActionInContext:_ctx];
1588 [_ctx removeObjectForKey:WETableView_SORTEDKEY];
1589 [_ctx removeObjectForKey:WETableView_ISDESCENDING];
1591 else if ([eid isEqual:@"title"] || [eid isEqual:@"button"] ||
1592 [eid isEqual:@"footer"]) {
1593 [_ctx consumeElementID];
1594 [_ctx appendElementIDComponent:eid];
1596 eid = [_ctx currentElementID];
1598 // reset autoScrollHeight
1599 if ([eid isEqualToString:@"pp"])
1600 result = [self increaseAutoScrollHeightInContext:_ctx];
1601 else if ([eid isEqualToString:@"mm"])
1602 result = [self decreaseAutoScrollHeightInContext:_ctx];
1604 result = [self->template invokeActionForRequest:_request inContext:_ctx];
1606 [_ctx deleteLastElementIDComponent];
1609 result = [self->template invokeActionForRequest:_request inContext:_ctx];
1614 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
1617 [self updateStateInContext:_ctx];
1618 [self updateScriptIdInContext:_ctx];
1619 [self updateConfigInContext:_ctx];
1621 cmp = [_ctx component];
1623 /* open tableView */
1624 [_response appendContentString:
1625 @"<table border='0' width='100%' cellpadding='0' cellspacing='0'>"];
1627 /* append tableTitle + navigation */
1628 [_response appendContentString:@"<tr><td>"];
1629 [self _appendTitle:_response inContext:_ctx];
1630 [_response appendContentString:@"</td></tr>"];
1632 [_response appendContentString:@"<tr><td></td></tr>"];
1634 if (self->state->doScriptScrolling) {
1635 [self _appendTableContentAsScript:_response inContext:_ctx]; //close tables
1638 /* open header + data area */
1639 [_response appendContentString:@"<tr><td>"];
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\">"];
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:@"'>"];
1660 self->state->showBatchResizeButtons =
1661 ([self->showBatchResizeButtons boolValueInComponent:cmp] &&
1662 (self->state->currentBatch < self->state->batchCount) &&
1663 !self->state->doOverflow);
1665 [self _appendHeader:_response inContext:_ctx];
1666 [self _appendData:_response inContext:_ctx];
1668 [_response appendContentString:@"</table>"];
1669 if (self->state->doOverflow)
1670 [_response appendContentString:@"</p>"];
1672 /* close header + data area */
1673 [_response appendContentString:@"</td></tr>"];
1675 [_response appendContentString:@"</table>"]; // </TABLE>
1678 [self _appendFooter:_response inContext:_ctx];
1684 if (self->state->doScriptScrolling)
1685 [self appendJavaScript:_response inContext:_ctx];
1687 [self removeConfigInContext:_ctx];
1690 @end /* WETableView */
1692 @implementation WETableViewInfo
1695 // --- JavaScript additions -------------------------------------------------
1697 @implementation WETableView(JavaScriptAdditions)
1699 - (void)_appendGroupCollapseScript:(WOResponse *)_resp
1700 inContext:(WOContext *)_ctx
1702 if ([_ctx objectForKey:WETableView_HasCollapseScript])
1705 [_resp appendContentString:
1706 @"\n<script language=\"JavaScript\">\n"
1708 @"function toggleTableGroup()\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"
1727 [_ctx setObject:YesNumber forKey:WETableView_HasCollapseScript];
1730 - (void)jsButton:(WOResponse *)_resp ctx:(WOContext *)_ctx
1731 name:(NSString *)_name button:(NSString *)_button
1736 _button = [_button stringByAppendingString:@".gif"];
1737 imgUri = WEUriOfResource(_button, _ctx);
1738 n = [_name stringByAppendingString:self->scriptID];
1740 n = [[StrClass alloc] initWithFormat:
1741 @"var %@ = new Image(); %@.src = \"%@\";\n", n, n, imgUri];
1742 [_resp appendContentString:n];
1746 - (void)appendJavaScript:(WOResponse *)_resp inContext:(WOContext *)_ctx {
1749 [_resp appendContentString:@"<script language=\"JavaScript\">\n<!--\n"];
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"];
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"
1768 @" page%@[i][\"Div\"].style.display = \"none\";\n"
1769 @" footer%@[i][\"Div\"].style.display = \"none\";\n"
1772 @" flushImages%@();\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
1783 [_resp appendContentString:n];
1786 n = [[StrClass alloc] initWithFormat:
1787 @"function firstPage%@() {\n"
1788 @" actualPage%@ = 1;\n"
1791 self->scriptID, // firstPage
1792 self->scriptID, // actualPage
1793 self->scriptID // showPage
1795 [_resp appendContentString:n];
1798 n = [[StrClass alloc] initWithFormat:
1799 @"function previousPage%@() {\n"
1800 @" if (actualPage%@ > 1) {\n"
1801 @" actualPage%@--;\n"
1805 self->scriptID, // previousPage
1806 self->scriptID, // actualPage
1807 self->scriptID, // actualPage
1808 self->scriptID // showPage
1810 [_resp appendContentString:n];
1813 n = [[StrClass alloc] initWithFormat:
1814 @"function nextPage%@() {\n"
1815 @" if (actualPage%@ < page%@.length - 1) {\n"
1816 @" actualPage%@++;\n"
1820 self->scriptID, // nextPage
1821 self->scriptID, // actualPage
1822 self->scriptID, // page
1823 self->scriptID, // actualPage
1824 self->scriptID // showPage
1826 [_resp appendContentString:n];
1829 n = [[StrClass alloc] initWithFormat:
1830 @"function lastPage%@() {\n"
1831 @" actualPage%@ = page%@.length - 1;\n"
1834 self->scriptID, // lastPage
1835 self->scriptID, // actualPage
1836 self->scriptID, // page
1837 self->scriptID // showPage
1839 [_resp appendContentString:n];
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"
1854 @" if (actualPage%@ == 2) {\n"
1855 @" document.images[\"firstPageImg%@\"].src = First2%@.src;\n"
1857 @" if (actualPage%@ == page%@.length -2) {\n"
1858 @" document.images[\"lastPageImg%@\"].src = Last2%@.src;\n"
1862 @" if (actualPage%@ == page%@.length - 1) {\n"
1863 @" document.images[\"nextPageImg%@\"].src = Next2%@.src;\n"
1864 @" document.images[\"lastPageImg%@\"].src = Last2%@.src;\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
1883 self->scriptID, // actualPage
1884 self->scriptID, // firstPageImg
1885 self->scriptID, // First2
1887 self->scriptID, // actualPage
1888 self->scriptID, // page
1889 self->scriptID, // lastPageImg
1890 self->scriptID, // Last2
1893 self->scriptID, // actualPage
1894 self->scriptID, // page
1895 self->scriptID, // nextPageImg,
1896 self->scriptID, // Next2
1897 self->scriptID, // lastPageImg
1898 self->scriptID // Last2
1900 [_resp appendContentString:n];
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
1912 [_resp appendContentString:n];
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
1926 self->scriptID, // page
1929 self->scriptID, // pageDiv
1930 self->scriptID, // footer
1932 self->scriptID, // footer
1935 self->scriptID // footerDiv
1937 [_resp appendContentString:n];
1941 n = [[StrClass alloc] initWithFormat:@"showPage%@();", self->scriptID];
1942 [_resp appendContentString:n];
1945 [_resp appendContentString:@"//-->\n</script>\n"];
1948 - (void)_appendTableContentAsScript:(WOResponse *)_resp
1949 inContext:(WOContext *)_ctx
1952 unsigned i, savedBatchIndex;
1954 cmp = [_ctx component];
1956 savedBatchIndex = self->state->currentBatch;
1957 /* open header + data area */
1958 [_resp appendContentString:@"<tr><td>"];
1960 for (i = 1; i <= self->state->batchCount; i++) {
1963 self->state->currentBatch = i;
1964 _applyState_(self, cmp);
1965 [self updateStateInContext:_ctx];
1967 s = [[StrClass alloc] initWithFormat: // <DIV...>
1968 @"<div id=\"page%dDiv%@\" style=\"display: ; \">", i, self->scriptID];
1969 [_resp appendContentString:s];
1972 [_resp appendContentString:
1973 @"<table border='0' width='100%' cellpadding='1' cellspacing='0'>"];
1975 [self _appendHeader:_resp inContext:_ctx];
1976 [self _appendData:_resp inContext:_ctx];
1978 [_resp appendContentString:@"</table>"];
1979 [_resp appendContentString:@"</div>"]; // </DIV>
1983 s = [[StrClass alloc] initWithFormat: // <DIV...>
1984 @"<div id=\"footer%dDiv%@\" style=\"display: ; \">", i, self->scriptID];
1985 [_resp appendContentString:s];
1988 [self _appendFooter:_resp inContext:_ctx];
1990 [_resp appendContentString:@"</div>"]; // </DIV>
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);
1999 [self updateStateInContext:_ctx];
2002 - (void)_appendScriptLink:(WOResponse *)_response name:(NSString *)_name {
2005 [_response appendContentString:@"JavaScript:"];
2006 [_response appendContentString:_name];
2007 s = [[StrClass alloc] initWithFormat:@"Page%@();", self->scriptID];
2008 [_response appendContentString:s];
2012 - (void)_appendScriptImgName:(WOResponse *)_response name:(NSString *)_name {
2013 [_response appendContentString:_name];
2014 [_response appendContentString:@"PageImg"];
2015 [_response appendContentString:self->scriptID];
2018 @end /* WETableView(JavaScriptAdditions) */