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#include "decommon.h"
24
25@interface WOBrowser : WOInput
26{
27  // WODynamicElement: extraAttributes
28  // WODynamicElement: otherTagString
29  // inherited: name
30  // inherited: value
31  // inherited: disabled
32@protected
33  WOAssociation *list;
34  WOAssociation *item;
35  WOAssociation *selection;         // => use 'selections'!
36  WOAssociation *string;            // WO4 => use 'displayString'!
37  WOAssociation *noSelectionString; // WO4
38
39  // non-WO:
40  WOAssociation *singleSelection; // selection contains an item, not an array
41
42  // WO 4.5
43  WOAssociation *multiple; // multiple selections allowed
44  WOAssociation *size;
45
46  // TODO: WO 4.5: selectedValues, escapeHTML
47}
48
49@end /* WOBrowser */
50
51@implementation WOBrowser
52
53- (id)initWithName:(NSString *)_name
54  associations:(NSDictionary *)_config
55  template:(WOElement *)_c
56{
57  if ((self = [super initWithName:_name associations:_config template:_c])) {
58    self->list              = OWGetProperty(_config, @"list");
59    self->item              = OWGetProperty(_config, @"item");
60    self->singleSelection   = OWGetProperty(_config, @"singleSelection");
61    self->multiple          = OWGetProperty(_config, @"multiple");
62    self->size              = OWGetProperty(_config, @"size");
63    self->noSelectionString = OWGetProperty(_config, @"noSelectionString");
64
65    if ((self->string = OWGetProperty(_config, @"displayString")) == nil) {
66      if ((self->string = OWGetProperty(_config, @"string")) != nil) {
67	[self debugWithFormat:
68		@"Note: using deprecated 'string' binding, "
69	        @"use 'displayString' instead."];
70      }
71    }
72    else if (OWGetProperty(_config, @"string") != nil) {
73      [self debugWithFormat:@"WARNING: 'displayString' AND 'string' bindings "
74	      @"are set, use only one! ('string' is deprecated!)"];
75    }
76
77    if ((self->selection = OWGetProperty(_config, @"selections")) == nil) {
78      if ((self->selection = OWGetProperty(_config, @"selection")) != nil) {
79	[self debugWithFormat:
80		@"Note: using deprecated 'selection' binding, "
81	        @"use 'selections' instead."];
82      }
83    }
84    else if (OWGetProperty(_config, @"selection") != nil) {
85      [self debugWithFormat:@"WARNING: 'selections' AND 'selection' bindings "
86	      @"are set, use only one! ('selection' is deprecated!)"];
87    }
88
89    // compatiblity
90    if (self->noSelectionString == nil)
91      self->noSelectionString = OWGetProperty(_config, @"nilString");
92
93    if (self->multiple == nil) {
94      self->multiple =
95        [WOAssociation associationWithValue:[NSNumber numberWithBool:YES]];
96      self->multiple = [self->multiple retain];
97    }
98  }
99  return self;
100}
101
102- (void)dealloc {
103  [self->noSelectionString release];
104  [self->singleSelection   release];
105  [self->list      release];
106  [self->item      release];
107  [self->selection release];
108  [self->string    release];
109  [self->size      release];
110  [self->multiple  release];
111  [super dealloc];
112}
113
114/* handling request */
115
116- (void)_takeSingleFormValue:(id)formValue fromRequest:(WORequest *)_request
117  inContext:(WOContext *)_ctx
118{
119  WOComponent *sComponent;
120  NSArray *objects;
121  id      object;
122
123  sComponent = [_ctx component];
124  objects = [self->list valueInComponent:sComponent];
125
126  if ([[formValue stringValue] isEqualToString:@"$"])
127    object = nil; // nil item selected
128  else {
129    int idx;
130
131    object = nil;
132    if ((idx = [formValue intValue]) >= 0) {
133      if (idx < (int)[objects count])
134        object = [objects objectAtIndex:idx];
135      else {
136        [sComponent logWithFormat:
137                      @"WOBrowser got invalid index '%i' (formvalue='%@') "
138                    @"for list with count %i !",
139                    idx, formValue, [objects count]];
140      }
141    }
142    else
143      [sComponent logWithFormat:@"WOBrowser got invalid index '%i' !", idx];
144  }
145
146  if ([self->selection isValueSettable]) {
147    NSArray *sel;
148
149    if ([self->item isValueSettable])
150      [self->item setValue:object inComponent:sComponent];
151
152    if (object) {
153      sel = [self->singleSelection boolValueInComponent:sComponent]
154        ? [object retain]
155        : [[NSArray alloc] initWithObjects:object,nil];
156    }
157    else // nil item selected
158      sel = nil;
159
160    [self->selection setValue:sel inComponent:sComponent];
161    [sel release]; sel = nil;
162  }
163}
164
165- (void)_takeMultiFormValue:(NSArray *)formValue fromRequest:(WORequest *)_rq
166  inContext:(WOContext *)_ctx
167{
168  WOComponent *sComponent;
169  NSEnumerator   *values;
170  NSString       *v;
171  NSArray        *objects;
172  id             object;
173
174  values     = [formValue objectEnumerator];
175  sComponent = [_ctx component];
176  objects    = [self->list valueInComponent:sComponent];
177
178  if ([self->selection isValueSettable]) {
179    NSMutableArray *sel;
180    unsigned objCount;
181
182    sel      = [[NSMutableArray alloc] initWithCapacity:[formValue count]];
183    objCount = [objects count];
184
185    while ((v = [values nextObject])) {
186        int idx;
187
188        object = nil;
189        if ((idx = [v intValue]) >= 0) {
190          if (idx < (int)objCount)
191            object = [objects objectAtIndex:idx];
192          else {
193            [sComponent logWithFormat:
194                          @"WOBrowser got invalid index '%i'(formValue='%@' "
195                          @"for list with count %i !",
196                          idx, objCount, v];
197          }
198
199          if ([self->item isValueSettable])
200            [self->item setValue:object inComponent:sComponent];
201        }
202        else {
203          [sComponent logWithFormat:@"WOBrowser got invalid index '%i' !",
204                        idx];
205        }
206
207        if (object) [sel addObject:object];
208    }
209
210    if ([self->singleSelection boolValueInComponent:sComponent]) {
211#if 0
212        if ([sel count] > 1) {
213          NSLog(@"WARNING(%@): "
214                @"using singleSelection with multiple selected values",
215                self);
216        }
217#endif
218        [self->selection setValue:[sel lastObject] inComponent:sComponent];
219    }
220    else
221      [self->selection setValue:sel inComponent:sComponent];
222    [sel release]; sel = nil;
223  }
224}
225
226- (void)takeValuesFromRequest:(WORequest *)_request
227  inContext:(WOContext *)_ctx
228{
229  WOComponent *sComponent;
230  id formValue = nil;
231
232  sComponent = [_ctx component];
233  if ([self->disabled boolValueInComponent:sComponent])
234    return;
235
236  formValue = [_request formValuesForKey:OWFormElementName(self, _ctx)];
237#if 0
238  [self logWithFormat:@"value=%@ ..", formValue];
239#endif
240
241  if ([self->value isValueSettable])
242    // TODO: is this correct?
243    [self->value setValue:formValue inComponent:sComponent];
244
245  if ([formValue count] == 1) {
246    [self _takeSingleFormValue:[formValue lastObject] fromRequest:_request
247          inContext:_ctx];
248  }
249  else if (formValue != nil) {
250    [self _takeMultiFormValue:formValue fromRequest:_request
251          inContext:_ctx];
252  }
253  else {
254    // nothing selected
255    if ([self->item isValueSettable])
256      [self->item setValue:nil inComponent:sComponent];
257    if ([self->selection isValueSettable])
258      [self->selection setValue:nil inComponent:sComponent];
259  }
260}
261
262/* generate response */
263
264- (void)appendOptionsToResponse:(WOResponse *)_response
265  inContext:(WOContext *)_ctx
266{
267  WOComponent *sComponent;
268  BOOL     isSingle;
269  NSString *nilStr;
270  NSArray  *array;
271  id       selArray;
272  int      i, toGo;
273
274  sComponent = [_ctx component];
275
276  nilStr   = [self->noSelectionString stringValueInComponent:sComponent];
277  isSingle = [self->singleSelection boolValueInComponent:sComponent];
278  array    = [self->list            valueInComponent:sComponent];
279  selArray = [self->selection       valueInComponent:sComponent];
280  toGo     = [array count];
281
282  if (nilStr != nil) {
283    WOResponse_AddCString(_response, "<option value=\"$\">");
284    WOResponse_AddHtmlString(_response, nilStr);
285    WOResponse_AddCString(_response, "</option>");
286  }
287
288  for (i = 0; i < toGo; i++) {
289    NSString *v, *displayV;
290    id       object;
291    BOOL     isSelected;
292
293    object = [array objectAtIndex:i];
294
295    if ([self->item isValueSettable])
296      [self->item setValue:object inComponent:sComponent];
297
298    isSelected = NO;
299    if (selArray != nil) {
300      isSelected = isSingle
301        ? [selArray isEqual:object] : [selArray containsObject:object];
302    }
303
304    v = (self->value != nil)
305      ? [self->value stringValueInComponent:sComponent]
306      : (NSString *)[NSString stringWithFormat:@"%i", i]; // TODO: slow
307
308    displayV = self->string
309      ? [self->string stringValueInComponent:sComponent]
310      : [object stringValue];
311
312    if (displayV == nil) displayV = @"";
313
314    WOResponse_AddCString(_response, "<option value=\"");
315    WOResponse_AddString(_response, v);
316    if (isSelected) {
317      WOResponse_AddString(_response, _ctx->wcFlags.allowEmptyAttributes
318			   ? @"\" selected>" : @"\" selected=\"selected\">");
319    }
320    else {
321      WOResponse_AddString(_response, @"\">");
322    }
323    WOResponse_AddHtmlString(_response, displayV);
324    WOResponse_AddCString(_response, "</option>");
325  }
326}
327
328- (void)appendToResponse:(WOResponse *)_response inContext:(WOContext *)_ctx {
329  BOOL     isMultiple;
330  unsigned s;
331
332  if ([[_ctx request] isFromClientComponent])
333    return;
334
335  isMultiple = [self->multiple boolValueInComponent:[_ctx component]];
336  s          = [self->size unsignedIntValueInComponent:[_ctx component]];
337
338  WOResponse_AddCString(_response, "<select name=\"");
339  [_response appendContentHTMLAttributeValue:OWFormElementName(self, _ctx)];
340  if (self->otherTagString) {
341    WOResponse_AddChar(_response, ' ');
342    WOResponse_AddString(_response,
343                         [self->otherTagString stringValueInComponent:
344                           [_ctx component]]);
345  }
346  WOResponse_AddCString(_response, "\"");
347
348  if (s > 0) {
349    WOResponse_AddCString(_response, " size=\"");
350    WOResponse_AddUInt(_response, s);
351    [_response appendContentCharacter:'"'];
352  }
353
354  if ([self->disabled boolValueInComponent:[_ctx component]])
355    WOResponse_AddCString(_response, " disabled=\"disabled\"");
356
357  if (isMultiple)
358    WOResponse_AddCString(_response, " multiple=\"multiple\"");
359
360  [self appendExtraAttributesToResponse:_response inContext:_ctx];
361  WOResponse_AddCString(_response, ">\n");
362
363  [self appendOptionsToResponse:_response inContext:_ctx];
364
365  WOResponse_AddCString(_response, "</select>");
366}
367
368/* description */
369
370- (NSString *)associationDescription {
371  NSMutableString *str;
372
373  str = [NSMutableString stringWithCapacity:256];
374  [str appendString:[super associationDescription]];
375
376  if (self->list)      [str appendFormat:@" list=%@",      self->list];
377  if (self->item)      [str appendFormat:@" item=%@",      self->item];
378  if (self->selection) [str appendFormat:@" selection=%@", self->selection];
379  if (self->string)    [str appendFormat:@" string=%@",    self->string];
380  if (self->noSelectionString)
381    [str appendFormat:@" noselection=%@", self->noSelectionString];
382  if (self->singleSelection)
383    [str appendFormat:@" singleSelection=%@", self->singleSelection];
384
385  if (self->size)     [str appendFormat:@" size=%@",     self->size];
386  if (self->multiple) [str appendFormat:@" multiple=%@", self->multiple];
387
388  return str;
389}
390
391@end /* WOBrowser */
392