void HTMLSelectElement::recalcListItems(bool updateSelectedStates) const { m_listItems.clear(); HTMLOptionElement* foundSelected = 0; for (Node* current = firstChild(); current; current = current->traverseNextSibling(this)) { if (current->hasTagName(optgroupTag) && current->firstChild()) { // FIXME: It doesn't make sense to add an optgroup to the list items, // when it has children, but not to add it if it happens to have, // children (say some comment nodes or text nodes), yet that's what // this code does! m_listItems.append(static_cast<HTMLElement*>(current)); current = current->firstChild(); // FIXME: It doesn't make sense to handle an <optgroup> inside another <optgroup> // if it's not the first child, but not handle it if it happens to be the first // child, yet that's what this code does! } if (current->hasTagName(optionTag)) { m_listItems.append(static_cast<HTMLElement*>(current)); if (updateSelectedStates) { if (!foundSelected && (usesMenuList() || (!m_multiple && static_cast<HTMLOptionElement*>(current)->selected()))) { foundSelected = static_cast<HTMLOptionElement*>(current); foundSelected->setSelectedState(true); } else if (foundSelected && !m_multiple && static_cast<HTMLOptionElement*>(current)->selected()) { foundSelected->setSelectedState(false); foundSelected = static_cast<HTMLOptionElement*>(current); } } } if (current->hasTagName(hrTag)) m_listItems.append(static_cast<HTMLElement*>(current)); } m_recalcListItems = false; }
void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions) { ASSERT(renderer() && renderer()->isListBox()); unsigned start; unsigned end; ASSERT(m_activeSelectionAnchorIndex >= 0); start = min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex); end = max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex); const Vector<HTMLElement*>& items = listItems(); for (unsigned i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag)) { HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]); if (!option->disabled()) { if (i >= start && i <= end) option->setSelectedState(m_activeSelectionState); else if (deselectOtherOptions || i >= m_cachedStateForActiveSelection.size()) option->setSelectedState(false); else option->setSelectedState(m_cachedStateForActiveSelection[i]); } } } scrollToSelection(); }
void SelectPopupClient::setValueAndClosePopup(int, const String& stringValue) { ASSERT(m_element); static const char* cancelValue = "-1"; if (stringValue == cancelValue) { closePopup(); return; } if (m_size > 0) { bool selecteds[m_size]; for (unsigned i = 0; i < m_size; i++) selecteds[i] = stringValue[i] - '0'; const Vector<HTMLElement*>& items = m_element->listItems(); if (items.size() != static_cast<unsigned int>(m_size)) return; HTMLOptionElement* option; for (unsigned i = 0; i < m_size; i++) { if (items[i]->hasTagName(HTMLNames::optionTag)) { option = static_cast<HTMLOptionElement*>(items[i]); option->setSelectedState(selecteds[i]); } } } // Force repaint because we do not send mouse events to the select element // and the element doesn't automatically repaint itself. if (m_element->renderer()) m_element->renderer()->repaint(); m_notifyChangeTimer.startOneShot(0); }
void SelectPopupClient::setValueAndClosePopup(int, const String& stringValue) { ASSERT(m_size == stringValue.length()); if (m_size > 0) { bool selecteds[m_size]; for (unsigned i = 0; i < m_size; i++) selecteds[i] = stringValue[i] - '0'; const WTF::Vector<HTMLElement*>& items = m_element->listItems(); if (items.size() != static_cast<unsigned int>(m_size)) return; HTMLOptionElement* option; for (unsigned i = 0; i < m_size; i++) { if (items[i]->hasTagName(HTMLNames::optionTag)) { option = static_cast<HTMLOptionElement*>(items[i]); option->setSelectedState(selecteds[i]); } } } // Force repaint because we do not send mouse events to the select element // and the element doesn't automatically repaint itself. m_element->dispatchFormControlChangeEvent(); m_element->renderer()->repaint(); closePopup(); }
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); }
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::reset() { bool optionSelected = false; HTMLOptionElement* firstOption = 0; const Vector<HTMLElement*>& items = listItems(); unsigned i; for (i = 0; i < items.size(); i++) { if (items[i]->hasLocalName(optionTag)) { HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]); if (!option->getAttribute(selectedAttr).isNull()) { option->setSelectedState(true); optionSelected = true; } else option->setSelectedState(false); if (!firstOption) firstOption = option; } } if (!optionSelected && firstOption && usesMenuList()) firstOption->setSelectedState(true); setChanged(); }
void HTMLSelectElement::accessKeySetSelectedIndex(int index) { // first bring into focus the list box if (!focused()) accessKeyAction(false); // if this index is already selected, unselect. otherwise update the selected index Node* listNode = item(index); if (listNode && listNode->hasTagName(optionTag)) { HTMLOptionElement* listElement = static_cast<HTMLOptionElement*>(listNode); if (listElement->selected()) listElement->setSelectedState(false); else setSelectedIndex(index, false, true); } listBoxOnChange(); scrollToSelection(); }
void HTMLSelectElement::listBoxDefaultEventHandler(Event* evt) { if (evt->type() == eventNames().mousedownEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton) { focus(); MouseEvent* mEvt = static_cast<MouseEvent*>(evt); int listIndex = static_cast<RenderListBox*>(renderer())->listIndexAtOffset(mEvt->offsetX(), mEvt->offsetY()); if (listIndex >= 0) { // Save the selection so it can be compared to the new selection when we call onChange during mouseup, or after autoscroll finishes. saveLastSelection(); m_activeSelectionState = true; bool multiSelectKeyPressed = false; #if PLATFORM(MAC) multiSelectKeyPressed = mEvt->metaKey(); #else multiSelectKeyPressed = mEvt->ctrlKey(); #endif bool shiftSelect = multiple() && mEvt->shiftKey(); bool multiSelect = multiple() && multiSelectKeyPressed && !mEvt->shiftKey(); HTMLElement* clickedElement = listItems()[listIndex]; HTMLOptionElement* option = 0; if (clickedElement->hasLocalName(optionTag)) { option = static_cast<HTMLOptionElement*>(clickedElement); // Keep track of whether an active selection (like during drag selection), should select or deselect if (option->selected() && multiSelectKeyPressed) m_activeSelectionState = false; if (!m_activeSelectionState) option->setSelectedState(false); } // If we're not in any special multiple selection mode, then deselect all other items, excluding the clicked option. // If no option was clicked, then this will deselect all items in the list. if (!shiftSelect && !multiSelect) deselectItems(option); // If the anchor hasn't been set, and we're doing a single selection or a shift selection, then initialize the anchor to the first selected index. if (m_activeSelectionAnchorIndex < 0 && !multiSelect) setActiveSelectionAnchorIndex(selectedIndex()); // Set the selection state of the clicked option if (option && !option->disabled()) option->setSelectedState(true); // If there was no selectedIndex() for the previous initialization, or // If we're doing a single selection, or a multiple selection (using cmd or ctrl), then initialize the anchor index to the listIndex that just got clicked. if (listIndex >= 0 && (m_activeSelectionAnchorIndex < 0 || !shiftSelect)) setActiveSelectionAnchorIndex(listIndex); setActiveSelectionEndIndex(listIndex); updateListBoxSelection(!multiSelect); if (Frame* frame = document()->frame()) frame->eventHandler()->setMouseDownMayStartAutoscroll(); evt->setDefaultHandled(); } } else if (evt->type() == eventNames().mouseupEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton && document()->frame()->eventHandler()->autoscrollRenderer() != renderer()) // This makes sure we fire onChange for a single click. For drag selection, onChange will fire when the autoscroll timer stops. listBoxOnChange(); else if (evt->type() == eventNames().keydownEvent) { if (!evt->isKeyboardEvent()) return; String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier(); int endIndex = 0; if (m_activeSelectionEndIndex < 0) { // Initialize the end index if (keyIdentifier == "Down") endIndex = nextSelectableListIndex(lastSelectedListIndex()); else if (keyIdentifier == "Up") endIndex = previousSelectableListIndex(optionToListIndex(selectedIndex())); } else { // Set the end index based on the current end index if (keyIdentifier == "Down") endIndex = nextSelectableListIndex(m_activeSelectionEndIndex); else if (keyIdentifier == "Up") endIndex = previousSelectableListIndex(m_activeSelectionEndIndex); } if (keyIdentifier == "Down" || keyIdentifier == "Up") { // Save the selection so it can be compared to the new selection when we call onChange immediately after making the new selection. saveLastSelection(); ASSERT(endIndex >= 0 && (unsigned)endIndex < listItems().size()); setActiveSelectionEndIndex(endIndex); // If the anchor is unitialized, or if we're going to deselect all other options, then set the anchor index equal to the end index. bool deselectOthers = !multiple() || !static_cast<KeyboardEvent*>(evt)->shiftKey(); if (m_activeSelectionAnchorIndex < 0 || deselectOthers) { m_activeSelectionState = true; if (deselectOthers) deselectItems(); setActiveSelectionAnchorIndex(m_activeSelectionEndIndex); } static_cast<RenderListBox*>(renderer())->scrollToRevealElementAtListIndex(endIndex); updateListBoxSelection(deselectOthers); listBoxOnChange(); evt->setDefaultHandled(); } } else if (evt->type() == eventNames().keypressEvent) { if (!evt->isKeyboardEvent()) return; int keyCode = static_cast<KeyboardEvent*>(evt)->keyCode(); if (keyCode == '\r') { if (form()) form()->submitClick(evt); evt->setDefaultHandled(); return; } } }