]> err.no Git - sope/blob - sope-appserver/NGObjWeb/DynamicElements/WOPopUpButton.m
synced with latest changes, bumped dyld versions
[sope] / sope-appserver / NGObjWeb / DynamicElements / WOPopUpButton.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "WOInput.h"
23
24 @class WOAssociation;
25
26 @interface WOPopUpButton : WOInput
27 {
28   // WODynamicElement: extraAttributes
29   // WODynamicElement: otherTagString
30   // WOInput:    name
31   // WOInput:    value
32   // WOInput:    disabled
33 @protected
34   WOAssociation *list;
35   WOAssociation *item;
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?
43 }
44
45 @end
46
47 @class WOResponse, WOContext;
48
49 @interface WOPopUpButton(PrivateMethods)
50 - (void)appendOptionsToResponse:(WOResponse *)_response
51   inContext:(WOContext *)_ctx;
52 @end
53
54 #include "decommon.h"
55
56 #ifdef DEBUG
57 static int profElements  = -1;
58 static Class NSDateClass = Nil;
59
60 @interface WOContext(ComponentStackCount)
61 - (unsigned)componentStackCount;
62 @end
63
64 #endif
65
66 @implementation WOPopUpButton
67
68 static NSNumber *yesNum = nil;
69
70 + (int)version {
71   return [super version] + 0 /* v2 */;
72 }
73
74 + (void)initialize {
75   NSAssert2([super version] == 2,
76             @"invalid superclass (%@) version %i !",
77             NSStringFromClass([self superclass]), [super version]);
78   
79   if (yesNum == nil) yesNum = [[NSNumber numberWithBool:YES] retain];
80 }
81
82 - (void)_handleDeprecatedBindings:(NSDictionary *)_config {
83   id tmp;
84   
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!"];
90       }
91       else {
92           [self errorWithFormat:
93             @"'singleSelection' binding is set to NO, which is "
94             @"unsupported now!"];
95       }
96     }
97     else {
98       [self errorWithFormat:
99               @"will ignore deprecated 'singleSelection' binding: %@", tmp];
100     }
101     [tmp release];
102   }
103 }
104
105 - (id)initWithName:(NSString *)_name
106   associations:(NSDictionary *)_config
107   template:(WOElement *)_t
108 {
109 #if DEBUG
110   if (profElements == -1) {
111     NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
112     profElements = [[ud objectForKey:@"WOProfileElements"] boolValue] ? 1 : 0;
113   }
114   if (NSDateClass == Nil)
115     NSDateClass = [NSDate class];
116 #endif
117   
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");
125     
126     self->noSelectionString = OWGetProperty(_config, @"noSelectionString");
127     self->selectedValue     = OWGetProperty(_config, @"selectedValue");
128     self->escapeHTML        = OWGetProperty(_config, @"escapeHTML");
129     self->itemGroup         = OWGetProperty(_config, @"itemGroup");
130
131     self->template = [_t retain];
132     
133     if (self->selection != nil && self->selectedValue != nil)
134       [self logWithFormat:
135         @"cannot have both 'selection' and 'selectedValue' bindings!"];
136     
137     /* compatibility */
138     
139     if (self->noSelectionString == nil)
140       self->noSelectionString = OWGetProperty(_config, @"nilString");
141     
142     if (self->escapeHTML == nil)
143       self->escapeHTML = [[WOAssociation associationWithValue:yesNum] retain];
144
145     [self _handleDeprecatedBindings:_config];
146   }
147   return self;
148 }
149
150 - (void)dealloc {
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];
159   [super dealloc];
160 }
161
162 /* handling the request */
163
164 - (void)takeValuesFromRequest:(WORequest *)_rq inContext:(WOContext *)_ctx {
165   WOComponent *sComponent;
166   NSString    *formValue;
167   NSArray     *objects;
168   id          object;
169   
170   sComponent = [_ctx component];
171   if ([self->disabled boolValueInComponent:[_ctx component]])
172     return;
173   
174   formValue = [_rq formValueForKey:OWFormElementName(self, _ctx)];
175 #if 0
176   [self logWithFormat:@"%@: value=%@ ..", [self elementID], formValue];
177 #endif
178     
179   if (formValue == nil) {
180     /* nothing changed, or not in submitted form */
181     return;
182   }
183       
184   objects = [self->list valueInComponent:sComponent];
185       
186   object = nil;
187   if (self->value != nil) {
188     /* has a value binding, walk list to find object */
189     unsigned i, toGo;
190
191     for (i = 0, toGo = [objects count]; i < toGo; i++) {
192       NSString *cv;
193           
194       object = [objects objectAtIndex:i];
195       
196       if ([self->item isValueSettable])
197         [self->item setValue:object inComponent:sComponent];
198       
199       cv = [self->value stringValueInComponent:sComponent];
200           
201       if ([cv isEqualToString:formValue])
202         break;
203     }
204   }
205   else if (![formValue isEqualToString:WONoSelectionString]) {
206     /* an index binding */
207     int idx;
208         
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];
213       object = nil;
214     }
215     else 
216       object = [objects objectAtIndex:idx];
217   }
218
219   if ([self->selectedValue isValueSettable])
220       [self->selectedValue setValue:formValue inComponent:sComponent];
221
222   /* process selection */
223       
224   if ([self->selection isValueSettable]) {
225     NSArray *sel;
226         
227     if (object) {
228       sel = [object retain];
229     }
230     else /* nil item selected */
231       sel = nil;
232           
233     [self->selection setValue:sel inComponent:sComponent];
234     [sel release]; sel = nil;
235   }
236   if ([self->item isValueSettable])
237     [self->item setValue:nil inComponent:sComponent]; // Reset 'item'
238 }
239
240 /* generate response */
241
242 - (void)appendOptionsToResponse:(WOResponse *)_response
243   inContext:(WOContext *)_ctx
244 {
245   WOComponent *sComponent = [_ctx component];
246   NSString *nilStr  = nil;
247   NSArray  *array   = nil;
248   id       sel = nil;
249   int      i, toGo;
250   BOOL     escapesHTML;
251   BOOL     byVal;
252   id       previousGroup = nil;
253 #if DEBUG
254   NSTimeInterval st = 0.0;
255     
256   if (profElements)
257     st = [[NSDateClass date] timeIntervalSince1970];
258 #endif
259     
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){
265       byVal = YES;
266       sel = [self->selectedValue valueInComponent:sComponent];
267     }
268     else{
269       byVal = NO;
270       sel = nil;
271     }
272   }
273   else{
274     if (self->selectedValue != nil){
275       byVal = YES;
276       sel = [self->selectedValue valueInComponent:sComponent];
277       NSLog(@"WARNING(%@): "
278             @"using both 'selection' and 'selectedValue' bindings!",
279             self);
280     }
281     else{
282       byVal = NO;
283       sel = [self->selection valueInComponent:sComponent];
284     }
285   }
286   toGo     = [array count];
287   
288 #if DEBUG
289   if (profElements) {
290     NSTimeInterval diff;
291     int j;
292     diff = [[NSDateClass date] timeIntervalSince1970] - st;
293     if (diff > 0.001) {
294       for (j = [_ctx componentStackCount]; j >= 0; j--)
295         printf("  ");
296       printf("PopUpOption[setup] %s: %0.3fs\n",
297              [[_ctx elementID] cString], diff);
298     }
299   }
300 #endif
301   
302   if (nilStr != nil) {
303     if (self->itemGroup != nil) {
304       id  group;
305       
306       if ([self->item isValueSettable])
307         [self->item setValue:nil inComponent:sComponent];
308       group = [self->itemGroup stringValueInComponent:sComponent];
309       
310       if (group != nil) {
311         WOResponse_AddCString(_response, "<optgroup label=\"");
312         if (escapesHTML) {
313           WOResponse_AddHtmlString(_response, group);
314         }
315         else {
316           WOResponse_AddString(_response, group);
317         }
318         WOResponse_AddCString(_response, "\">");
319         previousGroup = [group retain];
320       }
321     }
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?
328   }
329   
330   for (i = 0; i < toGo; i++) {
331     NSString *v         = nil;
332     NSString *displayV  = nil;
333     id       object;
334     BOOL     isSelected;
335     id       group;
336 #if DEBUG
337     NSTimeInterval st = 0.0;
338     
339     if (profElements)
340       st = [[NSDateClass date] timeIntervalSince1970];
341 #endif
342     
343     object = [array objectAtIndex:i];
344     
345     if ([self->item isValueSettable])
346       [self->item setValue:object inComponent:sComponent];
347     
348     isSelected = sel ? [sel isEqual:object] : NO;
349     v = self->value
350       ? [self->value stringValueInComponent:sComponent]
351       : [NSString stringWithFormat:@"%i", i];
352
353     if (byVal){
354         isSelected = sel ? [sel isEqual:v] : NO;
355     }
356     else
357       isSelected = sel ? [sel isEqual:object] : NO;
358     
359     displayV = self->string
360       ? [self->string stringValueInComponent:sComponent]
361       : [object stringValue];
362
363     if (displayV == nil) displayV = (escapesHTML ? @"<nil>" : @"&lt;nil&gt;");
364     
365     group = self->itemGroup != nil
366       ? [self->itemGroup stringValueInComponent:sComponent]
367       : nil;
368     
369     if (group != nil) {
370       BOOL  groupChanged = NO;
371       
372       if (previousGroup == nil)
373         groupChanged = YES;
374       else {
375         if (![group isEqualToString:previousGroup]) {
376           WOResponse_AddCString(_response, "</optgroup>");
377           groupChanged = YES;
378         }
379       }
380       if (groupChanged) {
381         WOResponse_AddCString(_response, "<optgroup label=\"");
382         if (escapesHTML) {
383           WOResponse_AddHtmlString(_response, group);
384         }
385         else {
386           WOResponse_AddString(_response, group);
387         }
388         WOResponse_AddCString(_response, "\">");
389         ASSIGN(previousGroup, group);
390       }
391     }
392     else {
393       if (previousGroup != nil) {
394         WOResponse_AddCString(_response, "</optgroup>");
395         ASSIGN(previousGroup, nil);
396       }
397     }
398     WOResponse_AddCString(_response, "<option value=\"");
399     WOResponse_AddHtmlString(_response, v); // WO escapes it, always
400     
401     if (isSelected) {
402       WOResponse_AddString(_response,
403                            _ctx->wcFlags.allowEmptyAttributes 
404                            ? @"\" selected>" : @"\" selected=\"selected\">");
405     }
406     else {
407       WOResponse_AddString(_response, @"\">");
408     }
409     
410     if (escapesHTML){
411       WOResponse_AddHtmlString(_response, displayV);
412     }
413     else{
414       WOResponse_AddString(_response, displayV);
415     }
416     WOResponse_AddCString(_response, "</option>");
417     
418 #if DEBUG
419     if (profElements) {
420       NSTimeInterval diff;
421       int j;
422       diff = [[NSDateClass date] timeIntervalSince1970] - st;
423       if (diff > 0.001) {
424 #if 1
425         for (j = [_ctx componentStackCount]; j >= 0; j--)
426           printf("  ");
427 #endif
428         printf("PopUpOption[%i] %s: %0.3fs\n", i,
429                [[_ctx elementID] cString], diff);
430       }
431     }
432 #endif
433   }
434   if (previousGroup != nil) {
435     WOResponse_AddCString(_response, "</optgroup>");
436     [previousGroup release];
437   }
438   if ([self->item isValueSettable])
439     [self->item setValue:nil inComponent:sComponent]; // Reset 'item'
440 }
441
442 - (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
443 #if DEBUG
444   NSTimeInterval st = 0.0;
445
446   if (profElements)
447     st = [[NSDateClass date] timeIntervalSince1970];
448 #endif
449   
450   WOResponse_AddCString(_response, "<select name=\"");
451   [_response appendContentHTMLAttributeValue:OWFormElementName(self, _ctx)];
452   WOResponse_AddChar(_response, '"');
453   
454   [self appendExtraAttributesToResponse:_response inContext:_ctx];
455   
456   if (self->otherTagString != nil) {
457     WOResponse_AddChar(_response, ' ');
458     WOResponse_AddString(_response,
459                          [self->otherTagString stringValueInComponent:
460                               [_ctx component]]);
461   }
462   
463   if ([self->disabled boolValueInComponent:[_ctx component]])
464     WOResponse_AddCString(_response, " disabled=\"disabled\"");
465
466   WOResponse_AddChar(_response, '>');
467   
468   [self appendOptionsToResponse:_response inContext:_ctx];
469   [self->template appendToResponse:_response inContext:_ctx];
470
471   WOResponse_AddCString(_response, "</select>");
472   
473 #if DEBUG
474   if (profElements) {
475     NSTimeInterval diff;
476     int i;
477     diff = [[NSDateClass date] timeIntervalSince1970] - st;
478     if (diff > 0.001) {
479 #if 1
480       for (i = [_ctx componentStackCount]; i >= 0; i--)
481         printf("  ");
482 #endif
483       printf("PopUpButton %s: %0.3fs\n",
484              [[_ctx elementID] cString], diff);
485     }
486   }
487 #endif
488 }
489
490 /* description */
491
492 - (NSString *)associationDescription {
493   NSMutableString *str;
494   
495   str = [NSMutableString stringWithCapacity:256];
496   [str appendString:[super associationDescription]];
497
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];
508   if (self->itemGroup)
509     [str appendFormat:@" itemGroup=%@", self->itemGroup];
510   
511   return str;
512 }
513
514 @end /* WOPopUpButton */