e982adb7ec394b79e77d1c5716cb7c6de27836f4
[WebKit-https.git] / WebCore / html / HTMLSelectElement.cpp
1 /*
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2001 Dirk Mueller (mueller@kde.org)
7  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
8  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  *
25  */
26  
27 #include "config.h"
28 #include "HTMLSelectElement.h"
29
30 #include "CharacterNames.h"
31 #include "CSSPropertyNames.h"
32 #include "Document.h"
33 #include "Event.h"
34 #include "EventHandler.h"
35 #include "EventNames.h"
36 #include "FormDataList.h"
37 #include "Frame.h"
38 #include "HTMLFormElement.h"
39 #include "HTMLNames.h"
40 #include "HTMLOptionElement.h"
41 #include "HTMLOptionsCollection.h"
42 #include "KeyboardEvent.h"
43 #include "MouseEvent.h"
44 #include "RenderListBox.h"
45 #include "RenderMenuList.h"
46 #include "cssstyleselector.h"
47 #include <wtf/Vector.h>
48 #include <math.h>
49
50 #if PLATFORM(MAC)
51 #define ARROW_KEYS_POP_MENU 1
52 #else
53 #define ARROW_KEYS_POP_MENU 0
54 #endif
55
56 using namespace std;
57 using namespace WTF;
58 using namespace Unicode;
59
60 namespace WebCore {
61
62 using namespace EventNames;
63 using namespace HTMLNames;
64
65 const DOMTimeStamp typeAheadTimeout = 1000;
66
67 HTMLSelectElement::HTMLSelectElement(Document* doc, HTMLFormElement* f)
68     : HTMLGenericFormElement(selectTag, doc, f)
69     , m_minwidth(0)
70     , m_size(0)
71     , m_multiple(false)
72     , m_recalcListItems(false)
73     , m_lastOnChangeIndex(-1)
74     , m_activeSelectionAnchorIndex(-1)
75     , m_activeSelectionEndIndex(-1)
76     , m_activeSelectionState(false)
77     , m_repeatingChar(0)
78     , m_lastCharTime(0)
79     , m_typedString(String())
80 {
81     document()->registerFormElementWithState(this);
82 }
83
84 HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
85     : HTMLGenericFormElement(tagName, doc, f), m_minwidth(0), m_size(0), m_multiple(false), m_recalcListItems(false)
86 {
87     document()->registerFormElementWithState(this);
88 }
89
90 HTMLSelectElement::~HTMLSelectElement()
91 {
92     document()->deregisterFormElementWithState(this);
93 }
94
95 bool HTMLSelectElement::checkDTD(const Node* newChild)
96 {
97     return newChild->isTextNode() || newChild->hasTagName(optionTag) || newChild->hasTagName(optgroupTag) || newChild->hasTagName(hrTag) ||
98            newChild->hasTagName(scriptTag);
99 }
100
101 void HTMLSelectElement::recalcStyle( StyleChange ch )
102 {
103     if (hasChangedChild() && renderer()) {
104         if (usesMenuList())
105             static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true);
106         else
107             static_cast<RenderListBox*>(renderer())->setOptionsChanged(true);
108     }
109
110     HTMLGenericFormElement::recalcStyle( ch );
111 }
112
113 const AtomicString& HTMLSelectElement::type() const
114 {
115     static const AtomicString selectMultiple("select-multiple");
116     static const AtomicString selectOne("select-one");
117     return m_multiple ? selectMultiple : selectOne;
118 }
119
120 int HTMLSelectElement::selectedIndex() const
121 {
122     // return the number of the first option selected
123     unsigned index = 0;
124     const Vector<HTMLElement*>& items = listItems();
125     for (unsigned int i = 0; i < items.size(); i++) {
126         if (items[i]->hasLocalName(optionTag)) {
127             if (static_cast<HTMLOptionElement*>(items[i])->selected())
128                 return index;
129             index++;
130         }
131     }
132     return -1;
133 }
134
135 int HTMLSelectElement::lastSelectedListIndex() const
136 {
137     // return the number of the last option selected
138     unsigned index = 0;
139     bool found = false;
140     const Vector<HTMLElement*>& items = listItems();
141     for (unsigned int i = 0; i < items.size(); i++) {
142         if (items[i]->hasLocalName(optionTag)) {
143             if (static_cast<HTMLOptionElement*>(items[i])->selected()) {
144                 index = i;
145                 found = true;
146             }
147         }
148     }
149     return found ? (int) index : -1;
150 }
151
152 void HTMLSelectElement::deselectItems(HTMLOptionElement* excludeElement)
153 {
154     const Vector<HTMLElement*>& items = listItems();
155     unsigned i;
156     for (i = 0; i < items.size(); i++) {
157         if (items[i]->hasLocalName(optionTag) && (items[i] != excludeElement)) {
158             HTMLOptionElement* element = static_cast<HTMLOptionElement*>(items[i]);
159             element->m_selected = false;
160             element->setChanged();
161         }
162     }
163 }
164
165 void HTMLSelectElement::setSelectedIndex(int optionIndex, bool deselect, bool fireOnChange)
166 {
167     const Vector<HTMLElement*>& items = listItems();
168     int listIndex = optionToListIndex(optionIndex);
169     HTMLOptionElement* element = 0;
170
171     if (listIndex >= 0) {
172         element = static_cast<HTMLOptionElement*>(items[listIndex]);
173         element->setSelected(true);
174     }
175
176     if (deselect)
177         deselectItems(element);
178
179     if (listIndex >= 0) {
180         if (m_activeSelectionAnchorIndex < 0 || deselect)
181             setActiveSelectionAnchorIndex(listIndex);
182         if (m_activeSelectionEndIndex < 0 || deselect)
183             setActiveSelectionEndIndex(listIndex);
184     }
185
186     if (usesMenuList() && fireOnChange && m_lastOnChangeIndex != optionIndex) {
187         m_lastOnChangeIndex = optionIndex;
188         onChange();
189     }
190 }
191
192 int HTMLSelectElement::length() const
193 {
194     int len = 0;
195     unsigned i;
196     const Vector<HTMLElement*>& items = listItems();
197     for (i = 0; i < items.size(); i++) {
198         if (items[i]->hasLocalName(optionTag))
199             len++;
200     }
201     return len;
202 }
203
204 void HTMLSelectElement::add( HTMLElement *element, HTMLElement *before, ExceptionCode& ec)
205 {
206     RefPtr<HTMLElement> protectNewChild(element); // make sure the element is ref'd and deref'd so we don't leak it
207
208     if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag)))
209         return;
210
211     insertBefore(element, before, ec);
212     if (!ec)
213         setRecalcListItems();
214 }
215
216 void HTMLSelectElement::remove(int index)
217 {
218     ExceptionCode ec = 0;
219     int listIndex = optionToListIndex(index);
220
221     const Vector<HTMLElement*>& items = listItems();
222     if (listIndex < 0 || index >= int(items.size()))
223         return; // ### what should we do ? remove the last item?
224
225     removeChild(items[listIndex], ec);
226     if (!ec)
227         setRecalcListItems();
228 }
229
230 String HTMLSelectElement::value()
231 {
232     unsigned i;
233     const Vector<HTMLElement*>& items = listItems();
234     for (i = 0; i < items.size(); i++) {
235         if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->selected())
236             return static_cast<HTMLOptionElement*>(items[i])->value();
237     }
238     return String("");
239 }
240
241 void HTMLSelectElement::setValue(const String &value)
242 {
243     if (value.isNull())
244         return;
245     // find the option with value() matching the given parameter
246     // and make it the current selection.
247     const Vector<HTMLElement*>& items = listItems();
248     for (unsigned i = 0; i < items.size(); i++)
249         if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->value() == value) {
250             static_cast<HTMLOptionElement*>(items[i])->setSelected(true);
251             return;
252         }
253 }
254
255 String HTMLSelectElement::stateValue() const
256 {
257     const Vector<HTMLElement*>& items = listItems();
258     int l = items.size();
259     Vector<char, 1024> characters(l);
260     for (int i = 0; i < l; ++i) {
261         HTMLElement* e = items[i];
262         bool selected = e->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(e)->selected();
263         characters[i] = selected ? 'X' : '.';
264     }
265     return String(characters, l);
266 }
267
268 void HTMLSelectElement::restoreState(const String& state)
269 {
270     recalcListItems();
271
272     const Vector<HTMLElement*>& items = listItems();
273     int l = items.size();
274     for (int i = 0; i < l; i++)
275         if (items[i]->hasLocalName(optionTag))
276             static_cast<HTMLOptionElement*>(items[i])->setSelected(state[i] == 'X');
277     setChanged(true);
278 }
279
280 bool HTMLSelectElement::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec)
281 {
282     bool result = HTMLGenericFormElement::insertBefore(newChild, refChild, ec);
283     if (result)
284         setRecalcListItems();
285     return result;
286 }
287
288 bool HTMLSelectElement::replaceChild(PassRefPtr<Node> newChild, Node *oldChild, ExceptionCode& ec)
289 {
290     bool result = HTMLGenericFormElement::replaceChild(newChild, oldChild, ec);
291     if (result)
292         setRecalcListItems();
293     return result;
294 }
295
296 bool HTMLSelectElement::removeChild(Node* oldChild, ExceptionCode& ec)
297 {
298     bool result = HTMLGenericFormElement::removeChild(oldChild, ec);
299     if (result)
300         setRecalcListItems();
301     return result;
302 }
303
304 bool HTMLSelectElement::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec)
305 {
306     bool result = HTMLGenericFormElement::appendChild(newChild, ec);
307     if (result)
308         setRecalcListItems();
309     return result;
310 }
311
312 ContainerNode* HTMLSelectElement::addChild(PassRefPtr<Node> newChild)
313 {
314     ContainerNode* result = HTMLGenericFormElement::addChild(newChild);
315     if (result)
316         setRecalcListItems();
317     return result;
318 }
319
320 void HTMLSelectElement::parseMappedAttribute(MappedAttribute *attr)
321 {
322     bool oldUsesMenuList = usesMenuList();
323     if (attr->name() == sizeAttr) {
324         m_size = max(attr->value().toInt(), 1);
325         if (oldUsesMenuList != usesMenuList() && attached()) {
326             detach();
327             attach();
328         }
329     } else if (attr->name() == widthAttr) {
330         m_minwidth = max(attr->value().toInt(), 0);
331     } else if (attr->name() == multipleAttr) {
332         m_multiple = (!attr->isNull());
333         if (oldUsesMenuList != usesMenuList() && attached()) {
334             detach();
335             attach();
336         }
337     } else if (attr->name() == accesskeyAttr) {
338         // FIXME: ignore for the moment
339     } else if (attr->name() == alignAttr) {
340         // Don't map 'align' attribute.  This matches what Firefox, Opera and IE do.
341         // See http://bugs.webkit.org/show_bug.cgi?id=12072
342     } else if (attr->name() == onfocusAttr) {
343         setHTMLEventListener(focusEvent, attr);
344     } else if (attr->name() == onblurAttr) {
345         setHTMLEventListener(blurEvent, attr);
346     } else if (attr->name() == onchangeAttr) {
347         setHTMLEventListener(changeEvent, attr);
348     } else
349         HTMLGenericFormElement::parseMappedAttribute(attr);
350 }
351
352 bool HTMLSelectElement::isKeyboardFocusable(KeyboardEvent* event) const
353 {
354     if (renderer())
355         return isFocusable();
356     return HTMLGenericFormElement::isKeyboardFocusable(event);
357 }
358
359 bool HTMLSelectElement::isMouseFocusable() const
360 {
361     if (renderer())
362         return isFocusable();
363     return HTMLGenericFormElement::isMouseFocusable();
364 }
365
366 bool HTMLSelectElement::canSelectAll() const
367 {
368     return !usesMenuList() && renderer() && renderer()->canSelect(); 
369 }
370
371 void HTMLSelectElement::selectAll()
372 {
373     ASSERT(!usesMenuList());
374     if (!renderer() || !multiple())
375         return;
376     
377     m_activeSelectionState = true;
378     setActiveSelectionAnchorIndex(nextSelectableListIndex(-1));
379     setActiveSelectionEndIndex(previousSelectableListIndex(-1));
380     
381     updateListBoxSelection(false);
382     renderer()->repaint();
383     listBoxOnChange();
384 }
385
386 RenderObject *HTMLSelectElement::createRenderer(RenderArena *arena, RenderStyle *style)
387 {
388     if (usesMenuList())
389         return new (arena) RenderMenuList(this);
390     return new (arena) RenderListBox(this);
391 }
392
393 bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
394 {
395     bool successful = false;
396     const Vector<HTMLElement*>& items = listItems();
397
398     unsigned i;
399     for (i = 0; i < items.size(); i++) {
400         if (items[i]->hasLocalName(optionTag)) {
401             HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]);
402             if (option->selected()) {
403                 list.appendData(name(), option->value());
404                 successful = true;
405             }
406         }
407     }
408
409     // ### this case should not happen. make sure that we select the first option
410     // in any case. otherwise we have no consistency with the DOM interface. FIXME!
411     // we return the first one if it was a combobox select
412     if (!successful && !m_multiple && m_size <= 1 && items.size() &&
413         (items[0]->hasLocalName(optionTag))) {
414         HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[0]);
415         if (option->value().isNull())
416             list.appendData(name(), option->text().stripWhiteSpace());
417         else
418             list.appendData(name(), option->value());
419         successful = true;
420     }
421
422     return successful;
423 }
424
425 int HTMLSelectElement::optionToListIndex(int optionIndex) const
426 {
427     const Vector<HTMLElement*>& items = listItems();
428     if (optionIndex < 0 || optionIndex >= int(items.size()))
429         return -1;
430
431     int listIndex = 0;
432     int optionIndex2 = 0;
433     for (;
434          optionIndex2 < int(items.size()) && optionIndex2 <= optionIndex;
435          listIndex++) { // not a typo!
436         if (items[listIndex]->hasLocalName(optionTag))
437             optionIndex2++;
438     }
439     listIndex--;
440     return listIndex;
441 }
442
443 int HTMLSelectElement::listToOptionIndex(int listIndex) const
444 {
445     const Vector<HTMLElement*>& items = listItems();
446     if (listIndex < 0 || listIndex >= int(items.size()) ||
447         !items[listIndex]->hasLocalName(optionTag))
448         return -1;
449
450     int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list
451     for (int i = 0; i < listIndex; i++)
452         if (items[i]->hasLocalName(optionTag))
453             optionIndex++;
454     return optionIndex;
455 }
456
457 PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options()
458 {
459     return new HTMLOptionsCollection(this);
460 }
461
462 void HTMLSelectElement::recalcListItems() const
463 {
464     Node* current = firstChild();
465     m_listItems.clear();
466     HTMLOptionElement* foundSelected = 0;
467     while (current) {
468         if (current->hasTagName(optgroupTag) && current->firstChild()) {
469             // ### what if optgroup contains just comments? don't want one of no options in it...
470             m_listItems.append(static_cast<HTMLElement*>(current));
471             current = current->firstChild();
472         }
473         if (current->hasTagName(optionTag)) {
474             m_listItems.append(static_cast<HTMLElement*>(current));
475             if (!foundSelected && (usesMenuList() || (!m_multiple && static_cast<HTMLOptionElement*>(current)->selected()))) {
476                 foundSelected = static_cast<HTMLOptionElement*>(current);
477                 foundSelected->m_selected = true;
478             } else if (foundSelected && !m_multiple && static_cast<HTMLOptionElement*>(current)->selected()) {
479                 foundSelected->m_selected = false;
480                 foundSelected = static_cast<HTMLOptionElement*>(current);
481             }
482         }
483         if (current->hasTagName(hrTag))
484             m_listItems.append(static_cast<HTMLElement*>(current));
485
486         Node* parent = current->parentNode();
487         current = current->nextSibling();
488         if (!current) {
489             if (parent != this)
490                 current = parent->nextSibling();
491         }
492     }
493     m_recalcListItems = false;
494 }
495
496 void HTMLSelectElement::childrenChanged()
497 {
498     setRecalcListItems();
499
500     HTMLGenericFormElement::childrenChanged();
501 }
502
503 void HTMLSelectElement::setRecalcListItems()
504 {
505     m_recalcListItems = true;
506     if (renderer()) {
507         if (usesMenuList())
508             static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true);
509         else
510             static_cast<RenderListBox*>(renderer())->setOptionsChanged(true);
511     }
512     setChanged();
513 }
514
515 void HTMLSelectElement::reset()
516 {
517     bool optionSelected = false;
518     HTMLOptionElement* firstOption = 0;
519     const Vector<HTMLElement*>& items = listItems();
520     unsigned i;
521     for (i = 0; i < items.size(); i++) {
522         if (items[i]->hasLocalName(optionTag)) {
523             HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]);
524             if (!option->getAttribute(selectedAttr).isNull()) {
525                 option->setSelected(true);
526                 optionSelected = true;
527             } else
528                 option->setSelected(false);
529             if (!firstOption)
530                 firstOption = option;
531         }
532     }
533     if (!optionSelected && firstOption && usesMenuList())
534         firstOption->setSelected(true);
535     setChanged(true);
536     m_lastOnChangeIndex = -1;
537 }
538
539 void HTMLSelectElement::notifyOptionSelected(HTMLOptionElement *selectedOption, bool selected)
540  {
541     if (selected && !m_multiple)
542         deselectItems(selectedOption);
543
544     if (renderer() && !usesMenuList())
545         static_cast<RenderListBox*>(renderer())->setSelectionChanged(true);
546
547     setChanged(true);
548 }
549
550 void HTMLSelectElement::dispatchBlurEvent()
551 {
552 #if !ARROW_KEYS_POP_MENU
553     if (usesMenuList() && selectedIndex() != m_lastOnChangeIndex) {
554         m_lastOnChangeIndex = selectedIndex();
555         onChange();
556     }
557 #endif
558     HTMLGenericFormElement::dispatchBlurEvent();
559 }
560
561 void HTMLSelectElement::defaultEventHandler(Event* evt)
562 {
563     if (usesMenuList())
564         menuListDefaultEventHandler(evt);
565     else 
566         listBoxDefaultEventHandler(evt);
567     
568     if (evt->defaultHandled())
569         return;
570
571     if (!evt->defaultHandled() && evt->type() == keypressEvent && evt->isKeyboardEvent()) {
572         KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(evt);
573     
574         if (!keyboardEvent->ctrlKey() && !keyboardEvent->altKey() && !keyboardEvent->metaKey() &&
575             isPrintableChar(keyboardEvent->charCode())) {
576             typeAheadFind(keyboardEvent);
577             evt->setDefaultHandled();
578             return;
579         }
580     }
581
582     HTMLGenericFormElement::defaultEventHandler(evt);
583 }
584
585 void HTMLSelectElement::menuListDefaultEventHandler(Event* evt)
586 {
587     RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer());
588
589     // Use key press event here since sending simulated mouse events
590     // on key down blocks the proper sending of the key press event.
591     if (evt->type() == keypressEvent) {
592         if (!renderer() || !evt->isKeyboardEvent())
593             return;
594         String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
595         bool handled = false;
596 #if ARROW_KEYS_POP_MENU
597         if (form() && keyIdentifier == "Enter") {
598             blur();
599             // Make sure the form hasn't been destroyed during the blur.
600             if (form())
601                 form()->submitClick(evt);
602             handled = true;
603         }
604         if ((keyIdentifier == "Down" || keyIdentifier == "Up" || keyIdentifier == "U+000020") && renderer() && usesMenuList()) {
605             focus();
606             menuList->showPopup();
607             handled = true;
608         }
609 #else
610         int listIndex = optionToListIndex(selectedIndex());
611         if (keyIdentifier == "Down" || keyIdentifier == "Right") {
612             size_t size = listItems().size();
613             for (listIndex += 1;
614                  listIndex >= 0 && listIndex < size && (listItems()[listIndex]->disabled() || !listItems()[listIndex]->hasTagName(optionTag));
615                  ++listIndex);
616             
617             if (listIndex >= 0 && listIndex < size)
618                 setSelectedIndex(listToOptionIndex(listIndex));
619             handled = true;
620         } else if (keyIdentifier == "Up" || keyIdentifier == "Left") {
621             size_t size = listItems().size();
622             for (listIndex -= 1;
623                  listIndex >= 0 && listIndex < size && (listItems()[listIndex]->disabled() || !listItems()[listIndex]->hasTagName(optionTag));
624                  --listIndex);
625             
626             if (listIndex >= 0 && listIndex < size)
627                 setSelectedIndex(listToOptionIndex(listIndex));
628             handled = true;
629         } else if (keyIdentifier == "Enter") {
630             setSelectedIndex(listToOptionIndex(listIndex), true, true);
631         }
632 #endif
633         if (handled)
634             evt->setDefaultHandled();
635
636     }
637     if (evt->type() == mousedownEvent) {
638         focus();
639         if (menuList->popupIsVisible())
640             menuList->hidePopup();
641         else
642             menuList->showPopup();
643         evt->setDefaultHandled();
644     }
645 }
646
647 void HTMLSelectElement::listBoxDefaultEventHandler(Event* evt)
648 {
649     if (!renderer() || !renderer()->canSelect())
650         return;
651
652     if (evt->type() == mousedownEvent) {
653         MouseEvent* mEvt = static_cast<MouseEvent*>(evt);
654         int listIndex = static_cast<RenderListBox*>(renderer())->listIndexAtOffset(mEvt->offsetX(), mEvt->offsetY());
655         if (listIndex >= 0) {
656             m_activeSelectionState = true;
657             
658             bool multiSelectKeyPressed = false;
659 #if PLATFORM(MAC)
660             multiSelectKeyPressed = mEvt->metaKey();
661 #else
662             multiSelectKeyPressed = mEvt->ctrlKey();
663 #endif
664
665             bool shiftSelect = multiple() && mEvt->shiftKey();
666             bool multiSelect = multiple() && multiSelectKeyPressed && !mEvt->shiftKey();
667             
668             HTMLElement* clickedElement = listItems()[listIndex];            
669             HTMLOptionElement* option = 0;
670             if (clickedElement->hasLocalName(optionTag)) {
671                 option = static_cast<HTMLOptionElement*>(clickedElement);
672                 
673                 // Keep track of whether an active selection (like during drag selection), should select or deselect
674                 if (option->selected() && multiSelectKeyPressed)
675                     m_activeSelectionState = false;
676
677                 if (!m_activeSelectionState)
678                     option->m_selected = false;
679             }
680             
681             // If we're not in any special multiple selection mode, then deselect all other items, excluding the clicked option.
682             // If no option was clicked, then this will deselect all items in the list.
683             if (!shiftSelect && !multiSelect)
684                 deselectItems(option);
685
686             // 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.
687             if (m_activeSelectionAnchorIndex < 0 && !multiSelect)
688                 setActiveSelectionAnchorIndex(selectedIndex());
689
690             // Set the selection state of the clicked option
691             if (option && !option->disabled())
692                 option->m_selected = true;
693             
694             // If there was no selectedIndex() for the previous initialization, or
695             // 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.
696             if (listIndex >= 0 && (m_activeSelectionAnchorIndex < 0 || !shiftSelect))
697                 setActiveSelectionAnchorIndex(listIndex);
698             
699             setActiveSelectionEndIndex(listIndex);
700             updateListBoxSelection(!multiSelect);
701             renderer()->repaint();
702             evt->setDefaultHandled();
703         }
704     } else if (evt->type() == mouseupEvent && document()->frame()->eventHandler()->autoscrollRenderer() != renderer())
705         // This makes sure we fire onChange for a single click.  For drag selection, onChange will fire when the autoscroll timer stops.
706         listBoxOnChange();
707     else if (evt->type() == keypressEvent) {
708         if (!evt->isKeyboardEvent())
709             return;
710         String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
711         
712         int endIndex = 0;        
713         if (m_activeSelectionEndIndex < 0) {
714             // Initialize the end index
715             if (keyIdentifier == "Down")
716                 endIndex = nextSelectableListIndex(lastSelectedListIndex());
717             else if (keyIdentifier == "Up")
718                 endIndex = previousSelectableListIndex(optionToListIndex(selectedIndex()));
719         } else {
720             // Set the end index based on the current end index
721             if (keyIdentifier == "Down")
722                 endIndex = nextSelectableListIndex(m_activeSelectionEndIndex);
723             else if (keyIdentifier == "Up")
724                 endIndex = previousSelectableListIndex(m_activeSelectionEndIndex);    
725         }
726         
727         if (keyIdentifier == "Down" || keyIdentifier == "Up") {
728             ASSERT(endIndex >= 0 && (unsigned)endIndex < listItems().size()); 
729             setActiveSelectionEndIndex(endIndex);
730             
731             // 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.
732             bool deselectOthers = !multiple() || !static_cast<KeyboardEvent*>(evt)->shiftKey();
733             if (m_activeSelectionAnchorIndex < 0 || deselectOthers) {
734                 m_activeSelectionState = true;
735                 if (deselectOthers)
736                     deselectItems();
737                 setActiveSelectionAnchorIndex(m_activeSelectionEndIndex);
738             }
739
740             static_cast<RenderListBox*>(renderer())->scrollToRevealElementAtListIndex(endIndex);
741             updateListBoxSelection(deselectOthers);
742             renderer()->repaint();            
743             listBoxOnChange();
744             evt->setDefaultHandled();
745         }
746     }
747 }
748
749 void HTMLSelectElement::setActiveSelectionAnchorIndex(int index)
750 {
751     m_activeSelectionAnchorIndex = index;
752     
753     // Cache the selection state so we can restore the old selection as the new selection pivots around this anchor index
754     const Vector<HTMLElement*>& items = listItems();
755     m_cachedStateForActiveSelection.clear();
756     for (unsigned i = 0; i < items.size(); i++) {
757         if (items[i]->hasLocalName(optionTag)) {
758             HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]);
759             m_cachedStateForActiveSelection.append(option->selected());
760         } else
761             m_cachedStateForActiveSelection.append(false);
762     }
763 }
764
765 void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions)
766 {
767     ASSERT(renderer() && renderer()->isListBox());
768     
769     unsigned start;
770     unsigned end;
771     ASSERT(m_activeSelectionAnchorIndex >= 0);
772     start = min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
773     end = max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
774
775     const Vector<HTMLElement*>& items = listItems();
776     for (unsigned i = 0; i < items.size(); i++) {
777         if (items[i]->hasLocalName(optionTag)) {
778             HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]);
779             if (!option->disabled()) {
780                 if (i >= start && i <= end)
781                     option->m_selected = m_activeSelectionState;
782                 else if (deselectOtherOptions)
783                     option->m_selected = false;
784                 else
785                     option->m_selected = m_cachedStateForActiveSelection[i];
786             }
787         }
788     }
789
790     static_cast<RenderListBox*>(renderer())->setSelectionChanged(true);
791 }
792
793 void HTMLSelectElement::listBoxOnChange()
794 {
795     const Vector<HTMLElement*>& items = listItems();
796     
797     // If the cached selection list is empty, or the size has changed, then rebuild the list, fire onChange, and return early.
798     if (m_lastOnChangeSelection.isEmpty() || m_lastOnChangeSelection.size() != items.size()) {
799         m_lastOnChangeSelection.clear();
800         for (unsigned i = 0; i < items.size(); i++) {
801             if (items[i]->hasLocalName(optionTag)) {
802                 HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]);
803                 m_lastOnChangeSelection.append(option->selected());
804             } else
805                 m_lastOnChangeSelection.append(false);
806         }
807         onChange();
808         return;
809     }
810     
811     // Update m_lastOnChangeSelection and fire onChange
812     bool fireOnChange = false;
813     for (unsigned i = 0; i < items.size(); i++) {
814         bool selected = false;
815         if (items[i]->hasLocalName(optionTag))
816             selected = static_cast<HTMLOptionElement*>(items[i])->selected();
817         if (selected != m_lastOnChangeSelection[i])      
818             fireOnChange = true;
819         m_lastOnChangeSelection[i] = selected;
820     }
821     if (fireOnChange)
822         onChange();
823 }
824
825 static String stripLeadingWhiteSpace(const String& string)
826 {
827     int length = string.length();
828     int i;
829     for (i = 0; i < length; ++i)
830         if (string[i] != noBreakSpace &&
831             (string[i] <= 0x7F ? !isspace(string[i]) : (direction(string[i]) != WhiteSpaceNeutral)))
832             break;
833
834     return string.substring(i, length - i);
835 }
836
837 void HTMLSelectElement::typeAheadFind(KeyboardEvent* event)
838 {
839     if (event->timeStamp() < m_lastCharTime)
840         return;
841
842     DOMTimeStamp delta = event->timeStamp() - m_lastCharTime;
843
844     m_lastCharTime = event->timeStamp();
845
846     UChar c = event->charCode();
847
848     String prefix;
849     int searchStartOffset = 1;
850     if (delta > typeAheadTimeout) {
851         m_typedString = prefix = String(&c, 1);
852         m_repeatingChar = c;
853     } else {
854         m_typedString.append(c);
855
856         if (c == m_repeatingChar)
857             // The user is likely trying to cycle through all the items starting with this character, so just search on the character
858             prefix = String(&c, 1);
859         else {
860             m_repeatingChar = 0;
861             prefix = m_typedString;
862             searchStartOffset = 0;
863         }
864     }
865
866     const Vector<HTMLElement*>& items = listItems();
867     int itemCount = items.size();
868
869     int index = (optionToListIndex(selectedIndex()) + searchStartOffset) % itemCount;
870     for (int i = 0; i < itemCount; i++, index = (index + 1) % itemCount) {
871         if (!items[index]->hasTagName(optionTag) || items[index]->disabled())
872             continue;
873
874         if (stripLeadingWhiteSpace(static_cast<HTMLOptionElement*>(items[index])->optionText()).startsWith(prefix, false)) {
875             setSelectedIndex(listToOptionIndex(index));
876             setChanged();
877             return;
878         }
879     }
880 }
881
882 int HTMLSelectElement::nextSelectableListIndex(int startIndex)
883 {
884     const Vector<HTMLElement*>& items = listItems();
885     int index = startIndex + 1;
886     while (index >= 0 && (unsigned)index < items.size() && (!items[index]->hasLocalName(optionTag) || items[index]->disabled()))
887         index++;
888     if ((unsigned) index == items.size())
889         return startIndex;
890     return index;
891 }
892
893 int HTMLSelectElement::previousSelectableListIndex(int startIndex)
894 {
895     const Vector<HTMLElement*>& items = listItems();
896     if (startIndex == -1)
897         startIndex = items.size();
898     int index = startIndex - 1;
899     while (index >= 0 && (unsigned)index < items.size() && (!items[index]->hasLocalName(optionTag) || items[index]->disabled()))
900         index--;
901     if (index == -1)
902         return startIndex;
903     return index;
904 }
905
906 void HTMLSelectElement::accessKeyAction(bool sendToAnyElement)
907 {
908     // send the mouse button events iff the caller specified sendToAnyElement
909     dispatchSimulatedClick(0, sendToAnyElement);
910 }
911
912 void HTMLSelectElement::setMultiple(bool multiple)
913 {
914     setAttribute(multipleAttr, multiple ? "" : 0);
915 }
916
917 void HTMLSelectElement::setSize(int size)
918 {
919     setAttribute(sizeAttr, String::number(size));
920 }
921
922 Node* HTMLSelectElement::namedItem(const String &name, bool caseSensitive)
923 {
924     return options()->namedItem(name, caseSensitive);
925 }
926
927 Node* HTMLSelectElement::item(unsigned index)
928 {
929     return options()->item(index);
930 }
931
932 void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec)
933 {
934     ec = 0;
935     if (index > INT_MAX)
936         index = INT_MAX;
937     int diff = index  - length();
938     HTMLElement* before = 0;
939     // out of array bounds ? first insert empty dummies
940     if (diff > 0) {
941         setLength(index, ec);
942         // replace an existing entry ?
943     } else if (diff < 0) {
944         before = static_cast<HTMLElement*>(options()->item(index+1));
945         remove(index);
946     }
947     // finally add the new element
948     if (ec == 0) {
949         add(option, before, ec);
950         if (diff >= 0 && option->selected())
951             setSelectedIndex(index, !m_multiple);
952     }
953 }
954
955 void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec)
956 {
957     ec = 0;
958     if (newLen > INT_MAX)
959         newLen = INT_MAX;
960     int diff = length() - newLen;
961
962     if (diff < 0) { // add dummy elements
963         do {
964             RefPtr<Element> option = ownerDocument()->createElement("option", ec);
965             if (!option)
966                 break;
967             add(static_cast<HTMLElement*>(option.get()), 0, ec);
968             if (ec)
969                 break;
970         } while (++diff);
971     }
972     else // remove elements
973         while (diff-- > 0)
974             remove(newLen);
975 }
976
977 } // namespace