void SelectElement::selectAll(SelectElementData& data, Element* element) { ASSERT(!data.usesMenuList()); if (!element->renderer() || !data.multiple()) return; // Save the selection so it can be compared to the new selectAll selection when dispatching change events saveLastSelection(data, element); data.setActiveSelectionState(true); setActiveSelectionAnchorIndex(data, element, nextSelectableListIndex(data, element, -1)); setActiveSelectionEndIndex(data, previousSelectableListIndex(data, element, -1)); updateListBoxSelection(data, element, false); listBoxOnChange(data, element); }
bool HTMLSelectElement::platformHandleKeydownEvent(KeyboardEvent* event) { // Allow (Shift) F4 and (Ctrl/Shift) Alt/AltGr + Up/Down arrow to pop the menu, matching Firefox. bool eventShowsMenu = (!event->altKey() && !event->ctrlKey() && event->keyIdentifier() == "F4") || ((event->altGraphKey() || event->altKey()) && (event->keyIdentifier() == "Down" || event->keyIdentifier() == "Up")); if (!eventShowsMenu) return false; // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex, // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. saveLastSelection(); if (RenderMenuList* menuList = toRenderMenuList(renderer())) menuList->showPopup(); int index = selectedIndex(); ASSERT(index >= 0); ASSERT_WITH_SECURITY_IMPLICATION(index < static_cast<int>(listItems().size())); setSelectedIndex(index); event->setDefaultHandled(); return true; }
void SelectElement::menuListDefaultEventHandler(SelectElementData& data, Element* element, Event* event, HTMLFormElement* htmlForm) { #if !ARROW_KEYS_POP_MENU UNUSED_PARAM(htmlForm); #endif if (event->type() == eventNames().keydownEvent) { if (!element->renderer() || !event->isKeyboardEvent()) return; String keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier(); bool handled = false; #if ARROW_KEYS_POP_MENU if (keyIdentifier == "Down" || keyIdentifier == "Up") { element->focus(); // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex, // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. saveLastSelection(data, element); if (RenderMenuList* menuList = static_cast<RenderMenuList*>(element->renderer())) menuList->showPopup(); handled = true; } #else const Vector<Element*>& listItems = data.listItems(element); int size = listItems.size(); int listIndex = optionToListIndex(data, element, selectedIndex(data, element)); if (keyIdentifier == "Down" || keyIdentifier == "Right") { for (listIndex += 1; listIndex >= 0 && listIndex < size && (listItems[listIndex]->disabled() || !isOptionElement(listItems[listIndex])); ++listIndex) { } if (listIndex >= 0 && listIndex < size) setSelectedIndex(data, element, listToOptionIndex(data, element, listIndex)); handled = true; } else if (keyIdentifier == "Up" || keyIdentifier == "Left") { for (listIndex -= 1; listIndex >= 0 && listIndex < size && (listItems[listIndex]->disabled() || !isOptionElement(listItems[listIndex])); --listIndex) { } if (listIndex >= 0 && listIndex < size) setSelectedIndex(data, element, listToOptionIndex(data, element, listIndex)); handled = true; } #endif if (handled) event->setDefaultHandled(); } // Use key press event here since sending simulated mouse events // on key down blocks the proper sending of the key press event. if (event->type() == eventNames().keypressEvent) { if (!element->renderer() || !event->isKeyboardEvent()) return; int keyCode = static_cast<KeyboardEvent*>(event)->keyCode(); bool handled = false; #if SPACE_OR_RETURN_POP_MENU if (keyCode == ' ' || keyCode == '\r') { element->focus(); // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex, // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. saveLastSelection(data, element); if (RenderMenuList* menuList = static_cast<RenderMenuList*>(element->renderer())) menuList->showPopup(); handled = true; } #elif ARROW_KEYS_POP_MENU if (keyCode == ' ') { element->focus(); // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex, // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. saveLastSelection(data, element); if (RenderMenuList* menuList = static_cast<RenderMenuList*>(element->renderer())) menuList->showPopup(); handled = true; } else if (keyCode == '\r') { menuListOnChange(data, element); if (htmlForm) htmlForm->submitClick(event); handled = true; } #else int listIndex = optionToListIndex(data, element, selectedIndex(data, element)); if (keyCode == '\r') { // listIndex should already be selected, but this will fire the onchange handler. setSelectedIndex(data, element, listToOptionIndex(data, element, listIndex), true, true); handled = true; } #endif if (handled) event->setDefaultHandled(); } if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { element->focus(); if (RenderMenuList* menuList = static_cast<RenderMenuList*>(element->renderer())) { if (menuList->popupIsVisible()) menuList->hidePopup(); else { // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex, // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. saveLastSelection(data, element); menuList->showPopup(); } } event->setDefaultHandled(); } }
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; } } }
void SelectElement::dispatchFocusEvent(SelectElementData& data, Element* element) { // Save the selection so it can be compared to the new selection when dispatching change events during blur event dispatchal if (data.usesMenuList()) saveLastSelection(data, element); }