/** * 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 namespace WebCore { using namespace WMLNames; WMLSelectElement::WMLSelectElement(const QualifiedName& tagName, Document* document) : WMLFormControlElement(tagName, document) , m_initialized(false) { } WMLSelectElement::~WMLSelectElement() { } 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()) { m_initialized = true; return; } // Spec: Step 2 – initialise variables initializeVariables(); // Spec: Step 3 – pre-select option(s) specified by the default option index selectDefaultOptions(); m_initialized = true; } 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::const_iterator end = m_defaultOptionIndices.end(); for (Vector::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& 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& 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 WMLSelectElement::parseIndexValueString(const String& indexValue) const { Vector indices; if (indexValue.isEmpty()) return indices; Vector indexStrings; indexValue.split(';', indexStrings); bool ok = false; unsigned optionCount = SelectElement::optionCount(m_data, this); Vector::const_iterator end = indexStrings.end(); for (Vector::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 WMLSelectElement::valueStringToOptionIndices(const String& value) const { Vector indices; if (value.isEmpty()) return indices; const Vector& items = m_data.listItems(this); if (items.isEmpty()) return indices; Vector indexStrings; value.split(';', indexStrings); unsigned optionIndex = 0; Vector::const_iterator end = indexStrings.end(); for (Vector::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& items = m_data.listItems(this); if (items.isEmpty()) return valueString; Vector::const_iterator end = m_defaultOptionIndices.end(); for (Vector::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::const_iterator end = m_defaultOptionIndices.end(); for (Vector::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