1/**
2 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include "config.h"
22
23#if ENABLE(WML)
24#include "WMLSelectElement.h"
25
26#include "Attribute.h"
27#include "HTMLNames.h"
28#include "OptionElement.h"
29#include "RenderListBox.h"
30#include "RenderMenuList.h"
31#include "WMLDocument.h"
32#include "WMLNames.h"
33#include "WMLVariables.h"
34#include <wtf/StdLibExtras.h>
35#include <wtf/text/CString.h>
36
37namespace WebCore {
38
39using namespace WMLNames;
40
41WMLSelectElement::WMLSelectElement(const QualifiedName& tagName, Document* document)
42    : WMLFormControlElement(tagName, document)
43    , m_initialized(false)
44{
45}
46
47PassRefPtr<WMLSelectElement> WMLSelectElement::create(const QualifiedName& tagName, Document* document)
48{
49    return adoptRef(new WMLSelectElement(tagName, document));
50}
51
52WMLSelectElement::~WMLSelectElement()
53{
54}
55
56const AtomicString& WMLSelectElement::formControlName() const
57{
58    AtomicString name = this->name();
59    return name.isNull() ? emptyAtom : name;
60}
61
62const AtomicString& WMLSelectElement::formControlType() const
63{
64    DEFINE_STATIC_LOCAL(const AtomicString, selectMultiple, ("select-multiple"));
65    DEFINE_STATIC_LOCAL(const AtomicString, selectOne, ("select-one"));
66    return m_data.multiple() ? selectMultiple : selectOne;
67}
68
69bool WMLSelectElement::isKeyboardFocusable(KeyboardEvent* event) const
70{
71    if (renderer())
72        return isFocusable();
73
74    return WMLFormControlElement::isKeyboardFocusable(event);
75}
76
77bool WMLSelectElement::isMouseFocusable() const
78{
79    if (renderer())
80        return isFocusable();
81
82    return WMLFormControlElement::isMouseFocusable();
83}
84
85void WMLSelectElement::selectAll()
86{
87    SelectElement::selectAll(m_data, this);
88}
89
90void WMLSelectElement::recalcStyle(StyleChange change)
91{
92    WMLFormControlElement::recalcStyle(change);
93}
94
95void WMLSelectElement::dispatchFocusEvent()
96{
97    SelectElement::dispatchFocusEvent(m_data, this);
98    WMLFormControlElement::dispatchFocusEvent();
99}
100
101void WMLSelectElement::dispatchBlurEvent()
102{
103    SelectElement::dispatchBlurEvent(m_data, this);
104    WMLFormControlElement::dispatchBlurEvent();
105}
106
107int WMLSelectElement::selectedIndex() const
108{
109    return SelectElement::selectedIndex(m_data, this);
110}
111
112void WMLSelectElement::setSelectedIndex(int optionIndex, bool deselect)
113{
114    SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, false, false);
115}
116
117void WMLSelectElement::setSelectedIndexByUser(int optionIndex, bool deselect, bool fireOnChangeNow, bool allowMultipleSelection)
118{
119    UNUSED_PARAM(allowMultipleSelection);
120    SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, fireOnChangeNow, true);
121}
122
123bool WMLSelectElement::saveFormControlState(String& value) const
124{
125    return SelectElement::saveFormControlState(m_data, this, value);
126}
127
128void WMLSelectElement::restoreFormControlState(const String& state)
129{
130    SelectElement::restoreFormControlState(m_data, this, state);
131}
132
133void WMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
134{
135    SelectElement::setRecalcListItems(m_data, this);
136    WMLFormControlElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
137}
138
139void WMLSelectElement::parseMappedAttribute(Attribute* attr)
140{
141    if (attr->name() == HTMLNames::multipleAttr)
142        SelectElement::parseMultipleAttribute(m_data, this, attr);
143    else
144        WMLFormControlElement::parseMappedAttribute(attr);
145}
146
147RenderObject* WMLSelectElement::createRenderer(RenderArena* arena, RenderStyle*)
148{
149    if (m_data.usesMenuList())
150        return new (arena) RenderMenuList(this);
151    return new (arena) RenderListBox(this);
152}
153
154bool WMLSelectElement::appendFormData(FormDataList& list, bool)
155{
156    return SelectElement::appendFormData(m_data, this, list);
157}
158
159int WMLSelectElement::optionToListIndex(int optionIndex) const
160{
161    return SelectElement::optionToListIndex(m_data, this, optionIndex);
162}
163
164int WMLSelectElement::listToOptionIndex(int listIndex) const
165{
166    return SelectElement::listToOptionIndex(m_data, this, listIndex);
167}
168
169void WMLSelectElement::reset()
170{
171    SelectElement::reset(m_data, this);
172}
173
174void WMLSelectElement::defaultEventHandler(Event* event)
175{
176    SelectElement::defaultEventHandler(m_data, this, event, 0);
177
178    // FIXME: There must be a better place to update the page variable state. Investigate.
179    updateVariables();
180
181    if (event->defaultHandled())
182        return;
183
184    WMLFormControlElement::defaultEventHandler(event);
185}
186
187void WMLSelectElement::accessKeyAction(bool sendToAnyElement)
188{
189    focus();
190    dispatchSimulatedClick(0, sendToAnyElement);
191}
192
193void WMLSelectElement::setActiveSelectionAnchorIndex(int index)
194{
195    SelectElement::setActiveSelectionAnchorIndex(m_data, this, index);
196}
197
198void WMLSelectElement::setActiveSelectionEndIndex(int index)
199{
200    SelectElement::setActiveSelectionEndIndex(m_data, index);
201}
202
203void WMLSelectElement::updateListBoxSelection(bool deselectOtherOptions)
204{
205    SelectElement::updateListBoxSelection(m_data, this, deselectOtherOptions);
206}
207
208void WMLSelectElement::listBoxOnChange()
209{
210    SelectElement::listBoxOnChange(m_data, this);
211}
212
213void WMLSelectElement::menuListOnChange()
214{
215    SelectElement::menuListOnChange(m_data, this);
216}
217
218int WMLSelectElement::activeSelectionStartListIndex() const
219{
220    if (m_data.activeSelectionAnchorIndex() >= 0)
221        return m_data.activeSelectionAnchorIndex();
222    return optionToListIndex(selectedIndex());
223}
224
225int WMLSelectElement::activeSelectionEndListIndex() const
226{
227    if (m_data.activeSelectionEndIndex() >= 0)
228        return m_data.activeSelectionEndIndex();
229    return SelectElement::lastSelectedListIndex(m_data, this);
230}
231
232void WMLSelectElement::accessKeySetSelectedIndex(int index)
233{
234    SelectElement::accessKeySetSelectedIndex(m_data, this, index);
235}
236
237void WMLSelectElement::setRecalcListItems()
238{
239    SelectElement::setRecalcListItems(m_data, this);
240}
241
242void WMLSelectElement::scrollToSelection()
243{
244    SelectElement::scrollToSelection(m_data, this);
245}
246
247void WMLSelectElement::selectInitialOptions()
248{
249    // Spec: Step 1 - the default option index is determined using iname and ivalue
250    calculateDefaultOptionIndices();
251
252    if (m_defaultOptionIndices.isEmpty()) {
253        m_initialized = true;
254        return;
255    }
256
257    // Spec: Step 2 â initialise variables
258    initializeVariables();
259
260    // Spec: Step 3 â pre-select option(s) specified by the default option index
261    selectDefaultOptions();
262    m_initialized = true;
263}
264
265void WMLSelectElement::insertedIntoTree(bool deep)
266{
267    SelectElement::insertedIntoTree(m_data, this);
268    WMLFormControlElement::insertedIntoTree(deep);
269}
270
271void WMLSelectElement::calculateDefaultOptionIndices()
272{
273    WMLPageState* pageState = wmlPageStateForDocument(document());
274    if (!pageState)
275        return;
276
277    String variable;
278
279    // Spec: If the 'iname' attribute is specified and names a variable that is set,
280    // then the default option index is the validated value of that variable.
281    String iname = this->iname();
282    if (!iname.isEmpty()) {
283        variable = pageState->getVariable(iname);
284        if (!variable.isEmpty())
285            m_defaultOptionIndices = parseIndexValueString(variable);
286    }
287
288    // Spec: If the default option index is empty and the 'ivalue' attribute is specified,
289    // then the default option index is the validated attribute value.
290    String ivalue = this->ivalue();
291    if (m_defaultOptionIndices.isEmpty() && !ivalue.isEmpty())
292        m_defaultOptionIndices = parseIndexValueString(ivalue);
293
294    // Spec: If the default option index is empty, and the 'name' attribute is specified
295    // and the 'name' ttribute names a variable that is set, then for each value in the 'name'
296    // variable that is present as a value in the select's option elements, the index of the
297    // first option element containing that value is added to the default index if that
298    // index has not been previously added.
299    String name = this->name();
300    if (m_defaultOptionIndices.isEmpty() && !name.isEmpty()) {
301        variable = pageState->getVariable(name);
302        if (!variable.isEmpty())
303            m_defaultOptionIndices = valueStringToOptionIndices(variable);
304    }
305
306    String value = parseValueSubstitutingVariableReferences(getAttribute(HTMLNames::valueAttr));
307
308    // Spec: If the default option index is empty and the 'value' attribute is specified then
309    // for each  value in the 'value' attribute that is present as a value in the select's
310    // option elements, the index of the first option element containing that value is added
311    // to the default index if that index has not been previously added.
312    if (m_defaultOptionIndices.isEmpty() && !value.isEmpty())
313        m_defaultOptionIndices = valueStringToOptionIndices(value);
314
315    // Spec: If the default option index is empty and the select is a multi-choice, then the
316    // default option index is set to zero. If the select is single-choice, then the default
317    // option index is set to one.
318    if (m_defaultOptionIndices.isEmpty())
319        m_defaultOptionIndices.append((unsigned) !m_data.multiple());
320}
321
322void WMLSelectElement::selectDefaultOptions()
323{
324    ASSERT(!m_defaultOptionIndices.isEmpty());
325
326    if (!m_data.multiple()) {
327        setSelectedIndex(m_defaultOptionIndices.first() - 1, false);
328        return;
329    }
330
331    Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end();
332    for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it)
333        setSelectedIndex((*it) - 1, false);
334}
335
336void WMLSelectElement::initializeVariables()
337{
338    ASSERT(!m_defaultOptionIndices.isEmpty());
339
340    WMLPageState* pageState = wmlPageStateForDocument(document());
341    if (!pageState)
342        return;
343
344    const Vector<Element*>& items = m_data.listItems(this);
345    if (items.isEmpty())
346        return;
347
348    // Spec: If the 'iname' attribute is specified, then the named variable is set with the default option index.
349    String iname = this->iname();
350    if (!iname.isEmpty())
351        pageState->storeVariable(iname, optionIndicesToString());
352
353    String name = this->name();
354    if (name.isEmpty())
355        return;
356
357    if (m_data.multiple()) {
358        // Spec: If the 'name' attribute is specified and the select is a multiple-choice element,
359        // then for each index greater than zero, the value of the 'value' attribute on the option
360        // element at the index is added to the name variable.
361        pageState->storeVariable(name, optionIndicesToValueString());
362        return;
363    }
364
365    // Spec: If the 'name' attribute is specified and the select is a single-choice element,
366    // then the named variable is set with the value of the 'value' attribute on the option
367    // element at the default option index.
368    unsigned optionIndex = m_defaultOptionIndices.first();
369    ASSERT(optionIndex >= 1);
370
371    int listIndex = optionToListIndex(optionIndex - 1);
372    ASSERT(listIndex >= 0);
373    ASSERT(listIndex < (int) items.size());
374
375    if (OptionElement* optionElement = toOptionElement(items[listIndex]))
376        pageState->storeVariable(name, optionElement->value());
377}
378
379void WMLSelectElement::updateVariables()
380{
381    WMLPageState* pageState = wmlPageStateForDocument(document());
382    if (!pageState)
383        return;
384
385    String name = this->name();
386    String iname = this->iname();
387    if (iname.isEmpty() && name.isEmpty())
388        return;
389
390    String nameString;
391    String inameString;
392
393    unsigned optionIndex = 0;
394    const Vector<Element*>& items = m_data.listItems(this);
395
396    for (unsigned i = 0; i < items.size(); ++i) {
397        OptionElement* optionElement = toOptionElement(items[i]);
398        if (!optionElement)
399            continue;
400
401        ++optionIndex;
402        if (!optionElement->selected())
403            continue;
404
405        if (!nameString.isEmpty())
406            nameString += ";";
407
408        if (!inameString.isEmpty())
409            inameString += ";";
410
411        nameString += optionElement->value();
412        inameString += String::number(optionIndex);
413    }
414
415    if (!name.isEmpty())
416        pageState->storeVariable(name, nameString);
417
418    if (!iname.isEmpty())
419        pageState->storeVariable(iname, inameString);
420}
421
422Vector<unsigned> WMLSelectElement::parseIndexValueString(const String& indexValue) const
423{
424    Vector<unsigned> indices;
425    if (indexValue.isEmpty())
426        return indices;
427
428    Vector<String> indexStrings;
429    indexValue.split(';', indexStrings);
430
431    bool ok = false;
432    unsigned optionCount = SelectElement::optionCount(m_data, this);
433
434    Vector<String>::const_iterator end = indexStrings.end();
435    for (Vector<String>::const_iterator it = indexStrings.begin(); it != end; ++it) {
436        unsigned parsedValue = (*it).toUIntStrict(&ok);
437        // Spec: Remove all non-integer indices from the value. Remove all out-of-range indices
438        // from the value, where out-of-range is defined as any index with a value greater than
439        // the number of options in the select or with a value less than one.
440        if (!ok || parsedValue < 1 || parsedValue > optionCount)
441            continue;
442
443        // Spec: Remove duplicate indices.
444        if (indices.find(parsedValue) == notFound)
445            indices.append(parsedValue);
446    }
447
448    return indices;
449}
450
451Vector<unsigned> WMLSelectElement::valueStringToOptionIndices(const String& value) const
452{
453    Vector<unsigned> indices;
454    if (value.isEmpty())
455        return indices;
456
457    const Vector<Element*>& items = m_data.listItems(this);
458    if (items.isEmpty())
459        return indices;
460
461    Vector<String> indexStrings;
462    value.split(';', indexStrings);
463
464    unsigned optionIndex = 0;
465
466    Vector<String>::const_iterator end = indexStrings.end();
467    for (Vector<String>::const_iterator it = indexStrings.begin(); it != end; ++it) {
468        String value = *it;
469
470        for (unsigned i = 0; i < items.size(); ++i) {
471            if (!isOptionElement(items[i]))
472                continue;
473
474            ++optionIndex;
475            if (OptionElement* optionElement = toOptionElement(items[i])) {
476                if (optionElement->value() == value) {
477                    indices.append(optionIndex);
478                    break;
479                }
480            }
481        }
482    }
483
484    return indices;
485}
486
487String WMLSelectElement::optionIndicesToValueString() const
488{
489    String valueString;
490    if (m_defaultOptionIndices.isEmpty())
491        return valueString;
492
493    const Vector<Element*>& items = m_data.listItems(this);
494    if (items.isEmpty())
495        return valueString;
496
497    Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end();
498    for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it) {
499        unsigned optionIndex = (*it);
500        if (optionIndex < 1 || optionIndex > items.size())
501            continue;
502
503        int listIndex = optionToListIndex((*it) - 1);
504        ASSERT(listIndex >= 0);
505        ASSERT(listIndex < (int) items.size());
506
507        if (OptionElement* optionElement = toOptionElement(items[listIndex])) {
508            if (!valueString.isEmpty())
509                valueString += ";";
510
511            valueString += optionElement->value();
512        }
513    }
514
515    return valueString;
516}
517
518String WMLSelectElement::optionIndicesToString() const
519{
520    String valueString;
521    if (m_defaultOptionIndices.isEmpty())
522        return valueString;
523
524    Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end();
525    for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it) {
526        if (!valueString.isEmpty())
527            valueString += ";";
528
529        valueString += String::number(*it);
530    }
531
532    return valueString;
533}
534
535String WMLSelectElement::name() const
536{
537    return parseValueForbiddingVariableReferences(getAttribute(HTMLNames::nameAttr));
538}
539
540String WMLSelectElement::value() const
541{
542    return parseValueSubstitutingVariableReferences(getAttribute(HTMLNames::valueAttr));
543}
544
545String WMLSelectElement::iname() const
546{
547    return parseValueForbiddingVariableReferences(getAttribute(inameAttr));
548}
549
550String WMLSelectElement::ivalue() const
551{
552    return parseValueSubstitutingVariableReferences(getAttribute(ivalueAttr));
553}
554
555void WMLSelectElement::listBoxSelectItem(int, bool, bool, bool)
556{
557    /* Dummy implementation as listBoxSelectItem is pure virtual in SelectElement class */
558}
559
560}
561
562#endif
563