Reviewed by Adele.
[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 "CSSPropertyNames.h"
31 #include "Document.h"
32 #include "DeprecatedRenderSelect.h"
33 #include "Event.h"
34 #include "EventNames.h"
35 #include "FormDataList.h"
36 #include "Frame.h"
37 #include "HTMLFormElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLOptionElement.h"
40 #include "HTMLOptionsCollection.h"
41 #include "KeyboardEvent.h"
42 #include "MouseEvent.h"
43 #include "RenderListBox.h"
44 #include "RenderMenuList.h"
45 #include "cssstyleselector.h"
46 #include <wtf/Vector.h>
47 #include <math.h>
48
49 #if PLATFORM(MAC)
50 #define ARROW_KEYS_POP_MENU 1
51 #else
52 #define ARROW_KEYS_POP_MENU 0
53 #endif
54
55 using namespace std;
56
57 namespace WebCore {
58
59 using namespace EventNames;
60 using namespace HTMLNames;
61
62 HTMLSelectElement::HTMLSelectElement(Document* doc, HTMLFormElement* f)
63     : HTMLGenericFormElement(selectTag, doc, f)
64     , m_minwidth(0)
65     , m_size(0)
66     , m_multiple(false)
67     , m_recalcListItems(false)
68     , m_lastOnChangeIndex(0)
69 {
70     document()->registerFormElementWithState(this);
71 }
72
73 HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
74     : HTMLGenericFormElement(tagName, doc, f), m_minwidth(0), m_size(0), m_multiple(false), m_recalcListItems(false)
75 {
76     document()->registerFormElementWithState(this);
77 }
78
79 HTMLSelectElement::~HTMLSelectElement()
80 {
81     document()->deregisterFormElementWithState(this);
82 }
83
84 bool HTMLSelectElement::checkDTD(const Node* newChild)
85 {
86     return newChild->isTextNode() || newChild->hasTagName(optionTag) || newChild->hasTagName(optgroupTag) || newChild->hasTagName(hrTag) ||
87            newChild->hasTagName(scriptTag);
88 }
89
90 void HTMLSelectElement::recalcStyle( StyleChange ch )
91 {
92     if (hasChangedChild() && renderer()) {
93         if (usesMenuList())
94             static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true);
95         else if (renderer() && renderer()->isListBox())
96             static_cast<RenderListBox*>(renderer())->setOptionsChanged(true);
97         else
98             static_cast<DeprecatedRenderSelect*>(renderer())->setOptionsChanged(true);
99     }
100
101     HTMLGenericFormElement::recalcStyle( ch );
102 }
103
104 const AtomicString& HTMLSelectElement::type() const
105 {
106     static const AtomicString selectMultiple("select-multiple");
107     static const AtomicString selectOne("select-one");
108     return m_multiple ? selectMultiple : selectOne;
109 }
110
111 int HTMLSelectElement::selectedIndex() const
112 {
113     // return the number of the first option selected
114     unsigned index = 0;
115     const Vector<HTMLElement*>& items = listItems();
116     for (unsigned int i = 0; i < items.size(); i++) {
117         if (items[i]->hasLocalName(optionTag)) {
118             if (static_cast<HTMLOptionElement*>(items[i])->selected())
119                 return index;
120             index++;
121         }
122     }
123     return -1;
124 }
125
126 int HTMLSelectElement::lastSelectedListIndex() const
127 {
128     // return the number of the last option selected
129     unsigned index = 0;
130     bool found = false;
131     const Vector<HTMLElement*>& items = listItems();
132     for (unsigned int i = 0; i < items.size(); i++) {
133         if (items[i]->hasLocalName(optionTag)) {
134             if (static_cast<HTMLOptionElement*>(items[i])->selected()) {
135                 index = i;
136                 found = true;
137             }
138         }
139     }
140     return found ? (int) index : -1;
141 }
142
143 void HTMLSelectElement::deselectItems(HTMLOptionElement* excludeElement)
144 {
145     const Vector<HTMLElement*>& items = listItems();
146     unsigned i;
147     for (i = 0; i < items.size(); i++) {
148         if (items[i]->hasLocalName(optionTag) && (items[i] != excludeElement)) {
149             HTMLOptionElement* element = static_cast<HTMLOptionElement*>(items[i]);
150             element->m_selected = false;
151             element->setChanged();
152         }
153     }
154 }
155
156 void HTMLSelectElement::setSelectedIndex(int index, bool deselect, bool fireOnChange)
157 {
158     const Vector<HTMLElement*>& items = listItems();
159     int listIndex = optionToListIndex(index);
160     HTMLOptionElement* element = 0;
161     if (listIndex >= 0) {
162         element = static_cast<HTMLOptionElement*>(items[listIndex]);
163         element->setSelected(true);
164     }
165     if (deselect)
166         deselectItems(element);
167     if (fireOnChange) {
168         m_lastOnChangeIndex = index;
169         onChange();
170     }
171 }
172
173 int HTMLSelectElement::length() const
174 {
175     int len = 0;
176     unsigned i;
177     const Vector<HTMLElement*>& items = listItems();
178     for (i = 0; i < items.size(); i++) {
179         if (items[i]->hasLocalName(optionTag))
180             len++;
181     }
182     return len;
183 }
184
185 void HTMLSelectElement::add( HTMLElement *element, HTMLElement *before, ExceptionCode& ec)
186 {
187     RefPtr<HTMLElement> protectNewChild(element); // make sure the element is ref'd and deref'd so we don't leak it
188
189     if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag)))
190         return;
191
192     insertBefore(element, before, ec);
193     if (!ec)
194         setRecalcListItems();
195 }
196
197 void HTMLSelectElement::remove(int index)
198 {
199     ExceptionCode ec = 0;
200     int listIndex = optionToListIndex(index);
201
202     const Vector<HTMLElement*>& items = listItems();
203     if (listIndex < 0 || index >= int(items.size()))
204         return; // ### what should we do ? remove the last item?
205
206     removeChild(items[listIndex], ec);
207     if (!ec)
208         setRecalcListItems();
209 }
210
211 String HTMLSelectElement::value()
212 {
213     unsigned i;
214     const Vector<HTMLElement*>& items = listItems();
215     for (i = 0; i < items.size(); i++) {
216         if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->selected())
217             return static_cast<HTMLOptionElement*>(items[i])->value();
218     }
219     return String("");
220 }
221
222 void HTMLSelectElement::setValue(const String &value)
223 {
224     if (value.isNull())
225         return;
226     // find the option with value() matching the given parameter
227     // and make it the current selection.
228     const Vector<HTMLElement*>& items = listItems();
229     for (unsigned i = 0; i < items.size(); i++)
230         if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->value() == value) {
231             static_cast<HTMLOptionElement*>(items[i])->setSelected(true);
232             return;
233         }
234 }
235
236 String HTMLSelectElement::stateValue() const
237 {
238     const Vector<HTMLElement*>& items = listItems();
239     int l = items.size();
240     Vector<char, 1024> characters(l);
241     for (int i = 0; i < l; ++i) {
242         HTMLElement* e = items[i];
243         bool selected = e->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(e)->selected();
244         characters[i] = selected ? 'X' : '.';
245     }
246     return String(characters, l);
247 }
248
249 void HTMLSelectElement::restoreState(const String& state)
250 {
251     recalcListItems();
252
253     const Vector<HTMLElement*>& items = listItems();
254     int l = items.size();
255     for (int i = 0; i < l; i++)
256         if (items[i]->hasLocalName(optionTag))
257             static_cast<HTMLOptionElement*>(items[i])->setSelected(state[i] == 'X');
258     setChanged(true);
259 }
260
261 bool HTMLSelectElement::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec)
262 {
263     bool result = HTMLGenericFormElement::insertBefore(newChild, refChild, ec);
264     if (result)
265         setRecalcListItems();
266     return result;
267 }
268
269 bool HTMLSelectElement::replaceChild(PassRefPtr<Node> newChild, Node *oldChild, ExceptionCode& ec)
270 {
271     bool result = HTMLGenericFormElement::replaceChild(newChild, oldChild, ec);
272     if (result)
273         setRecalcListItems();
274     return result;
275 }
276
277 bool HTMLSelectElement::removeChild(Node* oldChild, ExceptionCode& ec)
278 {
279     bool result = HTMLGenericFormElement::removeChild(oldChild, ec);
280     if (result)
281         setRecalcListItems();
282     return result;
283 }
284
285 bool HTMLSelectElement::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec)
286 {
287     bool result = HTMLGenericFormElement::appendChild(newChild, ec);
288     if (result)
289         setRecalcListItems();
290     return result;
291 }
292
293 ContainerNode* HTMLSelectElement::addChild(PassRefPtr<Node> newChild)
294 {
295     ContainerNode* result = HTMLGenericFormElement::addChild(newChild);
296     if (result)
297         setRecalcListItems();
298     return result;
299 }
300
301 void HTMLSelectElement::parseMappedAttribute(MappedAttribute *attr)
302 {
303     bool oldUsesMenuList = usesMenuList();
304     if (attr->name() == sizeAttr) {
305         m_size = max(attr->value().toInt(), 1);
306         if (oldUsesMenuList != usesMenuList() && attached()) {
307             detach();
308             attach();
309         }
310     } else if (attr->name() == widthAttr) {
311         m_minwidth = max(attr->value().toInt(), 0);
312     } else if (attr->name() == multipleAttr) {
313         m_multiple = (!attr->isNull());
314         if (oldUsesMenuList != usesMenuList() && attached()) {
315             detach();
316             attach();
317         }
318     } else if (attr->name() == accesskeyAttr) {
319         // FIXME: ignore for the moment
320     } else if (attr->name() == onfocusAttr) {
321         setHTMLEventListener(focusEvent, attr);
322     } else if (attr->name() == onblurAttr) {
323         setHTMLEventListener(blurEvent, attr);
324     } else if (attr->name() == onchangeAttr) {
325         setHTMLEventListener(changeEvent, attr);
326     } else
327         HTMLGenericFormElement::parseMappedAttribute(attr);
328 }
329
330 bool HTMLSelectElement::isKeyboardFocusable() const
331 {
332     if (renderer() && (usesMenuList() || renderer()->isListBox()))
333         return isFocusable();
334     return HTMLGenericFormElement::isKeyboardFocusable();
335 }
336
337 bool HTMLSelectElement::isMouseFocusable() const
338 {
339     if (renderer() && (usesMenuList() || renderer()->isListBox()))
340         return isFocusable();
341     return HTMLGenericFormElement::isMouseFocusable();
342 }
343
344 RenderObject *HTMLSelectElement::createRenderer(RenderArena *arena, RenderStyle *style)
345 {
346     if (usesMenuList())
347         return new (arena) RenderMenuList(this);
348 #if PLATFORM(MAC)
349     // FIXME: Remove this when DeprecatedRenderSelect is no longer needed.
350     if (style->appearance() == ListboxAppearance)
351         return new (arena) RenderListBox(this);
352     return new (arena) DeprecatedRenderSelect(this);
353 #endif
354     return new (arena) RenderListBox(this);
355 }
356
357 bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
358 {
359     bool successful = false;
360     const Vector<HTMLElement*>& items = listItems();
361
362     unsigned i;
363     for (i = 0; i < items.size(); i++) {
364         if (items[i]->hasLocalName(optionTag)) {
365             HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]);
366             if (option->selected()) {
367                 list.appendData(name(), option->value());
368                 successful = true;
369             }
370         }
371     }
372
373     // ### this case should not happen. make sure that we select the first option
374     // in any case. otherwise we have no consistency with the DOM interface. FIXME!
375     // we return the first one if it was a combobox select
376     if (!successful && !m_multiple && m_size <= 1 && items.size() &&
377         (items[0]->hasLocalName(optionTag))) {
378         HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[0]);
379         if (option->value().isNull())
380             list.appendData(name(), option->text().stripWhiteSpace());
381         else
382             list.appendData(name(), option->value());
383         successful = true;
384     }
385
386     return successful;
387 }
388
389 int HTMLSelectElement::optionToListIndex(int optionIndex) const
390 {
391     const Vector<HTMLElement*>& items = listItems();
392     if (optionIndex < 0 || optionIndex >= int(items.size()))
393         return -1;
394
395     int listIndex = 0;
396     int optionIndex2 = 0;
397     for (;
398          optionIndex2 < int(items.size()) && optionIndex2 <= optionIndex;
399          listIndex++) { // not a typo!
400         if (items[listIndex]->hasLocalName(optionTag))
401             optionIndex2++;
402     }
403     listIndex--;
404     return listIndex;
405 }
406
407 int HTMLSelectElement::listToOptionIndex(int listIndex) const
408 {
409     const Vector<HTMLElement*>& items = listItems();
410     if (listIndex < 0 || listIndex >= int(items.size()) ||
411         !items[listIndex]->hasLocalName(optionTag))
412         return -1;
413
414     int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list
415     for (int i = 0; i < listIndex; i++)
416         if (items[i]->hasLocalName(optionTag))
417             optionIndex++;
418     return optionIndex;
419 }
420
421 PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options()
422 {
423     return new HTMLOptionsCollection(this);
424 }
425
426 void HTMLSelectElement::recalcListItems() const
427 {
428     Node* current = firstChild();
429     m_listItems.clear();
430     HTMLOptionElement* foundSelected = 0;
431     while (current) {
432         if (current->hasTagName(optgroupTag) && current->firstChild()) {
433             // ### what if optgroup contains just comments? don't want one of no options in it...
434             m_listItems.append(static_cast<HTMLElement*>(current));
435             current = current->firstChild();
436         }
437         if (current->hasTagName(optionTag)) {
438             m_listItems.append(static_cast<HTMLElement*>(current));
439             if (!foundSelected && (usesMenuList() || (!m_multiple && static_cast<HTMLOptionElement*>(current)->selected()))) {
440                 foundSelected = static_cast<HTMLOptionElement*>(current);
441                 foundSelected->m_selected = true;
442             } else if (foundSelected && !m_multiple && static_cast<HTMLOptionElement*>(current)->selected()) {
443                 foundSelected->m_selected = false;
444                 foundSelected = static_cast<HTMLOptionElement*>(current);
445             }
446         }
447         if (current->hasTagName(hrTag))
448             m_listItems.append(static_cast<HTMLElement*>(current));
449
450         Node* parent = current->parentNode();
451         current = current->nextSibling();
452         if (!current) {
453             if (parent != this)
454                 current = parent->nextSibling();
455         }
456     }
457     m_recalcListItems = false;
458 }
459
460 void HTMLSelectElement::childrenChanged()
461 {
462     setRecalcListItems();
463
464     HTMLGenericFormElement::childrenChanged();
465 }
466
467 void HTMLSelectElement::setRecalcListItems()
468 {
469     m_recalcListItems = true;
470     if (renderer()) {
471         if (usesMenuList())
472             static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true);
473         else if (renderer() && renderer()->isListBox())
474             static_cast<RenderListBox*>(renderer())->setOptionsChanged(true);
475         else
476             static_cast<DeprecatedRenderSelect*>(renderer())->setOptionsChanged(true);
477     }
478     setChanged();
479 }
480
481 void HTMLSelectElement::reset()
482 {
483     bool optionSelected = false;
484     HTMLOptionElement* firstOption = 0;
485     const Vector<HTMLElement*>& items = listItems();
486     unsigned i;
487     for (i = 0; i < items.size(); i++) {
488         if (items[i]->hasLocalName(optionTag)) {
489             HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]);
490             if (!option->getAttribute(selectedAttr).isNull()) {
491                 option->setSelected(true);
492                 optionSelected = true;
493             } else
494                 option->setSelected(false);
495             if (!firstOption)
496                 firstOption = option;
497         }
498     }
499     if (!optionSelected && firstOption)
500         firstOption->setSelected(true);
501     setChanged(true);
502 }
503
504 void HTMLSelectElement::notifyOptionSelected(HTMLOptionElement *selectedOption, bool selected)
505  {
506     if (selected && !m_multiple)
507         deselectItems(selectedOption);
508
509     if (renderer() && !usesMenuList()) {
510         if (renderer()->isListBox())
511             static_cast<RenderListBox*>(renderer())->setSelectionChanged(true);
512         else
513             static_cast<DeprecatedRenderSelect*>(renderer())->setSelectionChanged(true);
514     }
515  
516     setChanged(true);
517 }
518
519 void HTMLSelectElement::dispatchBlurEvent()
520 {
521 #if !ARROW_KEYS_POP_MENU
522     if (selectedIndex() != m_lastOnChangeIndex) {
523         m_lastOnChangeIndex = selectedIndex();
524         onChange();
525     }
526 #endif
527     HTMLGenericFormElement::dispatchBlurEvent();
528 }
529 void HTMLSelectElement::defaultEventHandler(Event* evt)
530 {
531     if (usesMenuList())
532         menuListDefaultEventHandler(evt);
533     else if (renderer() && renderer()->isListBox() && renderer()->isListBox()) 
534         listBoxDefaultEventHandler(evt);
535     HTMLGenericFormElement::defaultEventHandler(evt);
536 }
537
538 void HTMLSelectElement::menuListDefaultEventHandler(Event* evt)
539 {
540     RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer());
541
542     // Use key press event here since sending simulated mouse events
543     // on key down blocks the proper sending of the key press event.
544     if (evt->type() == keypressEvent) {
545         if (!renderer() || !evt->isKeyboardEvent())
546             return;
547         String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
548         bool handled = false;
549 #if ARROW_KEYS_POP_MENU
550         if (form() && keyIdentifier == "Enter") {
551             blur();
552             // Make sure the form hasn't been destroyed during the blur.
553             if (form())
554                 form()->submitClick();
555             handled = true;
556         }
557         if ((keyIdentifier == "Down" || keyIdentifier == "Up" || keyIdentifier == "U+000020") && renderer() && usesMenuList()) {
558             focus();
559             menuList->showPopup();
560             handled = true;
561         }
562 #else
563         int index = optionToListIndex(selectedIndex());
564         if (keyIdentifier == "Down" || keyIdentifier == "Right") {
565             if (index < listItems().size() - 1)
566                 setSelectedIndex(++index);
567             handled = true;
568         } else if (keyIdentifier == "Up" || keyIdentifier == "Left") {
569             if (index > 0)
570                 setSelectedIndex(--index);
571             handled = true;
572         } else if (keyIdentifier == "Enter" && index != m_lastOnChangeIndex) {
573             setSelectedIndex(index, true, true);
574         }
575
576 #endif
577         if (handled)
578             evt->setDefaultHandled();
579
580     }
581     if (evt->type() == mousedownEvent) {
582          focus();
583         if (menuList->popupIsVisible())
584             menuList->hidePopup();
585         else
586             menuList->showPopup();
587         evt->setDefaultHandled();
588     }
589 }
590
591 void HTMLSelectElement::listBoxDefaultEventHandler(Event* evt)
592 {
593     if (evt->type() == mousedownEvent) {
594         MouseEvent* mEvt = static_cast<MouseEvent*>(evt);
595         if (HTMLOptionElement* element = static_cast<RenderListBox*>(renderer())->optionAtPoint(mEvt->x(), mEvt->y())) {
596             bool deselectOtherOptions = true;
597             bool shouldSelect = true;
598             
599             bool multiSelectKeyPressed = false;
600 #if PLATFORM(MAC)
601             multiSelectKeyPressed = mEvt->metaKey();
602 #else
603             multiSelectKeyPressed = mEvt->ctrlKey();
604 #endif
605             if (multiple() && multiSelectKeyPressed)
606                 deselectOtherOptions = false;
607             if (element->selected() && multiSelectKeyPressed)
608                 shouldSelect = false;
609             
610             int optionIndex = element->index();
611             if (!shouldSelect) {
612                 optionIndex = -1;
613                 element->m_selected = false;
614             }
615             setSelectedIndex(optionIndex, deselectOtherOptions);
616         }
617     }
618     
619     if (evt->type() == keypressEvent) {
620         if (!evt->isKeyboardEvent())
621             return;
622         String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
623         
624         int index = 0;
625         const Vector<HTMLElement*>& items = listItems();
626
627         if (keyIdentifier == "Down") {
628             index = nextSelectableListIndex(lastSelectedListIndex());
629             
630         } else if (keyIdentifier == "Up") {
631             index = previousSelectableListIndex(optionToListIndex(selectedIndex()));
632         }
633         if (keyIdentifier == "Down" || keyIdentifier == "Up") {
634             ASSERT(index >= 0 && (unsigned)index < items.size()); 
635             HTMLOptionElement* element = static_cast<HTMLOptionElement*>(items[index]);
636             
637             setSelectedIndex(element->index(), !multiple() || !static_cast<KeyboardEvent*>(evt)->shiftKey());
638             static_cast<RenderListBox*>(renderer())->scrollToRevealElementAtListIndex(index);
639             
640             evt->setDefaultHandled();
641             setChanged();
642             renderer()->repaint();
643         }
644     }
645 }
646
647 int HTMLSelectElement::nextSelectableListIndex(int startIndex)
648 {
649     const Vector<HTMLElement*>& items = listItems();
650     int index = startIndex + 1;
651     while (index >= 0 && (unsigned)index < items.size() && (!items[index]->hasLocalName(optionTag) || items[index]->disabled()))
652         index++;
653     if ((unsigned) index == items.size())
654         return startIndex;
655     return index;
656 }
657
658 int HTMLSelectElement::previousSelectableListIndex(int startIndex)
659 {
660     const Vector<HTMLElement*>& items = listItems();
661     if (startIndex == -1)
662         startIndex = items.size();
663     int index = startIndex - 1;
664     while (index >= 0 && (unsigned)index < items.size() && (!items[index]->hasLocalName(optionTag) || items[index]->disabled()))
665         index--;
666     if (index == -1)
667         return startIndex;
668     return index;
669 }
670
671 void HTMLSelectElement::accessKeyAction(bool sendToAnyElement)
672 {
673     // send the mouse button events iff the
674     // caller specified sendToAnyElement
675     click(sendToAnyElement);
676 }
677
678 void HTMLSelectElement::setMultiple(bool multiple)
679 {
680     setAttribute(multipleAttr, multiple ? "" : 0);
681 }
682
683 void HTMLSelectElement::setSize(int size)
684 {
685     setAttribute(sizeAttr, String::number(size));
686 }
687
688 Node* HTMLSelectElement::namedItem(const String &name, bool caseSensitive)
689 {
690     return (options()->namedItem(name, caseSensitive));
691 }
692
693 void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec)
694 {
695     ec = 0;
696     if (index > INT_MAX)
697         index = INT_MAX;
698     int diff = index  - length();
699     HTMLElement* before = 0;
700     // out of array bounds ? first insert empty dummies
701     if (diff > 0) {
702         setLength(index, ec);
703         // replace an existing entry ?
704     } else if (diff < 0) {
705         before = static_cast<HTMLElement*>(options()->item(index+1));
706         remove(index);
707     }
708     // finally add the new element
709     if (ec == 0) {
710         add(option, before, ec);
711         if (diff >= 0 && option->selected())
712             setSelectedIndex(index, !m_multiple);
713     }
714 }
715
716 void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec)
717 {
718     ec = 0;
719     if (newLen > INT_MAX)
720         newLen = INT_MAX;
721     int diff = length() - newLen;
722
723     if (diff < 0) { // add dummy elements
724         do {
725             RefPtr<Element> option = ownerDocument()->createElement("option", ec);
726             if (!option)
727                 break;
728             add(static_cast<HTMLElement*>(option.get()), 0, ec);
729             if (ec)
730                 break;
731         } while (++diff);
732     }
733     else // remove elements
734         while (diff-- > 0)
735             remove(newLen);
736 }
737
738 } // namespace