LayoutTests:
[WebKit-https.git] / WebCore / rendering / render_form.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) 2000 Dirk Mueller (mueller@kde.org)
7  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  *
24  */
25
26 #include "config.h"
27 #include "render_form.h"
28
29 #include "BrowserExtension.h"
30 #include "EventNames.h"
31 #include "FrameView.h"
32 #include "HTMLFormElement.h"
33 #include "HTMLInputElement.h"
34 #include "HTMLOptGroupElement.h"
35 #include "HTMLOptionElement.h"
36 #include "HTMLSelectElement.h"
37 #include "HTMLTextAreaElement.h"
38 #include "KWQComboBox.h"
39 #include "KWQFileButton.h"
40 #include "KWQLineEdit.h"
41 #include "KWQSlider.h"
42 #include "KWQTextEdit.h"
43 #include "PlatformMouseEvent.h"
44 #include "dom2_eventsimpl.h"
45
46 using namespace std;
47
48 namespace WebCore {
49
50 using namespace EventNames;
51 using namespace HTMLNames;
52
53 RenderFormElement::RenderFormElement(HTMLGenericFormElement *element)
54     : RenderWidget(element)
55 {
56     setInline(true);
57 }
58
59 RenderFormElement::~RenderFormElement()
60 {
61 }
62
63 short RenderFormElement::baselinePosition( bool f, bool isRootLineBox ) const
64 {
65     return marginTop() + widget()->baselinePosition(m_height);
66 }
67
68 void RenderFormElement::setStyle(RenderStyle* s)
69 {
70     if (canHaveIntrinsicMargins())
71         addIntrinsicMarginsIfAllowed(s);
72
73     RenderWidget::setStyle(s);
74
75     // Do not paint a background or border for Aqua form elements
76     setShouldPaintBackgroundOrBorder(false);
77
78     m_widget->setFont(style()->font());
79 }
80
81 void RenderFormElement::updateFromElement()
82 {
83     m_widget->setEnabled(!static_cast<HTMLGenericFormElement*>(node())->disabled());
84 }
85
86 void RenderFormElement::layout()
87 {
88     KHTMLAssert( needsLayout() );
89     KHTMLAssert( minMaxKnown() );
90
91     // minimum height
92     m_height = 0;
93
94     calcWidth();
95     calcHeight();
96     
97     setNeedsLayout(false);
98 }
99
100 void RenderFormElement::clicked(Widget*)
101 {
102     RenderArena* arena = ref();
103     PlatformMouseEvent event; // gets "current event"
104     if (node())
105         static_cast<EventTargetNode*>(node())->dispatchMouseEvent(event, clickEvent, event.clickCount());
106     deref(arena);
107 }
108
109 HorizontalAlignment RenderFormElement::textAlignment() const
110 {
111     switch (style()->textAlign()) {
112         case LEFT:
113         case KHTML_LEFT:
114             return AlignLeft;
115         case RIGHT:
116         case KHTML_RIGHT:
117             return AlignRight;
118         case CENTER:
119         case KHTML_CENTER:
120             return AlignHCenter;
121         case JUSTIFY:
122             // Just fall into the auto code for justify.
123         case TAAUTO:
124             return style()->direction() == RTL ? AlignRight : AlignLeft;
125     }
126     ASSERT(false); // Should never be reached.
127     return AlignLeft;
128 }
129
130
131 void RenderFormElement::addIntrinsicMarginsIfAllowed(RenderStyle* _style)
132 {
133     // Cut out the intrinsic margins completely if we end up using mini controls.
134     if (_style->fontSize() < 11)
135         return;
136     
137     // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
138     int m = intrinsicMargin();
139     if (_style->width().isIntrinsicOrAuto()) {
140         if (_style->marginLeft().quirk())
141             _style->setMarginLeft(Length(m, Fixed));
142         if (_style->marginRight().quirk())
143             _style->setMarginRight(Length(m, Fixed));
144     }
145
146     if (_style->height().isAuto()) {
147         if (_style->marginTop().quirk())
148             _style->setMarginTop(Length(m, Fixed));
149         if (_style->marginBottom().quirk())
150             _style->setMarginBottom(Length(m, Fixed));
151     }
152 }
153
154 // -------------------------------------------------------------------------------
155
156 RenderImageButton::RenderImageButton(HTMLInputElement *element)
157     : RenderImage(element)
158 {
159     // ### support DOMActivate event when clicked
160 }
161
162 // -------------------------------------------------------------------------------
163
164
165 // -----------------------------------------------------------------------------
166
167 RenderLineEdit::RenderLineEdit(HTMLInputElement *element)
168     : RenderFormElement(element), m_updating(false)
169 {
170     QLineEdit::Type type;
171     switch (element->inputType()) {
172         case HTMLInputElement::PASSWORD:
173             type = QLineEdit::Password;
174             break;
175         case HTMLInputElement::SEARCH:
176             type = QLineEdit::Search;
177             break;
178         default:
179             type = QLineEdit::Normal;
180     }
181     QLineEdit* edit = new QLineEdit(type);
182     if (type == QLineEdit::Search)
183         edit->setLiveSearch(false);
184     setWidget(edit);
185 }
186
187 void RenderLineEdit::selectionChanged(Widget*)
188 {
189     // We only want to call onselect if there actually is a selection
190     QLineEdit* w = static_cast<QLineEdit*>(m_widget);
191     if (w->hasSelectedText())
192         static_cast<HTMLGenericFormElement*>(node())->onSelect();
193 }
194
195 void RenderLineEdit::returnPressed(Widget*)
196 {
197     // Emit onChange if necessary
198     // Works but might not be enough, dirk said he had another solution at
199     // hand (can't remember which) - David
200     if (isTextField() && isEdited()) {
201         static_cast<HTMLGenericFormElement*>(node())->onChange();
202         setEdited(false);
203     }
204
205     if (HTMLFormElement* fe = static_cast<HTMLGenericFormElement*>(node())->form())
206         fe->submitClick();
207 }
208
209 void RenderLineEdit::performSearch(Widget*)
210 {
211     // Fire the "search" DOM event.
212     static_cast<EventTargetNode*>(node())->dispatchHTMLEvent(searchEvent, true, false);
213 }
214
215 void RenderLineEdit::addSearchResult()
216 {
217     if (widget())
218         static_cast<QLineEdit*>(widget())->addSearchResult();
219 }
220
221 void RenderLineEdit::calcMinMaxWidth()
222 {
223     KHTMLAssert(!minMaxKnown());
224
225     // Let the widget tell us how big it wants to be.
226     m_updating = true;
227     int size = static_cast<HTMLInputElement*>(node())->size();
228     IntSize s(static_cast<QLineEdit*>(widget())->sizeForCharacterWidth(size > 0 ? size : 20));
229     m_updating = false;
230
231     setIntrinsicWidth(s.width());
232     setIntrinsicHeight(s.height());
233
234     RenderFormElement::calcMinMaxWidth();
235 }
236
237 void RenderLineEdit::setStyle(RenderStyle *s)
238 {
239     RenderFormElement::setStyle(s);
240
241     QLineEdit* w = static_cast<QLineEdit*>(widget());
242     w->setAlignment(textAlignment());
243     w->setWritingDirection(style()->direction() == RTL ? RTL : LTR);
244 }
245
246 void RenderLineEdit::updateFromElement()
247 {
248     HTMLInputElement* e = static_cast<HTMLInputElement*>(node());
249     QLineEdit* w = static_cast<QLineEdit*>(widget());
250     
251     int ml = e->maxLength();
252     if (ml <= 0 || ml > 1024)
253         ml = 1024;
254     if (w->maxLength() != ml)
255         w->setMaxLength(ml);
256
257     if (!e->valueMatchesRenderer()) {
258         String widgetText = w->text();
259         String newText = e->value();
260         newText.replace('\\', backslashAsCurrencySymbol());
261         if (widgetText != newText) {
262             int pos = w->cursorPosition();
263
264             m_updating = true;
265             w->setText(newText);
266             m_updating = false;
267             
268             w->setEdited( false );
269
270             w->setCursorPosition(pos);
271         }
272         e->setValueMatchesRenderer();
273     }
274
275     w->setReadOnly(e->isReadOnlyControl());
276     
277     // Handle updating the search attributes.
278     w->setPlaceholderString(e->getAttribute(placeholderAttr).deprecatedString());
279     if (w->type() == QLineEdit::Search) {
280         w->setLiveSearch(!e->getAttribute(incrementalAttr).isNull());
281         w->setAutoSaveName(e->getAttribute(autosaveAttr));
282         w->setMaxResults(e->maxResults());
283     }
284
285     w->setColors(style()->backgroundColor(), style()->color());
286
287     RenderFormElement::updateFromElement();
288 }
289
290 void RenderLineEdit::valueChanged(Widget*)
291 {
292     if (m_updating) // Don't alter the value if we are in the middle of initing the control, since
293         return;     // we are getting the value from the DOM and it's not user input.
294
295     String newText = static_cast<QLineEdit*>(widget())->text();
296
297     // A null string value is used to indicate that the form control has not altered the original
298     // default value.  That means that we should never use the null string value when the user
299     // empties a textfield, but should always force an empty textfield to use the empty string.
300     if (newText.isNull())
301         newText = "";
302
303     newText.replace(backslashAsCurrencySymbol(), '\\');
304     static_cast<HTMLInputElement*>(node())->setValueFromRenderer(newText);
305 }
306
307 int RenderLineEdit::selectionStart()
308 {
309     QLineEdit *lineEdit = static_cast<QLineEdit *>(m_widget);
310     int start = lineEdit->selectionStart();
311     if (start == -1)
312         start = lineEdit->cursorPosition();
313     return start;
314 }
315
316 int RenderLineEdit::selectionEnd()
317 {
318     QLineEdit *lineEdit = static_cast<QLineEdit *>(m_widget);
319     int start = lineEdit->selectionStart();
320     if (start == -1)
321         return lineEdit->cursorPosition();
322     return start + (int)lineEdit->selectedText().length();
323 }
324
325 void RenderLineEdit::setSelectionStart(int start)
326 {
327     int realStart = max(start, 0);
328     int length = max(selectionEnd() - realStart, 0);
329     static_cast<QLineEdit *>(m_widget)->setSelection(realStart, length);
330 }
331
332 void RenderLineEdit::setSelectionEnd(int end)
333 {
334     int start = selectionStart();
335     int realEnd = max(end, 0);
336     int length = realEnd - start;
337     if (length < 0) {
338         start = realEnd;
339         length = 0;
340     }
341     static_cast<QLineEdit *>(m_widget)->setSelection(start, length);
342 }
343
344 void RenderLineEdit::select()
345 {
346     static_cast<QLineEdit*>(m_widget)->selectAll();
347 }
348
349 bool RenderLineEdit::isEdited() const
350 {
351     return static_cast<QLineEdit*>(m_widget)->edited();
352 }
353 void RenderLineEdit::setEdited(bool x)
354 {
355     static_cast<QLineEdit*>(m_widget)->setEdited(x);
356 }
357
358 void RenderLineEdit::setSelectionRange(int start, int end)
359 {
360     int realStart = max(start, 0);
361     int length = max(end - realStart, 0);
362     static_cast<QLineEdit *>(m_widget)->setSelection(realStart, length);
363 }
364
365 // ---------------------------------------------------------------------------
366
367 RenderFieldset::RenderFieldset(HTMLGenericFormElement *element)
368 : RenderBlock(element)
369 {
370 }
371
372 RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren)
373 {
374     RenderObject* legend = findLegend();
375     if (legend) {
376         if (relayoutChildren)
377             legend->setNeedsLayout(true);
378         legend->layoutIfNeeded();
379
380         int xPos = borderLeft() + paddingLeft() + legend->marginLeft();
381         if (style()->direction() == RTL)
382             xPos = m_width - paddingRight() - borderRight() - legend->width() - legend->marginRight();
383         int b = borderTop();
384         int h = legend->height();
385         legend->setPos(xPos, max((b-h)/2, 0));
386         m_height = max(b,h) + paddingTop();
387     }
388     return legend;
389 }
390
391 RenderObject* RenderFieldset::findLegend()
392 {
393     for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) {
394       if (!legend->isFloatingOrPositioned() && legend->element() &&
395           legend->element()->hasTagName(legendTag))
396         return legend;
397     }
398     return 0;
399 }
400
401 void RenderFieldset::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
402 {
403     int w = width();
404     int h = height() + borderTopExtra() + borderBottomExtra();
405     RenderObject* legend = findLegend();
406     if (!legend)
407         return RenderBlock::paintBoxDecorations(i, _tx, _ty);
408
409     int yOff = (legend->yPos() > 0) ? 0 : (legend->height()-borderTop())/2;
410     h -= yOff;
411     _ty += yOff - borderTopExtra();
412
413     int my = max(_ty, i.r.y());
414     int end = min(i.r.bottom(),  _ty + h);
415     int mh = end - my;
416
417     paintBackground(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
418
419     if (style()->hasBorder())
420         paintBorderMinusLegend(i.p, _tx, _ty, w, h, style(), legend->xPos(), legend->width());
421 }
422
423 void RenderFieldset::paintBorderMinusLegend(GraphicsContext* p, int _tx, int _ty, int w, int h,
424                                             const RenderStyle* style, int lx, int lw)
425 {
426
427     const Color& tc = style->borderTopColor();
428     const Color& bc = style->borderBottomColor();
429
430     EBorderStyle ts = style->borderTopStyle();
431     EBorderStyle bs = style->borderBottomStyle();
432     EBorderStyle ls = style->borderLeftStyle();
433     EBorderStyle rs = style->borderRightStyle();
434
435     bool render_t = ts > BHIDDEN;
436     bool render_l = ls > BHIDDEN;
437     bool render_r = rs > BHIDDEN;
438     bool render_b = bs > BHIDDEN;
439
440     if(render_t) {
441         drawBorder(p, _tx, _ty, _tx + lx, _ty +  style->borderTopWidth(), BSTop, tc, style->color(), ts,
442                    (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0), 0);
443         drawBorder(p, _tx+lx+lw, _ty, _tx + w, _ty +  style->borderTopWidth(), BSTop, tc, style->color(), ts,
444                    0, (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
445     }
446
447     if(render_b)
448         drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
449                    (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0),
450                    (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
451
452     if(render_l)
453     {
454         const Color& lc = style->borderLeftColor();
455
456         bool ignore_top =
457             (tc == lc) &&
458             (ls >= OUTSET) &&
459             (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
460
461         bool ignore_bottom =
462             (bc == lc) &&
463             (ls >= OUTSET) &&
464             (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
465
466         drawBorder(p, _tx, _ty, _tx + style->borderLeftWidth(), _ty + h, BSLeft, lc, style->color(), ls,
467                    ignore_top?0:style->borderTopWidth(),
468                    ignore_bottom?0:style->borderBottomWidth());
469     }
470
471     if(render_r)
472     {
473         const Color& rc = style->borderRightColor();
474
475         bool ignore_top =
476             (tc == rc) &&
477             (rs >= DOTTED || rs == INSET) &&
478             (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
479
480         bool ignore_bottom =
481             (bc == rc) &&
482             (rs >= DOTTED || rs == INSET) &&
483             (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
484
485         drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
486                    ignore_top?0:style->borderTopWidth(),
487                    ignore_bottom?0:style->borderBottomWidth());
488     }
489 }
490
491 void RenderFieldset::setStyle(RenderStyle* _style)
492 {
493     RenderBlock::setStyle(_style);
494
495     // WinIE renders fieldsets with display:inline like they're inline-blocks.  For us,
496     // an inline-block is just a block element with replaced set to true and inline set
497     // to true.  Ensure that if we ended up being inline that we set our replaced flag
498     // so that we're treated like an inline-block.
499     if (isInline())
500         setReplaced(true);
501 }    
502     
503 // -------------------------------------------------------------------------
504
505 RenderFileButton::RenderFileButton(HTMLInputElement *element)
506     : RenderFormElement(element)
507 {
508     setWidget(new KWQFileButton(view()->frame()));
509 }
510
511 void RenderFileButton::calcMinMaxWidth()
512 {
513     KHTMLAssert(!minMaxKnown());
514
515     // Let the widget tell us how big it wants to be.
516     int size = static_cast<HTMLInputElement*>(node())->size();
517     IntSize s(static_cast<KWQFileButton *>(widget())->sizeForCharacterWidth(size > 0 ? size : 20));
518
519     setIntrinsicWidth(s.width());
520     setIntrinsicHeight(s.height());
521
522     RenderFormElement::calcMinMaxWidth();
523 }
524
525 void RenderFileButton::updateFromElement()
526 {
527     static_cast<KWQFileButton*>(widget())->setFilename(
528         static_cast<HTMLInputElement*>(node())->value().deprecatedString());
529
530     static_cast<KWQFileButton*>(widget())->setDisabled(
531         static_cast<HTMLInputElement*>(node())->disabled());
532
533     RenderFormElement::updateFromElement();
534 }
535
536 void RenderFileButton::returnPressed(Widget*)
537 {
538     if (static_cast<HTMLInputElement*>(node())->form())
539         static_cast<HTMLInputElement*>(node())->form()->prepareSubmit();
540 }
541
542 void RenderFileButton::valueChanged(Widget*)
543 {
544     static_cast<HTMLInputElement*>(node())->setValueFromRenderer(static_cast<KWQFileButton*>(widget())->filename());
545     static_cast<HTMLInputElement*>(node())->onChange();
546 }
547
548 void RenderFileButton::select()
549 {
550 }
551
552
553 void RenderFileButton::click(bool sendMouseEvents)
554 {
555     static_cast<KWQFileButton *>(widget())->click(sendMouseEvents);
556 }
557
558
559 // -------------------------------------------------------------------------
560
561 RenderLabel::RenderLabel(HTMLGenericFormElement *element)
562     : RenderFormElement(element)
563 {
564
565 }
566
567 // -------------------------------------------------------------------------
568
569 RenderLegend::RenderLegend(HTMLGenericFormElement *element)
570     : RenderBlock(element)
571 {
572 }
573
574 // -------------------------------------------------------------------------
575
576 RenderSelect::RenderSelect(HTMLSelectElement *element)
577     : RenderFormElement(element)
578 {
579     m_ignoreSelectEvents = false;
580     m_multiple = element->multiple();
581     m_size = element->size();
582     m_useListBox = (m_multiple || m_size > 1);
583     m_selectionChanged = true;
584     m_optionsChanged = true;
585
586     if (m_useListBox)
587         setWidget(createListBox());
588     else
589         setWidget(new QComboBox);
590 }
591
592
593 void RenderSelect::setWidgetWritingDirection()
594 {
595     TextDirection d = style()->direction() == RTL ? RTL : LTR;
596     if (m_useListBox)
597         static_cast<QListBox *>(m_widget)->setWritingDirection(d);
598     else
599         static_cast<QComboBox *>(m_widget)->setWritingDirection(d);
600 }
601
602 void RenderSelect::setStyle(RenderStyle *s)
603 {
604     RenderFormElement::setStyle(s);
605     setWidgetWritingDirection();
606 }
607
608
609 void RenderSelect::updateFromElement()
610 {
611     m_ignoreSelectEvents = true;
612
613     // change widget type
614     bool oldMultiple = m_multiple;
615     unsigned oldSize = m_size;
616     bool oldListbox = m_useListBox;
617
618     m_multiple = static_cast<HTMLSelectElement*>(node())->multiple();
619     m_size = static_cast<HTMLSelectElement*>(node())->size();
620     m_useListBox = (m_multiple || m_size > 1);
621
622     if (oldMultiple != m_multiple || oldSize != m_size) {
623         if (m_useListBox != oldListbox) {
624             // type of select has changed
625             delete m_widget;
626
627             if (m_useListBox)
628                 setWidget(createListBox());
629             else
630                 setWidget(new QComboBox);
631             setWidgetWritingDirection();
632         }
633
634         if (m_useListBox && oldMultiple != m_multiple) {
635             static_cast<QListBox*>(m_widget)->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
636         }
637         m_selectionChanged = true;
638         m_optionsChanged = true;
639     }
640
641     // update contents listbox/combobox based on options in m_element
642     if ( m_optionsChanged ) {
643         if (static_cast<HTMLSelectElement*>(node())->m_recalcListItems)
644             static_cast<HTMLSelectElement*>(node())->recalcListItems();
645         DeprecatedArray<HTMLElement*> listItems = static_cast<HTMLSelectElement*>(node())->listItems();
646         int listIndex;
647
648         if (m_useListBox)
649             static_cast<QListBox*>(m_widget)->clear();
650         else
651             static_cast<QComboBox*>(m_widget)->clear();
652
653         bool groupEnabled = true;
654         for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) {
655             if (listItems[listIndex]->hasTagName(optgroupTag)) {
656                 HTMLOptGroupElement *optgroupElement = static_cast<HTMLOptGroupElement*>(listItems[listIndex]);
657                 DeprecatedString label = optgroupElement->getAttribute(labelAttr).deprecatedString();
658                 label.replace('\\', backslashAsCurrencySymbol());
659                 
660                 // In WinIE, an optgroup can't start or end with whitespace (other than the indent
661                 // we give it).  We match this behavior.
662                 label = label.stripWhiteSpace();
663                 // We want to collapse our whitespace too.  This will match other browsers.
664                 label = label.simplifyWhiteSpace();
665
666                 groupEnabled = optgroupElement->isEnabled();
667                 
668                 if (m_useListBox)
669                     static_cast<QListBox*>(m_widget)->appendGroupLabel(label, groupEnabled);
670                 else
671                     static_cast<QComboBox*>(m_widget)->appendGroupLabel(label);
672             }
673             else if (listItems[listIndex]->hasTagName(optionTag)) {
674                 HTMLOptionElement *optionElement = static_cast<HTMLOptionElement*>(listItems[listIndex]);
675                 DeprecatedString itemText;
676                 if (optionElement->hasAttribute(labelAttr))
677                     itemText = optionElement->getAttribute(labelAttr).deprecatedString();
678                 else
679                     itemText = optionElement->text().deprecatedString();
680                 
681                 itemText.replace('\\', backslashAsCurrencySymbol());
682
683                 // In WinIE, leading and trailing whitespace is ignored in options. We match this behavior.
684                 itemText = itemText.stripWhiteSpace();
685                 // We want to collapse our whitespace too.  This will match other browsers.
686                 itemText = itemText.simplifyWhiteSpace();
687                 
688                 if (listItems[listIndex]->parentNode()->hasTagName(optgroupTag))
689                     itemText.prepend("    ");
690
691                 if (m_useListBox)
692                     static_cast<QListBox*>(m_widget)->appendItem(itemText, groupEnabled && optionElement->isEnabled());
693                 else
694                     static_cast<QComboBox*>(m_widget)->appendItem(itemText, groupEnabled && optionElement->isEnabled());
695             }
696             else if (listItems[listIndex]->hasTagName(hrTag)) {
697                 if (!m_useListBox)
698                     static_cast<QComboBox*>(m_widget)->appendSeparator();
699             }
700             else
701                 KHTMLAssert(false);
702             m_selectionChanged = true;
703         }
704         if (m_useListBox)
705             static_cast<QListBox*>(m_widget)->doneAppendingItems();
706         setNeedsLayoutAndMinMaxRecalc();
707         m_optionsChanged = false;
708     }
709
710     // update selection
711     if (m_selectionChanged) {
712         updateSelection();
713     }
714
715     m_ignoreSelectEvents = false;
716
717     RenderFormElement::updateFromElement();
718 }
719
720
721 short RenderSelect::baselinePosition( bool f, bool isRootLineBox ) const
722 {
723     if (m_useListBox) {
724         // FIXME: Should get the hardcoded constant of 7 by calling a QListBox function,
725         // as we do for other widget classes.
726         return RenderWidget::baselinePosition( f, isRootLineBox ) - 7;
727     }
728     return RenderFormElement::baselinePosition( f, isRootLineBox );
729 }
730
731
732 void RenderSelect::calcMinMaxWidth()
733 {
734     KHTMLAssert( !minMaxKnown() );
735
736     if (m_optionsChanged)
737         updateFromElement();
738
739     // ### ugly HACK FIXME!!!
740     setMinMaxKnown();
741     layoutIfNeeded();
742     setNeedsLayoutAndMinMaxRecalc();
743     // ### end FIXME
744
745     RenderFormElement::calcMinMaxWidth();
746 }
747
748 void RenderSelect::layout( )
749 {
750     KHTMLAssert(needsLayout());
751     KHTMLAssert(minMaxKnown());
752
753     // ### maintain selection properly between type/size changes, and work
754     // out how to handle multiselect->singleselect (probably just select
755     // first selected one)
756
757     // calculate size
758     if(m_useListBox) {
759         QListBox* w = static_cast<QListBox*>(m_widget);
760
761
762         int size = m_size;
763         // check if multiple and size was not given or invalid
764         // Internet Exploder sets size to min(number of elements, 4)
765         // Netscape seems to simply set it to "number of elements"
766         // the average of that is IMHO min(number of elements, 10)
767         // so I did that ;-)
768         if(size < 1)
769             size = min(static_cast<QListBox*>(m_widget)->count(), 10U);
770
771         // Let the widget tell us how big it wants to be.
772         IntSize s(w->sizeForNumberOfLines(size));
773         setIntrinsicWidth( s.width() );
774         setIntrinsicHeight( s.height() );
775     }
776     else {
777         IntSize s(m_widget->sizeHint());
778         setIntrinsicWidth( s.width() );
779         setIntrinsicHeight( s.height() );
780     }
781
782     RenderFormElement::layout();
783
784     // and now disable the widget in case there is no <option> given
785     DeprecatedArray<HTMLElement*> listItems = static_cast<HTMLSelectElement*>(node())->listItems();
786
787     bool foundOption = false;
788     for (unsigned i = 0; i < listItems.size() && !foundOption; i++)
789         foundOption = (listItems[i]->hasTagName(optionTag));
790
791     m_widget->setEnabled(foundOption && ! static_cast<HTMLSelectElement*>(node())->disabled());
792 }
793
794 void RenderSelect::valueChanged(Widget*)
795 {
796     if (m_ignoreSelectEvents)
797         return;
798
799     KHTMLAssert(!m_useListBox);
800
801     int index = static_cast<QComboBox*>(m_widget)->currentItem();
802
803     DeprecatedArray<HTMLElement*> listItems = static_cast<HTMLSelectElement*>(node())->listItems();
804     if (index >= 0 && index < (int)listItems.size()) {
805         bool found = listItems[index]->hasTagName(optionTag);
806         if (!found) {
807             // this one is not selectable,  we need to find an option element
808             while ((unsigned) index < listItems.size()) {
809                 if (listItems[index]->hasTagName(optionTag)) {
810                     found = true;
811                     break;
812                 }
813                 ++index;
814             }
815
816             if (!found) {
817                 while (index >= 0) {
818                     if (listItems[index]->hasTagName(optionTag)) {
819                         found = true;
820                         break;
821                     }
822                     --index;
823                 }
824             }
825         }
826
827         if (found) {
828             if ( index != static_cast<QComboBox*>(m_widget)->currentItem() )
829                 static_cast<QComboBox*>(m_widget)->setCurrentItem( index );
830
831             for (unsigned i = 0; i < listItems.size(); ++i)
832                 if (listItems[i]->hasTagName(optionTag) && i != (unsigned int) index)
833                     static_cast<HTMLOptionElement*>( listItems[i] )->m_selected = false;
834
835             static_cast<HTMLOptionElement*>(listItems[index])->m_selected = true;
836         }
837     }
838
839     static_cast<HTMLSelectElement*>(node())->onChange();
840 }
841
842
843 void RenderSelect::selectionChanged(Widget*)
844 {
845     if (m_ignoreSelectEvents)
846         return;
847
848     // don't use listItems() here as we have to avoid recalculations - changing the
849     // option list will make use update options not in the way the user expects them
850     DeprecatedArray<HTMLElement*> listItems = static_cast<HTMLSelectElement*>(node())->m_listItems;
851     int j = 0;
852     for (unsigned i = 0; i < listItems.count(); i++) {
853         // don't use setSelected() here because it will cause us to be called
854         // again with updateSelection.
855         if (listItems[i]->hasTagName(optionTag))
856             static_cast<HTMLOptionElement*>( listItems[i] )
857                 ->m_selected = static_cast<QListBox*>( m_widget )->isSelected( j );
858         if (listItems[i]->hasTagName(optionTag) || listItems[i]->hasTagName(optgroupTag))
859             ++j;
860     }
861     static_cast<HTMLSelectElement*>(node())->onChange();
862 }
863
864
865 void RenderSelect::setOptionsChanged(bool _optionsChanged)
866 {
867     m_optionsChanged = _optionsChanged;
868 }
869
870 QListBox* RenderSelect::createListBox()
871 {
872     QListBox *lb = new QListBox();
873     lb->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
874     m_ignoreSelectEvents = false;
875     return lb;
876 }
877
878 void RenderSelect::updateSelection()
879 {
880     DeprecatedArray<HTMLElement*> listItems = static_cast<HTMLSelectElement*>(node())->listItems();
881     int i;
882     if (m_useListBox) {
883         // if multi-select, we select only the new selected index
884         QListBox *listBox = static_cast<QListBox*>(m_widget);
885         int j = 0;
886         for (i = 0; i < int(listItems.size()); i++) {
887             listBox->setSelected(j, listItems[i]->hasTagName(optionTag) &&
888                                 static_cast<HTMLOptionElement*>(listItems[i])->selected());
889             if (listItems[i]->hasTagName(optionTag) || listItems[i]->hasTagName(optgroupTag))
890                 ++j;
891             
892         }
893     }
894     else {
895         bool found = false;
896         unsigned firstOption = listItems.size();
897         i = listItems.size();
898         while (i--)
899             if (listItems[i]->hasTagName(optionTag)) {
900                 if (found)
901                     static_cast<HTMLOptionElement*>(listItems[i])->m_selected = false;
902                 else if (static_cast<HTMLOptionElement*>(listItems[i])->selected()) {
903                     static_cast<QComboBox*>( m_widget )->setCurrentItem(i);
904                     found = true;
905                 }
906                 firstOption = i;
907             }
908
909         ASSERT(firstOption == listItems.size() || found);
910     }
911
912     m_selectionChanged = false;
913 }
914
915
916 // -------------------------------------------------------------------------
917
918
919 // -------------------------------------------------------------------------
920
921 RenderTextArea::RenderTextArea(HTMLTextAreaElement *element)
922     : RenderFormElement(element), m_dirty(false), m_updating(false)
923 {
924     QTextEdit *edit = new QTextEdit(view());
925
926     if (element->wrap() != HTMLTextAreaElement::ta_NoWrap)
927         edit->setWordWrap(QTextEdit::WidgetWidth);
928     else
929         edit->setWordWrap(QTextEdit::NoWrap);
930
931     setWidget(edit);
932 }
933
934 void RenderTextArea::destroy()
935 {
936     static_cast<HTMLTextAreaElement*>(node())->rendererWillBeDestroyed();
937     RenderFormElement::destroy();
938 }
939
940 void RenderTextArea::calcMinMaxWidth()
941 {
942     KHTMLAssert( !minMaxKnown() );
943
944     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
945     IntSize size(w->sizeWithColumnsAndRows(
946         max(static_cast<HTMLTextAreaElement*>(node())->cols(), 1),
947         max(static_cast<HTMLTextAreaElement*>(node())->rows(), 1)));
948
949     setIntrinsicWidth( size.width() );
950     setIntrinsicHeight( size.height() );
951
952     RenderFormElement::calcMinMaxWidth();
953 }
954
955 void RenderTextArea::setStyle(RenderStyle *s)
956 {
957     RenderFormElement::setStyle(s);
958
959     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
960     w->setAlignment(textAlignment());
961     w->setLineHeight(RenderObject::lineHeight(true));
962
963     w->setWritingDirection(style()->direction() == RTL ? RTL : LTR);
964
965     ScrollBarMode scrollMode = ScrollBarAuto;
966     switch (style()->overflow()) {
967         case OAUTO:
968         case OMARQUEE: // makes no sense, map to auto
969         case OOVERLAY: // not implemented for text, map to auto
970         case OVISIBLE:
971             break;
972         case OHIDDEN:
973             scrollMode = ScrollBarAlwaysOff;
974             break;
975         case OSCROLL:
976             scrollMode = ScrollBarAlwaysOn;
977             break;
978     }
979     ScrollBarMode horizontalScrollMode = scrollMode;
980     if (static_cast<HTMLTextAreaElement*>(node())->wrap() != HTMLTextAreaElement::ta_NoWrap)
981         horizontalScrollMode = ScrollBarAlwaysOff;
982
983     w->setScrollBarModes(horizontalScrollMode, scrollMode);
984 }
985
986 void RenderTextArea::setEdited(bool x)
987 {
988     m_dirty = x;
989 }
990
991 void RenderTextArea::updateFromElement()
992 {
993     HTMLTextAreaElement* e = static_cast<HTMLTextAreaElement*>(node());
994     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
995
996     w->setReadOnly(e->isReadOnlyControl());
997     w->setDisabled(e->disabled());
998
999     String widgetText = text();
1000     String text = e->value();
1001     text.replace('\\', backslashAsCurrencySymbol());
1002     if (widgetText != text) {
1003         int line, col;
1004         w->getCursorPosition( &line, &col );
1005         m_updating = true;
1006         w->setText(text);
1007         m_updating = false;
1008         w->setCursorPosition( line, col );
1009     }
1010     m_dirty = false;
1011
1012     w->setColors(style()->backgroundColor(), style()->color());
1013
1014     RenderFormElement::updateFromElement();
1015 }
1016
1017 String RenderTextArea::text()
1018 {
1019     String txt = static_cast<QTextEdit*>(m_widget)->text();
1020     return txt.replace(backslashAsCurrencySymbol(), '\\');
1021 }
1022
1023 String RenderTextArea::textWithHardLineBreaks()
1024 {
1025     String txt = static_cast<QTextEdit*>(m_widget)->textWithHardLineBreaks();
1026     return txt.replace(backslashAsCurrencySymbol(), '\\');
1027 }
1028
1029 void RenderTextArea::valueChanged(Widget*)
1030 {
1031     if (m_updating)
1032         return;
1033     static_cast<HTMLTextAreaElement*>(node())->invalidateValue();
1034     m_dirty = true;
1035 }
1036
1037 int RenderTextArea::selectionStart()
1038 {
1039     QTextEdit *textEdit = static_cast<QTextEdit *>(m_widget);
1040     return textEdit->selectionStart();
1041 }
1042
1043 int RenderTextArea::selectionEnd()
1044 {
1045     QTextEdit *textEdit = static_cast<QTextEdit *>(m_widget);
1046     return textEdit->selectionEnd();
1047 }
1048
1049 void RenderTextArea::setSelectionStart(int start)
1050 {
1051     QTextEdit *textEdit = static_cast<QTextEdit *>(m_widget);
1052     textEdit->setSelectionStart(start);
1053 }
1054
1055 void RenderTextArea::setSelectionEnd(int end)
1056 {
1057     QTextEdit *textEdit = static_cast<QTextEdit *>(m_widget);
1058     textEdit->setSelectionEnd(end);
1059 }
1060
1061 void RenderTextArea::select()
1062 {
1063     static_cast<QTextEdit *>(m_widget)->selectAll();
1064 }
1065
1066 void RenderTextArea::setSelectionRange(int start, int end)
1067 {
1068     QTextEdit *textEdit = static_cast<QTextEdit *>(m_widget);
1069     textEdit->setSelectionRange(start, end-start);
1070 }
1071
1072 void RenderTextArea::selectionChanged(Widget*)
1073 {
1074     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1075
1076     // We only want to call onselect if there actually is a selection
1077     if (!w->hasSelectedText())
1078         return;
1079     
1080     static_cast<HTMLTextAreaElement*>(node())->onSelect();
1081 }
1082
1083 // ---------------------------------------------------------------------------
1084
1085 RenderSlider::RenderSlider(HTMLInputElement* element)
1086     : RenderFormElement(element)
1087 {
1088     setWidget(new QSlider);
1089 }
1090
1091 void RenderSlider::calcMinMaxWidth()
1092 {
1093     KHTMLAssert(!minMaxKnown());
1094     
1095     // Let the widget tell us how big it wants to be.
1096     IntSize s(widget()->sizeHint());
1097     bool widthSet = !style()->width().isAuto();
1098     bool heightSet = !style()->height().isAuto();
1099     if (heightSet && !widthSet) {
1100         // Flip the intrinsic dimensions.
1101         int barLength = s.width();
1102         s = IntSize(s.height(), barLength);
1103     }
1104     setIntrinsicWidth(s.width());
1105     setIntrinsicHeight(s.height());
1106     
1107     RenderFormElement::calcMinMaxWidth();
1108 }
1109
1110 void RenderSlider::updateFromElement()
1111 {
1112     String value = static_cast<HTMLInputElement*>(node())->value();
1113     const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr);
1114     const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr);
1115     const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
1116     
1117     double minVal = minStr.isNull() ? 0.0 : minStr.deprecatedString().toDouble();
1118     double maxVal = maxStr.isNull() ? 100.0 : maxStr.deprecatedString().toDouble();
1119     minVal = min(minVal, maxVal); // Make sure the range is sane.
1120     
1121     double val = value.isNull() ? (maxVal + minVal)/2.0 : value.deprecatedString().toDouble();
1122     val = max(minVal, min(val, maxVal)); // Make sure val is within min/max.
1123     
1124     // Force integer value if not float.
1125     if (!equalIgnoringCase(precision, "float"))
1126         val = (int)(val + 0.5);
1127
1128     static_cast<HTMLInputElement*>(node())->setValue(String::number(val));
1129
1130     QSlider* slider = (QSlider*)widget();
1131      
1132     slider->setMinValue(minVal);
1133     slider->setMaxValue(maxVal);
1134     slider->setValue(val);
1135
1136     RenderFormElement::updateFromElement();
1137 }
1138
1139 void RenderSlider::valueChanged(Widget*)
1140 {
1141     QSlider* slider = static_cast<QSlider*>(widget());
1142
1143     double val = slider->value();
1144     const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
1145
1146     // Force integer value if not float.
1147     if (!equalIgnoringCase(precision, "float"))
1148         val = (int)(val + 0.5);
1149
1150     static_cast<HTMLInputElement*>(node())->setValue(String::number(val));
1151     
1152     // Fire the "input" DOM event.
1153     static_cast<HTMLInputElement*>(node())->dispatchHTMLEvent(inputEvent, true, false);
1154 }
1155
1156 }