📄 htmlselectelement.cpp
字号:
/* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. * (C) 2006 Alexey Proskuryakov (ap@nypop.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"#include "HTMLSelectElement.h"#include "AXObjectCache.h"#include "CSSPropertyNames.h"#include "CSSStyleSelector.h"#include "CharacterNames.h"#include "ChromeClient.h"#include "Document.h"#include "Event.h"#include "EventHandler.h"#include "EventNames.h"#include "FormDataList.h"#include "Frame.h"#include "FrameLoader.h"#include "FrameLoaderClient.h"#include "HTMLFormElement.h"#include "HTMLNames.h"#include "HTMLOptionElement.h"#include "HTMLOptionsCollection.h"#include "KeyboardEvent.h"#include "MouseEvent.h"#include "Page.h"#include "RenderListBox.h"#include "RenderMenuList.h"#include <math.h>#include <wtf/StdLibExtras.h>#include <wtf/Vector.h>#if PLATFORM(MAC)#define ARROW_KEYS_POP_MENU 1#else#define ARROW_KEYS_POP_MENU 0#endifusing namespace std;using namespace WTF;using namespace Unicode;namespace WebCore {using namespace HTMLNames;static const DOMTimeStamp typeAheadTimeout = 1000;HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f) : HTMLFormControlElementWithState(tagName, doc, f) , m_minwidth(0) , m_size(0) , m_multiple(false) , m_recalcListItems(false) , m_lastOnChangeIndex(-1) , m_activeSelectionAnchorIndex(-1) , m_activeSelectionEndIndex(-1) , m_activeSelectionState(false) , m_repeatingChar(0) , m_lastCharTime(0){ ASSERT(hasTagName(selectTag) || hasTagName(keygenTag));}bool HTMLSelectElement::checkDTD(const Node* newChild){ // Make sure to keep <optgroup> in sync with this. return newChild->isTextNode() || newChild->hasTagName(optionTag) || newChild->hasTagName(optgroupTag) || newChild->hasTagName(hrTag) || newChild->hasTagName(scriptTag);}void HTMLSelectElement::recalcStyle( StyleChange ch ){ if (hasChangedChild() && renderer()) { if (usesMenuList()) static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true); else static_cast<RenderListBox*>(renderer())->setOptionsChanged(true); } else if (m_recalcListItems) recalcListItems(); HTMLFormControlElementWithState::recalcStyle(ch);}const AtomicString& HTMLSelectElement::type() const{ DEFINE_STATIC_LOCAL(const AtomicString, selectMultiple, ("select-multiple")); DEFINE_STATIC_LOCAL(const AtomicString, selectOne, ("select-one")); return m_multiple ? selectMultiple : selectOne;}int HTMLSelectElement::selectedIndex() const{ // return the number of the first option selected unsigned index = 0; const Vector<HTMLElement*>& items = listItems(); for (unsigned int i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag)) { if (static_cast<HTMLOptionElement*>(items[i])->selected()) return index; index++; } } return -1;}int HTMLSelectElement::lastSelectedListIndex() const{ // return the number of the last option selected unsigned index = 0; bool found = false; const Vector<HTMLElement*>& items = listItems(); for (unsigned int i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag)) { if (static_cast<HTMLOptionElement*>(items[i])->selected()) { index = i; found = true; } } } return found ? (int) index : -1;}void HTMLSelectElement::deselectItems(HTMLOptionElement* excludeElement){ const Vector<HTMLElement*>& items = listItems(); unsigned i; for (i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag) && (items[i] != excludeElement)) { HTMLOptionElement* element = static_cast<HTMLOptionElement*>(items[i]); element->setSelectedState(false); } }}void HTMLSelectElement::setSelectedIndex(int optionIndex, bool deselect, bool fireOnChange){ const Vector<HTMLElement*>& items = listItems(); int listIndex = optionToListIndex(optionIndex); HTMLOptionElement* element = 0; if (!multiple()) deselect = true; if (listIndex >= 0) { if (m_activeSelectionAnchorIndex < 0 || deselect) setActiveSelectionAnchorIndex(listIndex); if (m_activeSelectionEndIndex < 0 || deselect) setActiveSelectionEndIndex(listIndex); element = static_cast<HTMLOptionElement*>(items[listIndex]); element->setSelectedState(true); } if (deselect) deselectItems(element); scrollToSelection(); // This only gets called with fireOnChange for menu lists. if (fireOnChange && usesMenuList()) menuListOnChange(); Frame* frame = document()->frame(); if (frame) frame->page()->chrome()->client()->formStateDidChange(this);}int HTMLSelectElement::activeSelectionStartListIndex() const{ if (m_activeSelectionAnchorIndex >= 0) return m_activeSelectionAnchorIndex; return optionToListIndex(selectedIndex());}int HTMLSelectElement::activeSelectionEndListIndex() const{ if (m_activeSelectionEndIndex >= 0) return m_activeSelectionEndIndex; return lastSelectedListIndex();}unsigned HTMLSelectElement::length() const{ unsigned len = 0; const Vector<HTMLElement*>& items = listItems(); for (unsigned i = 0; i < items.size(); ++i) { if (items[i]->hasLocalName(optionTag)) ++len; } return len;}void HTMLSelectElement::add(HTMLElement *element, HTMLElement *before, ExceptionCode& ec){ RefPtr<HTMLElement> protectNewChild(element); // make sure the element is ref'd and deref'd so we don't leak it if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag))) return; ec = 0; insertBefore(element, before, ec); if (!ec) setRecalcListItems();}void HTMLSelectElement::remove(int index){ ExceptionCode ec = 0; int listIndex = optionToListIndex(index); const Vector<HTMLElement*>& items = listItems(); if (listIndex < 0 || index >= int(items.size())) return; // ### what should we do ? remove the last item? Element *item = items[listIndex]; ASSERT(item->parentNode()); item->parentNode()->removeChild(item, ec); if (!ec) setRecalcListItems();}String HTMLSelectElement::value(){ unsigned i; const Vector<HTMLElement*>& items = listItems(); for (i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->selected()) return static_cast<HTMLOptionElement*>(items[i])->value(); } return String("");}void HTMLSelectElement::setValue(const String &value){ if (value.isNull()) return; // find the option with value() matching the given parameter // and make it the current selection. const Vector<HTMLElement*>& items = listItems(); unsigned optionIndex = 0; for (unsigned i = 0; i < items.size(); i++) if (items[i]->hasLocalName(optionTag)) { if (static_cast<HTMLOptionElement*>(items[i])->value() == value) { setSelectedIndex(optionIndex, true); return; } optionIndex++; }}bool HTMLSelectElement::saveState(String& value) const{ const Vector<HTMLElement*>& items = listItems(); int l = items.size(); Vector<char, 1024> characters(l); for (int i = 0; i < l; ++i) { HTMLElement* e = items[i]; bool selected = e->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(e)->selected(); characters[i] = selected ? 'X' : '.'; } value = String(characters.data(), l); return true;}void HTMLSelectElement::restoreState(const String& state){ recalcListItems(); const Vector<HTMLElement*>& items = listItems(); int l = items.size(); for (int i = 0; i < l; i++) if (items[i]->hasLocalName(optionTag)) static_cast<HTMLOptionElement*>(items[i])->setSelectedState(state[i] == 'X'); setChanged();}bool HTMLSelectElement::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec, bool shouldLazyAttach){ bool result = HTMLFormControlElementWithState::insertBefore(newChild, refChild, ec, shouldLazyAttach); if (result) setRecalcListItems(); return result;}bool HTMLSelectElement::replaceChild(PassRefPtr<Node> newChild, Node *oldChild, ExceptionCode& ec, bool shouldLazyAttach){ bool result = HTMLFormControlElementWithState::replaceChild(newChild, oldChild, ec, shouldLazyAttach); if (result) setRecalcListItems(); return result;}bool HTMLSelectElement::removeChild(Node* oldChild, ExceptionCode& ec){ bool result = HTMLFormControlElementWithState::removeChild(oldChild, ec); if (result) setRecalcListItems(); return result;}bool HTMLSelectElement::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bool shouldLazyAttach){ bool result = HTMLFormControlElementWithState::appendChild(newChild, ec, shouldLazyAttach); if (result) setRecalcListItems(); return result;}bool HTMLSelectElement::removeChildren(){ bool result = HTMLFormControlElementWithState::removeChildren(); if (result) setRecalcListItems(); return result;}void HTMLSelectElement::parseMappedAttribute(MappedAttribute *attr){ bool oldUsesMenuList = usesMenuList(); if (attr->name() == sizeAttr) { int oldSize = m_size; // Set the attribute value to a number. // This is important since the style rules for this attribute can determine the appearance property. int size = attr->value().toInt(); String attrSize = String::number(size); if (attrSize != attr->value()) attr->setValue(attrSize); m_size = max(size, 1); if ((oldUsesMenuList != usesMenuList() || !oldUsesMenuList && m_size != oldSize) && attached()) { detach(); attach(); setRecalcListItems(); } } else if (attr->name() == widthAttr) { m_minwidth = max(attr->value().toInt(), 0); } else if (attr->name() == multipleAttr) { m_multiple = (!attr->isNull()); if (oldUsesMenuList != usesMenuList() && attached()) { detach(); attach(); } } else if (attr->name() == accesskeyAttr) { // FIXME: ignore for the moment } else if (attr->name() == alignAttr) { // Don't map 'align' attribute. This matches what Firefox, Opera and IE do. // See http://bugs.webkit.org/show_bug.cgi?id=12072 } else if (attr->name() == onfocusAttr) { setInlineEventListenerForTypeAndAttribute(eventNames().focusEvent, attr); } else if (attr->name() == onblurAttr) { setInlineEventListenerForTypeAndAttribute(eventNames().blurEvent, attr); } else if (attr->name() == onchangeAttr) { setInlineEventListenerForTypeAndAttribute(eventNames().changeEvent, attr); } else HTMLFormControlElementWithState::parseMappedAttribute(attr);}bool HTMLSelectElement::isKeyboardFocusable(KeyboardEvent* event) const
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -