| /** |
| * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "config.h" |
| |
| #if ENABLE(WML) |
| #include "WMLSelectElement.h" |
| #include "CString.h" |
| #include "HTMLNames.h" |
| #include "MappedAttribute.h" |
| #include "OptionElement.h" |
| #include "RenderListBox.h" |
| #include "RenderMenuList.h" |
| #include "WMLDocument.h" |
| #include "WMLNames.h" |
| #include "WMLVariables.h" |
| #include <wtf/StdLibExtras.h> |
| |
| namespace WebCore { |
| |
| using namespace WMLNames; |
| |
| WMLSelectElement::WMLSelectElement(const QualifiedName& tagName, Document* document) |
| : WMLFormControlElement(tagName, document) |
| { |
| } |
| |
| WMLSelectElement::~WMLSelectElement() |
| { |
| } |
| |
| String WMLSelectElement::title() const |
| { |
| return substituteVariableReferences(getAttribute(HTMLNames::titleAttr), document()); |
| } |
| |
| const AtomicString& WMLSelectElement::formControlName() const |
| { |
| AtomicString name = this->name(); |
| return name.isNull() ? emptyAtom : name; |
| } |
| |
| const AtomicString& WMLSelectElement::formControlType() const |
| { |
| DEFINE_STATIC_LOCAL(const AtomicString, selectMultiple, ("select-multiple")); |
| DEFINE_STATIC_LOCAL(const AtomicString, selectOne, ("select-one")); |
| return m_data.multiple() ? selectMultiple : selectOne; |
| } |
| |
| bool WMLSelectElement::isKeyboardFocusable(KeyboardEvent* event) const |
| { |
| if (renderer()) |
| return isFocusable(); |
| |
| return WMLFormControlElement::isKeyboardFocusable(event); |
| } |
| |
| bool WMLSelectElement::isMouseFocusable() const |
| { |
| if (renderer()) |
| return isFocusable(); |
| |
| return WMLFormControlElement::isMouseFocusable(); |
| } |
| |
| void WMLSelectElement::selectAll() |
| { |
| SelectElement::selectAll(m_data, this); |
| } |
| |
| void WMLSelectElement::recalcStyle(StyleChange change) |
| { |
| SelectElement::recalcStyle(m_data, this); |
| WMLFormControlElement::recalcStyle(change); |
| } |
| |
| void WMLSelectElement::dispatchFocusEvent() |
| { |
| SelectElement::dispatchFocusEvent(m_data, this); |
| WMLFormControlElement::dispatchFocusEvent(); |
| } |
| |
| void WMLSelectElement::dispatchBlurEvent() |
| { |
| SelectElement::dispatchBlurEvent(m_data, this); |
| WMLFormControlElement::dispatchBlurEvent(); |
| } |
| |
| int WMLSelectElement::selectedIndex() const |
| { |
| return SelectElement::selectedIndex(m_data, this); |
| } |
| |
| void WMLSelectElement::setSelectedIndex(int optionIndex, bool deselect) |
| { |
| SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, false, false); |
| } |
| |
| void WMLSelectElement::setSelectedIndexByUser(int optionIndex, bool deselect, bool fireOnChangeNow) |
| { |
| SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, fireOnChangeNow, true); |
| } |
| |
| bool WMLSelectElement::saveFormControlState(String& value) const |
| { |
| return SelectElement::saveFormControlState(m_data, this, value); |
| } |
| |
| void WMLSelectElement::restoreFormControlState(const String& state) |
| { |
| SelectElement::restoreFormControlState(m_data, this, state); |
| } |
| |
| void WMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) |
| { |
| SelectElement::setRecalcListItems(m_data, this); |
| WMLFormControlElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); |
| } |
| |
| void WMLSelectElement::parseMappedAttribute(MappedAttribute* attr) |
| { |
| if (attr->name() == HTMLNames::multipleAttr) |
| SelectElement::parseMultipleAttribute(m_data, this, attr); |
| else |
| WMLFormControlElement::parseMappedAttribute(attr); |
| } |
| |
| RenderObject* WMLSelectElement::createRenderer(RenderArena* arena, RenderStyle*) |
| { |
| if (m_data.usesMenuList()) |
| return new (arena) RenderMenuList(this); |
| return new (arena) RenderListBox(this); |
| } |
| |
| bool WMLSelectElement::appendFormData(FormDataList& list, bool) |
| { |
| return SelectElement::appendFormData(m_data, this, list); |
| } |
| |
| int WMLSelectElement::optionToListIndex(int optionIndex) const |
| { |
| return SelectElement::optionToListIndex(m_data, this, optionIndex); |
| } |
| |
| int WMLSelectElement::listToOptionIndex(int listIndex) const |
| { |
| return SelectElement::listToOptionIndex(m_data, this, listIndex); |
| } |
| |
| void WMLSelectElement::reset() |
| { |
| SelectElement::reset(m_data, this); |
| } |
| |
| void WMLSelectElement::defaultEventHandler(Event* event) |
| { |
| SelectElement::defaultEventHandler(m_data, this, event); |
| |
| // FIXME: There must be a better place to update the page variable state. Investigate. |
| updateVariables(); |
| |
| if (event->defaultHandled()) |
| return; |
| |
| WMLFormControlElement::defaultEventHandler(event); |
| } |
| |
| void WMLSelectElement::accessKeyAction(bool sendToAnyElement) |
| { |
| focus(); |
| dispatchSimulatedClick(0, sendToAnyElement); |
| } |
| |
| void WMLSelectElement::setActiveSelectionAnchorIndex(int index) |
| { |
| SelectElement::setActiveSelectionAnchorIndex(m_data, this, index); |
| } |
| |
| void WMLSelectElement::setActiveSelectionEndIndex(int index) |
| { |
| SelectElement::setActiveSelectionEndIndex(m_data, index); |
| } |
| |
| void WMLSelectElement::updateListBoxSelection(bool deselectOtherOptions) |
| { |
| SelectElement::updateListBoxSelection(m_data, this, deselectOtherOptions); |
| } |
| |
| void WMLSelectElement::listBoxOnChange() |
| { |
| SelectElement::listBoxOnChange(m_data, this); |
| } |
| |
| void WMLSelectElement::menuListOnChange() |
| { |
| SelectElement::menuListOnChange(m_data, this); |
| } |
| |
| int WMLSelectElement::activeSelectionStartListIndex() const |
| { |
| if (m_data.activeSelectionAnchorIndex() >= 0) |
| return m_data.activeSelectionAnchorIndex(); |
| return optionToListIndex(selectedIndex()); |
| } |
| |
| int WMLSelectElement::activeSelectionEndListIndex() const |
| { |
| if (m_data.activeSelectionEndIndex() >= 0) |
| return m_data.activeSelectionEndIndex(); |
| return SelectElement::lastSelectedListIndex(m_data, this); |
| } |
| |
| void WMLSelectElement::accessKeySetSelectedIndex(int index) |
| { |
| SelectElement::accessKeySetSelectedIndex(m_data, this, index); |
| } |
| |
| void WMLSelectElement::setRecalcListItems() |
| { |
| SelectElement::setRecalcListItems(m_data, this); |
| } |
| |
| void WMLSelectElement::scrollToSelection() |
| { |
| SelectElement::scrollToSelection(m_data, this); |
| } |
| |
| void WMLSelectElement::selectInitialOptions() |
| { |
| // Spec: Step 1 - the default option index is determined using iname and ivalue |
| calculateDefaultOptionIndices(); |
| |
| if (m_defaultOptionIndices.isEmpty()) |
| return; |
| |
| // Spec: Step 2 – initialise variables |
| initializeVariables(); |
| |
| // Spec: Step 3 – pre-select option(s) specified by the default option index |
| selectDefaultOptions(); |
| } |
| |
| void WMLSelectElement::insertedIntoTree(bool deep) |
| { |
| SelectElement::insertedIntoTree(m_data, this); |
| WMLFormControlElement::insertedIntoTree(deep); |
| } |
| |
| void WMLSelectElement::calculateDefaultOptionIndices() |
| { |
| WMLPageState* pageState = wmlPageStateForDocument(document()); |
| if (!pageState) |
| return; |
| |
| String variable; |
| |
| // Spec: If the 'iname' attribute is specified and names a variable that is set, |
| // then the default option index is the validated value of that variable. |
| String iname = this->iname(); |
| if (!iname.isEmpty()) { |
| variable = pageState->getVariable(iname); |
| if (!variable.isEmpty()) |
| m_defaultOptionIndices = parseIndexValueString(variable); |
| } |
| |
| // Spec: If the default option index is empty and the 'ivalue' attribute is specified, |
| // then the default option index is the validated attribute value. |
| String ivalue = this->ivalue(); |
| if (m_defaultOptionIndices.isEmpty() && !ivalue.isEmpty()) |
| m_defaultOptionIndices = parseIndexValueString(ivalue); |
| |
| // Spec: If the default option index is empty, and the 'name' attribute is specified |
| // and the 'name' ttribute names a variable that is set, then for each value in the 'name' |
| // variable that is present as a value in the select's option elements, the index of the |
| // first option element containing that value is added to the default index if that |
| // index has not been previously added. |
| String name = this->name(); |
| if (m_defaultOptionIndices.isEmpty() && !name.isEmpty()) { |
| variable = pageState->getVariable(name); |
| if (!variable.isEmpty()) |
| m_defaultOptionIndices = valueStringToOptionIndices(variable); |
| } |
| |
| String value = parseValueSubstitutingVariableReferences(getAttribute(HTMLNames::valueAttr)); |
| |
| // Spec: If the default option index is empty and the 'value' attribute is specified then |
| // for each value in the 'value' attribute that is present as a value in the select's |
| // option elements, the index of the first option element containing that value is added |
| // to the default index if that index has not been previously added. |
| if (m_defaultOptionIndices.isEmpty() && !value.isEmpty()) |
| m_defaultOptionIndices = valueStringToOptionIndices(value); |
| |
| // Spec: If the default option index is empty and the select is a multi-choice, then the |
| // default option index is set to zero. If the select is single-choice, then the default |
| // option index is set to one. |
| if (m_defaultOptionIndices.isEmpty()) |
| m_defaultOptionIndices.append((unsigned) !m_data.multiple()); |
| } |
| |
| void WMLSelectElement::selectDefaultOptions() |
| { |
| ASSERT(!m_defaultOptionIndices.isEmpty()); |
| |
| if (!m_data.multiple()) { |
| setSelectedIndex(m_defaultOptionIndices.first() - 1, false); |
| return; |
| } |
| |
| Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end(); |
| for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it) |
| setSelectedIndex((*it) - 1, false); |
| } |
| |
| void WMLSelectElement::initializeVariables() |
| { |
| ASSERT(!m_defaultOptionIndices.isEmpty()); |
| |
| WMLPageState* pageState = wmlPageStateForDocument(document()); |
| if (!pageState) |
| return; |
| |
| const Vector<Element*>& items = m_data.listItems(this); |
| if (items.isEmpty()) |
| return; |
| |
| // Spec: If the 'iname' attribute is specified, then the named variable is set with the default option index. |
| String iname = this->iname(); |
| if (!iname.isEmpty()) |
| pageState->storeVariable(iname, optionIndicesToString()); |
| |
| String name = this->name(); |
| if (name.isEmpty()) |
| return; |
| |
| if (m_data.multiple()) { |
| // Spec: If the 'name' attribute is specified and the select is a multiple-choice element, |
| // then for each index greater than zero, the value of the 'value' attribute on the option |
| // element at the index is added to the name variable. |
| pageState->storeVariable(name, optionIndicesToValueString()); |
| return; |
| } |
| |
| // Spec: If the 'name' attribute is specified and the select is a single-choice element, |
| // then the named variable is set with the value of the 'value' attribute on the option |
| // element at the default option index. |
| unsigned optionIndex = m_defaultOptionIndices.first(); |
| ASSERT(optionIndex >= 1); |
| |
| int listIndex = optionToListIndex(optionIndex - 1); |
| ASSERT(listIndex >= 0); |
| ASSERT(listIndex < (int) items.size()); |
| |
| if (OptionElement* optionElement = toOptionElement(items[listIndex])) |
| pageState->storeVariable(name, optionElement->value()); |
| } |
| |
| void WMLSelectElement::updateVariables() |
| { |
| WMLPageState* pageState = wmlPageStateForDocument(document()); |
| if (!pageState) |
| return; |
| |
| String name = this->name(); |
| String iname = this->iname(); |
| if (iname.isEmpty() && name.isEmpty()) |
| return; |
| |
| String nameString; |
| String inameString; |
| |
| unsigned optionIndex = 0; |
| const Vector<Element*>& items = m_data.listItems(this); |
| |
| for (unsigned i = 0; i < items.size(); ++i) { |
| OptionElement* optionElement = toOptionElement(items[i]); |
| if (!optionElement) |
| continue; |
| |
| ++optionIndex; |
| if (!optionElement->selected()) |
| continue; |
| |
| if (!nameString.isEmpty()) |
| nameString += ";"; |
| |
| if (!inameString.isEmpty()) |
| inameString += ";"; |
| |
| nameString += optionElement->value(); |
| inameString += String::number(optionIndex); |
| } |
| |
| if (!name.isEmpty()) |
| pageState->storeVariable(name, nameString); |
| |
| if (!iname.isEmpty()) |
| pageState->storeVariable(iname, inameString); |
| } |
| |
| Vector<unsigned> WMLSelectElement::parseIndexValueString(const String& indexValue) const |
| { |
| Vector<unsigned> indices; |
| if (indexValue.isEmpty()) |
| return indices; |
| |
| Vector<String> indexStrings; |
| indexValue.split(';', indexStrings); |
| |
| bool ok = false; |
| unsigned optionCount = SelectElement::optionCount(m_data, this); |
| |
| Vector<String>::const_iterator end = indexStrings.end(); |
| for (Vector<String>::const_iterator it = indexStrings.begin(); it != end; ++it) { |
| unsigned parsedValue = (*it).toUIntStrict(&ok); |
| // Spec: Remove all non-integer indices from the value. Remove all out-of-range indices |
| // from the value, where out-of-range is defined as any index with a value greater than |
| // the number of options in the select or with a value less than one. |
| if (!ok || parsedValue < 1 || parsedValue > optionCount) |
| continue; |
| |
| // Spec: Remove duplicate indices. |
| if (indices.find(parsedValue) == notFound) |
| indices.append(parsedValue); |
| } |
| |
| return indices; |
| } |
| |
| Vector<unsigned> WMLSelectElement::valueStringToOptionIndices(const String& value) const |
| { |
| Vector<unsigned> indices; |
| if (value.isEmpty()) |
| return indices; |
| |
| const Vector<Element*>& items = m_data.listItems(this); |
| if (items.isEmpty()) |
| return indices; |
| |
| Vector<String> indexStrings; |
| value.split(';', indexStrings); |
| |
| unsigned optionIndex = 0; |
| |
| Vector<String>::const_iterator end = indexStrings.end(); |
| for (Vector<String>::const_iterator it = indexStrings.begin(); it != end; ++it) { |
| String value = *it; |
| |
| for (unsigned i = 0; i < items.size(); ++i) { |
| if (!isOptionElement(items[i])) |
| continue; |
| |
| ++optionIndex; |
| if (OptionElement* optionElement = toOptionElement(items[i])) { |
| if (optionElement->value() == value) { |
| indices.append(optionIndex); |
| break; |
| } |
| } |
| } |
| } |
| |
| return indices; |
| } |
| |
| String WMLSelectElement::optionIndicesToValueString() const |
| { |
| String valueString; |
| if (m_defaultOptionIndices.isEmpty()) |
| return valueString; |
| |
| const Vector<Element*>& items = m_data.listItems(this); |
| if (items.isEmpty()) |
| return valueString; |
| |
| Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end(); |
| for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it) { |
| unsigned optionIndex = (*it); |
| if (optionIndex < 1 || optionIndex > items.size()) |
| continue; |
| |
| int listIndex = optionToListIndex((*it) - 1); |
| ASSERT(listIndex >= 0); |
| ASSERT(listIndex < (int) items.size()); |
| |
| if (OptionElement* optionElement = toOptionElement(items[listIndex])) { |
| if (!valueString.isEmpty()) |
| valueString += ";"; |
| |
| valueString += optionElement->value(); |
| } |
| } |
| |
| return valueString; |
| } |
| |
| String WMLSelectElement::optionIndicesToString() const |
| { |
| String valueString; |
| if (m_defaultOptionIndices.isEmpty()) |
| return valueString; |
| |
| Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end(); |
| for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it) { |
| if (!valueString.isEmpty()) |
| valueString += ";"; |
| |
| valueString += String::number(*it); |
| } |
| |
| return valueString; |
| } |
| |
| String WMLSelectElement::name() const |
| { |
| return parseValueForbiddingVariableReferences(getAttribute(HTMLNames::nameAttr)); |
| } |
| |
| String WMLSelectElement::value() const |
| { |
| return parseValueSubstitutingVariableReferences(getAttribute(HTMLNames::valueAttr)); |
| } |
| |
| String WMLSelectElement::iname() const |
| { |
| return parseValueForbiddingVariableReferences(getAttribute(inameAttr)); |
| } |
| |
| String WMLSelectElement::ivalue() const |
| { |
| return parseValueSubstitutingVariableReferences(getAttribute(ivalueAttr)); |
| } |
| |
| } |
| |
| #endif |