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
26 @interface WOPopUpButton : WOInput
28 // WODynamicElement: extraAttributes
29 // WODynamicElement: otherTagString
36 WOAssociation *selection;
37 WOAssociation *string; // WO4
38 WOAssociation *noSelectionString; // WO4
39 WOAssociation *selectedValue; // WO4.5
40 WOAssociation *escapeHTML; // WO4.5
41 WOAssociation *itemGroup; // SOPE
42 WOElement *template; // SOPE?
47 @class WOResponse, WOContext;
49 @interface WOPopUpButton(PrivateMethods)
50 - (void)appendOptionsToResponse:(WOResponse *)_response
51 inContext:(WOContext *)_ctx;
57 static int profElements = -1;
58 static Class NSDateClass = Nil;
60 @interface WOContext(ComponentStackCount)
61 - (unsigned)componentStackCount;
66 @implementation WOPopUpButton
68 static NSNumber *yesNum = nil;
71 return [super version] + 0 /* v2 */;
75 NSAssert2([super version] == 2,
76 @"invalid superclass (%@) version %i !",
77 NSStringFromClass([self superclass]), [super version]);
79 if (yesNum == nil) yesNum = [[NSNumber numberWithBool:YES] retain];
82 - (void)_handleDeprecatedBindings:(NSDictionary *)_config {
85 if ((tmp = OWGetProperty(_config, @"singleSelection"))) {
86 if ([tmp isValueConstant]) {
87 if ([tmp boolValueInComponent:nil]) {
88 [self debugWithFormat:
89 @"Note: template uses deprecated 'singleSelection' binding!"];
92 [self errorWithFormat:
93 @"'singleSelection' binding is set to NO, which is "
98 [self errorWithFormat:
99 @"will ignore deprecated 'singleSelection' binding: %@", tmp];
105 - (id)initWithName:(NSString *)_name
106 associations:(NSDictionary *)_config
107 template:(WOElement *)_t
110 if (profElements == -1) {
111 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
112 profElements = [[ud objectForKey:@"WOProfileElements"] boolValue] ? 1 : 0;
114 if (NSDateClass == Nil)
115 NSDateClass = [NSDate class];
118 if ((self = [super initWithName:_name associations:_config template:_t])) {
119 self->list = OWGetProperty(_config, @"list");
120 self->item = OWGetProperty(_config, @"item");
121 self->selection = OWGetProperty(_config, @"selection");
122 self->string = OWGetProperty(_config, @"string");
123 if (self->string == nil )
124 self->string = OWGetProperty(_config, @"displayString");
126 self->noSelectionString = OWGetProperty(_config, @"noSelectionString");
127 self->selectedValue = OWGetProperty(_config, @"selectedValue");
128 self->escapeHTML = OWGetProperty(_config, @"escapeHTML");
129 self->itemGroup = OWGetProperty(_config, @"itemGroup");
131 self->template = [_t retain];
133 if (self->selection != nil && self->selectedValue != nil)
135 @"cannot have both 'selection' and 'selectedValue' bindings!"];
139 if (self->noSelectionString == nil)
140 self->noSelectionString = OWGetProperty(_config, @"nilString");
142 if (self->escapeHTML == nil)
143 self->escapeHTML = [[WOAssociation associationWithValue:yesNum] retain];
145 [self _handleDeprecatedBindings:_config];
151 [self->noSelectionString release];
152 [self->escapeHTML release];
153 [self->list release];
154 [self->item release];
155 [self->selection release];
156 [self->string release];
157 [self->selectedValue release];
158 [self->itemGroup release];
162 /* handling the request */
164 - (void)takeValuesFromRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
165 WOComponent *sComponent;
170 sComponent = [_ctx component];
171 if ([self->disabled boolValueInComponent:[_ctx component]])
174 formValue = [_rq formValueForKey:OWFormElementName(self, _ctx)];
176 [self logWithFormat:@"%@: value=%@ ..", [self elementID], formValue];
179 if (formValue == nil) {
180 /* nothing changed, or not in submitted form */
184 objects = [self->list valueInComponent:sComponent];
187 if (self->value != nil) {
188 /* has a value binding, walk list to find object */
191 for (i = 0, toGo = [objects count]; i < toGo; i++) {
194 object = [objects objectAtIndex:i];
196 if ([self->item isValueSettable])
197 [self->item setValue:object inComponent:sComponent];
199 cv = [self->value stringValueInComponent:sComponent];
201 if ([cv isEqualToString:formValue])
205 else if (![formValue isEqualToString:WONoSelectionString]) {
206 /* an index binding */
209 idx = [formValue intValue];
210 if (idx >= (int)[objects count]) {
211 [[_ctx page] logWithFormat:@"popup-index %i out of range 0-%i",
212 idx, [objects count] - 1];
216 object = [objects objectAtIndex:idx];
219 if ([self->selectedValue isValueSettable])
220 [self->selectedValue setValue:formValue inComponent:sComponent];
222 /* process selection */
224 if ([self->selection isValueSettable]) {
228 sel = [object retain];
230 else /* nil item selected */
233 [self->selection setValue:sel inComponent:sComponent];
234 [sel release]; sel = nil;
236 if ([self->item isValueSettable])
237 [self->item setValue:nil inComponent:sComponent]; // Reset 'item'
240 /* generate response */
242 - (void)appendOptionsToResponse:(WOResponse *)_response
243 inContext:(WOContext *)_ctx
245 WOComponent *sComponent = [_ctx component];
246 NSString *nilStr = nil;
247 NSArray *array = nil;
252 id previousGroup = nil;
254 NSTimeInterval st = 0.0;
257 st = [[NSDateClass date] timeIntervalSince1970];
260 escapesHTML = [self->escapeHTML boolValueInComponent:sComponent];
261 nilStr = [self->noSelectionString stringValueInComponent:sComponent];
262 array = [self->list valueInComponent:sComponent];
263 if (self->selection == nil){
264 if (self->selectedValue != nil){
266 sel = [self->selectedValue valueInComponent:sComponent];
274 if (self->selectedValue != nil){
276 sel = [self->selectedValue valueInComponent:sComponent];
277 NSLog(@"WARNING(%@): "
278 @"using both 'selection' and 'selectedValue' bindings!",
283 sel = [self->selection valueInComponent:sComponent];
286 toGo = [array count];
292 diff = [[NSDateClass date] timeIntervalSince1970] - st;
294 for (j = [_ctx componentStackCount]; j >= 0; j--)
296 printf("PopUpOption[setup] %s: %0.3fs\n",
297 [[_ctx elementID] cString], diff);
303 if (self->itemGroup != nil) {
306 if ([self->item isValueSettable])
307 [self->item setValue:nil inComponent:sComponent];
308 group = [self->itemGroup stringValueInComponent:sComponent];
311 WOResponse_AddCString(_response, "<optgroup label=\"");
313 WOResponse_AddHtmlString(_response, group);
316 WOResponse_AddString(_response, group);
318 WOResponse_AddCString(_response, "\">");
319 previousGroup = [group retain];
322 WOResponse_AddCString(_response, "<option value=\"");
323 WOResponse_AddString(_response, WONoSelectionString);
324 WOResponse_AddCString(_response, "\">");
325 WOResponse_AddHtmlString(_response, nilStr);
326 WOResponse_AddCString(_response, "</option>");
327 // FIXME (stephane) Shouldn't we set the 'selected' if selArray/selValueArray is empty?
330 for (i = 0; i < toGo; i++) {
332 NSString *displayV = nil;
337 NSTimeInterval st = 0.0;
340 st = [[NSDateClass date] timeIntervalSince1970];
343 object = [array objectAtIndex:i];
345 if ([self->item isValueSettable])
346 [self->item setValue:object inComponent:sComponent];
348 isSelected = sel ? [sel isEqual:object] : NO;
350 ? [self->value stringValueInComponent:sComponent]
351 : [NSString stringWithFormat:@"%i", i];
354 isSelected = sel ? [sel isEqual:v] : NO;
357 isSelected = sel ? [sel isEqual:object] : NO;
359 displayV = self->string
360 ? [self->string stringValueInComponent:sComponent]
361 : [object stringValue];
363 if (displayV == nil) displayV = (escapesHTML ? @"<nil>" : @"<nil>");
365 group = self->itemGroup != nil
366 ? [self->itemGroup stringValueInComponent:sComponent]
370 BOOL groupChanged = NO;
372 if (previousGroup == nil)
375 if (![group isEqualToString:previousGroup]) {
376 WOResponse_AddCString(_response, "</optgroup>");
381 WOResponse_AddCString(_response, "<optgroup label=\"");
383 WOResponse_AddHtmlString(_response, group);
386 WOResponse_AddString(_response, group);
388 WOResponse_AddCString(_response, "\">");
389 ASSIGN(previousGroup, group);
393 if (previousGroup != nil) {
394 WOResponse_AddCString(_response, "</optgroup>");
395 ASSIGN(previousGroup, nil);
398 WOResponse_AddCString(_response, "<option value=\"");
399 WOResponse_AddHtmlString(_response, v); // WO escapes it, always
402 WOResponse_AddString(_response,
403 _ctx->wcFlags.allowEmptyAttributes
404 ? @"\" selected>" : @"\" selected=\"selected\">");
407 WOResponse_AddString(_response, @"\">");
411 WOResponse_AddHtmlString(_response, displayV);
414 WOResponse_AddString(_response, displayV);
416 WOResponse_AddCString(_response, "</option>");
422 diff = [[NSDateClass date] timeIntervalSince1970] - st;
425 for (j = [_ctx componentStackCount]; j >= 0; j--)
428 printf("PopUpOption[%i] %s: %0.3fs\n", i,
429 [[_ctx elementID] cString], diff);
434 if (previousGroup != nil) {
435 WOResponse_AddCString(_response, "</optgroup>");
436 [previousGroup release];
438 if ([self->item isValueSettable])
439 [self->item setValue:nil inComponent:sComponent]; // Reset 'item'
442 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
444 NSTimeInterval st = 0.0;
447 st = [[NSDateClass date] timeIntervalSince1970];
450 WOResponse_AddCString(_response, "<select name=\"");
451 [_response appendContentHTMLAttributeValue:OWFormElementName(self, _ctx)];
452 WOResponse_AddChar(_response, '"');
454 [self appendExtraAttributesToResponse:_response inContext:_ctx];
456 if (self->otherTagString != nil) {
457 WOResponse_AddChar(_response, ' ');
458 WOResponse_AddString(_response,
459 [self->otherTagString stringValueInComponent:
463 if ([self->disabled boolValueInComponent:[_ctx component]])
464 WOResponse_AddCString(_response, " disabled=\"disabled\"");
466 WOResponse_AddChar(_response, '>');
468 [self appendOptionsToResponse:_response inContext:_ctx];
469 [self->template appendToResponse:_response inContext:_ctx];
471 WOResponse_AddCString(_response, "</select>");
477 diff = [[NSDateClass date] timeIntervalSince1970] - st;
480 for (i = [_ctx componentStackCount]; i >= 0; i--)
483 printf("PopUpButton %s: %0.3fs\n",
484 [[_ctx elementID] cString], diff);
492 - (NSString *)associationDescription {
493 NSMutableString *str;
495 str = [NSMutableString stringWithCapacity:256];
496 [str appendString:[super associationDescription]];
498 if (self->list) [str appendFormat:@" list=%@", self->list];
499 if (self->item) [str appendFormat:@" item=%@", self->item];
500 if (self->selection) [str appendFormat:@" selection=%@", self->selection];
501 if (self->string) [str appendFormat:@" displayString=%@", self->string];
502 if (self->noSelectionString)
503 [str appendFormat:@" noselection=%@", self->noSelectionString];
504 if (self->escapeHTML)
505 [str appendFormat:@" escapeHTML=%@", self->escapeHTML];
506 if (self->selectedValue)
507 [str appendFormat:@" selectedValue=%@", self->selectedValue];
509 [str appendFormat:@" itemGroup=%@", self->itemGroup];
514 @end /* WOPopUpButton */