bool SelectElement::appendFormData(SelectElementData& data, Element* element, FormDataList& list) { const AtomicString& name = element->formControlName(); if (name.isEmpty()) return false; bool successful = false; const Vector<Element*>& items = data.listItems(element); for (unsigned i = 0; i < items.size(); ++i) { OptionElement* optionElement = toOptionElement(items[i]); if (optionElement && optionElement->selected()) { list.appendData(name, optionElement->value()); successful = true; } } // FIXME: This case should not happen. Make sure that we select the first option // in any case, otherwise we have no consistency with the DOM interface. // We return the first one if it was a combobox select if (!successful && !data.multiple() && data.size() <= 1 && items.size()) { OptionElement* optionElement = toOptionElement(items[0]); const AtomicString& value = optionElement->value(); if (value.isNull()) list.appendData(name, optionElement->text().stripWhiteSpace()); else list.appendData(name, value); successful = true; } return successful; }
void SelectElement::listBoxOnChange(SelectElementData& data, Element* element) { ASSERT(!data.usesMenuList()); Vector<bool>& lastOnChangeSelection = data.lastOnChangeSelection(); const Vector<Element*>& items = data.listItems(element); // If the cached selection list is empty, or the size has changed, then fire dispatchFormControlChangeEvent, and return early. if (lastOnChangeSelection.isEmpty() || lastOnChangeSelection.size() != items.size()) { element->dispatchFormControlChangeEvent(); return; } // Update lastOnChangeSelection and fire dispatchFormControlChangeEvent bool fireOnChange = false; for (unsigned i = 0; i < items.size(); ++i) { OptionElement* optionElement = toOptionElement(items[i]); bool selected = optionElement && optionElement->selected(); if (selected != lastOnChangeSelection[i]) fireOnChange = true; lastOnChangeSelection[i] = selected; } if (fireOnChange) element->dispatchFormControlChangeEvent(); }
void SelectElement::setActiveSelectionAnchorIndex(SelectElementData& data, Element* element, int index) { data.setActiveSelectionAnchorIndex(index); // Cache the selection state so we can restore the old selection as the new selection pivots around this anchor index Vector<bool>& cachedStateForActiveSelection = data.cachedStateForActiveSelection(); cachedStateForActiveSelection.clear(); const Vector<Element*>& items = data.listItems(element); for (unsigned i = 0; i < items.size(); ++i) { OptionElement* optionElement = toOptionElement(items[i]); cachedStateForActiveSelection.append(optionElement && optionElement->selected()); } }
void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, int listIndex) { SelectElement* select = toSelectElement(static_cast<Element*>(node())); const Vector<Element*>& listItems = select->listItems(); Element* element = listItems[listIndex]; OptionElement* optionElement = toOptionElement(element); String itemText; if (optionElement) itemText = optionElement->textIndentedToRespectGroupLabel(); else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) itemText = optionGroupElement->groupLabelText(); // Determine where the item text should be placed IntRect r = itemBoundingBoxRect(tx, ty, listIndex); r.move(optionsSpacingHorizontal, style()->font().ascent()); RenderStyle* itemStyle = element->renderStyle(); if (!itemStyle) itemStyle = style(); Color textColor = element->renderStyle() ? element->renderStyle()->color() : style()->color(); if (optionElement && optionElement->selected()) { if (document()->frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) textColor = theme()->activeListBoxSelectionForegroundColor(); // Honor the foreground color for disabled items else if (!element->disabled()) textColor = theme()->inactiveListBoxSelectionForegroundColor(); } ColorSpace colorSpace = itemStyle->colorSpace(); paintInfo.context->setFillColor(textColor, colorSpace); Font itemFont = style()->font(); if (isOptionGroupElement(element)) { FontDescription d = itemFont.fontDescription(); d.setWeight(d.bolderWeight()); itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); itemFont.update(document()->styleSelector()->fontSelector()); } unsigned length = itemText.length(); const UChar* string = itemText.characters(); TextRun textRun(string, length, 0, 0, 0, itemStyle->direction() == RTL, itemStyle->unicodeBidi() == Override, false, false); // Draw the item text if (itemStyle->visibility() != HIDDEN) paintInfo.context->drawBidiText(itemFont, textRun, r.location()); }
void SelectElement::saveLastSelection(SelectElementData& data, Element* element) { if (data.usesMenuList()) { data.setLastOnChangeIndex(selectedIndex(data, element)); return; } Vector<bool>& lastOnChangeSelection = data.lastOnChangeSelection(); lastOnChangeSelection.clear(); const Vector<Element*>& items = data.listItems(element); for (unsigned i = 0; i < items.size(); ++i) { OptionElement* optionElement = toOptionElement(items[i]); lastOnChangeSelection.append(optionElement && optionElement->selected()); } }
bool SelectElement::saveFormControlState(const SelectElementData& data, const Element* element, String& value) { const Vector<Element*>& items = data.listItems(element); int length = items.size(); // FIXME: Change this code to use the new StringImpl::createUninitialized code path. Vector<char, 1024> characters(length); for (int i = 0; i < length; ++i) { OptionElement* optionElement = toOptionElement(items[i]); bool selected = optionElement && optionElement->selected(); characters[i] = selected ? 'X' : '.'; } value = String(characters.data(), length); return true; }
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); }
void RenderListBox::paintItemBackground(PaintInfo& paintInfo, int tx, int ty, int listIndex) { SelectElement* select = toSelectElement(static_cast<Element*>(node())); const Vector<Element*>& listItems = select->listItems(); Element* element = listItems[listIndex]; OptionElement* optionElement = toOptionElement(element); Color backColor; if (optionElement && optionElement->selected()) { if (document()->frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) backColor = theme()->activeListBoxSelectionBackgroundColor(); else backColor = theme()->inactiveListBoxSelectionBackgroundColor(); } else backColor = element->renderStyle() ? element->renderStyle()->backgroundColor() : style()->backgroundColor(); // Draw the background for this list box item if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) { IntRect itemRect = itemBoundingBoxRect(tx, ty, listIndex); itemRect.intersect(controlClipRect(tx, ty)); paintInfo.context->fillRect(itemRect, backColor); } }
void SelectElement::listBoxDefaultEventHandler(SelectElementData& data, Element* element, Event* event, HTMLFormElement* htmlForm) { const Vector<Element*>& listItems = data.listItems(element); if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { element->focus(); // Convert to coords relative to the list box if needed. MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); IntPoint localOffset = roundedIntPoint(element->renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), false, true)); int listIndex = static_cast<RenderListBox*>(element->renderer())->listIndexAtOffset(localOffset.x(), localOffset.y()); if (listIndex >= 0) { // Save the selection so it can be compared to the new selection when dispatching change events during mouseup, or after autoscroll finishes. saveLastSelection(data, element); data.setActiveSelectionState(true); bool multiSelectKeyPressed = false; #if PLATFORM(MAC) multiSelectKeyPressed = mouseEvent->metaKey(); #else multiSelectKeyPressed = mouseEvent->ctrlKey(); #endif bool shiftSelect = data.multiple() && mouseEvent->shiftKey(); bool multiSelect = data.multiple() && multiSelectKeyPressed && !mouseEvent->shiftKey(); Element* clickedElement = listItems[listIndex]; OptionElement* option = toOptionElement(clickedElement); if (option) { // Keep track of whether an active selection (like during drag selection), should select or deselect if (option->selected() && multiSelectKeyPressed) data.setActiveSelectionState(false); if (!data.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(data, element, clickedElement); // 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 (data.activeSelectionAnchorIndex() < 0 && !multiSelect) setActiveSelectionAnchorIndex(data, element, selectedIndex(data, element)); // Set the selection state of the clicked option if (option && !clickedElement->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 && (data.activeSelectionAnchorIndex() < 0 || !shiftSelect)) setActiveSelectionAnchorIndex(data, element, listIndex); setActiveSelectionEndIndex(data, listIndex); updateListBoxSelection(data, element, !multiSelect); if (Frame* frame = element->document()->frame()) frame->eventHandler()->setMouseDownMayStartAutoscroll(); event->setDefaultHandled(); } } else if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton && element->document()->frame()->eventHandler()->autoscrollRenderer() != element->renderer()) // This makes sure we fire dispatchFormControlChangeEvent for a single click. For drag selection, onChange will fire when the autoscroll timer stops. listBoxOnChange(data, element); else if (event->type() == eventNames().keydownEvent) { if (!event->isKeyboardEvent()) return; String keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier(); int endIndex = 0; if (data.activeSelectionEndIndex() < 0) { // Initialize the end index if (keyIdentifier == "Down") endIndex = nextSelectableListIndex(data, element, lastSelectedListIndex(data, element)); else if (keyIdentifier == "Up") endIndex = previousSelectableListIndex(data, element, optionToListIndex(data, element, selectedIndex(data, element))); } else { // Set the end index based on the current end index if (keyIdentifier == "Down") endIndex = nextSelectableListIndex(data, element, data.activeSelectionEndIndex()); else if (keyIdentifier == "Up") endIndex = previousSelectableListIndex(data, element, data.activeSelectionEndIndex()); } if (keyIdentifier == "Down" || keyIdentifier == "Up") { // Save the selection so it can be compared to the new selection when dispatching change events immediately after making the new selection. saveLastSelection(data, element); ASSERT(endIndex >= 0 && (unsigned) endIndex < listItems.size()); setActiveSelectionEndIndex(data, 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 = !data.multiple() || !static_cast<KeyboardEvent*>(event)->shiftKey(); if (data.activeSelectionAnchorIndex() < 0 || deselectOthers) { data.setActiveSelectionState(true); if (deselectOthers) deselectItems(data, element); setActiveSelectionAnchorIndex(data, element, data.activeSelectionEndIndex()); } static_cast<RenderListBox*>(element->renderer())->scrollToRevealElementAtListIndex(endIndex); updateListBoxSelection(data, element, deselectOthers); listBoxOnChange(data, element); event->setDefaultHandled(); } } else if (event->type() == eventNames().keypressEvent) { if (!event->isKeyboardEvent()) return; int keyCode = static_cast<KeyboardEvent*>(event)->keyCode(); if (keyCode == '\r') { if (htmlForm) htmlForm->submitClick(event); event->setDefaultHandled(); return; } } }