917aa1edd5f745a37b9433d5459a59e40f9f656c
[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     RenderFormElement::updateFromElement();
531 }
532
533 void RenderFileButton::returnPressed(Widget*)
534 {
535     if (static_cast<HTMLInputElement*>(node())->form())
536         static_cast<HTMLInputElement*>(node())->form()->prepareSubmit();
537 }
538
539 void RenderFileButton::valueChanged(Widget*)
540 {
541     static_cast<HTMLInputElement*>(node())->setValueFromRenderer(static_cast<KWQFileButton*>(widget())->filename());
542     static_cast<HTMLInputElement*>(node())->onChange();
543 }
544
545 void RenderFileButton::select()
546 {
547 }
548
549
550 void RenderFileButton::click(bool sendMouseEvents)
551 {
552     static_cast<KWQFileButton *>(widget())->click(sendMouseEvents);
553 }
554
555
556 // -------------------------------------------------------------------------
557
558 RenderLabel::RenderLabel(HTMLGenericFormElement *element)
559     : RenderFormElement(element)
560 {
561
562 }
563
564 // -------------------------------------------------------------------------
565
566 RenderLegend::RenderLegend(HTMLGenericFormElement *element)
567     : RenderBlock(element)
568 {
569 }
570
571 // -------------------------------------------------------------------------
572
573 RenderSelect::RenderSelect(HTMLSelectElement *element)
574     : RenderFormElement(element)
575 {
576     m_ignoreSelectEvents = false;
577     m_multiple = element->multiple();
578     m_size = element->size();
579     m_useListBox = (m_multiple || m_size > 1);
580     m_selectionChanged = true;
581     m_optionsChanged = true;
582
583     if (m_useListBox)
584         setWidget(createListBox());
585     else
586         setWidget(new QComboBox);
587 }
588
589
590 void RenderSelect::setWidgetWritingDirection()
591 {
592     TextDirection d = style()->direction() == RTL ? RTL : LTR;
593     if (m_useListBox)
594         static_cast<QListBox *>(m_widget)->setWritingDirection(d);
595     else
596         static_cast<QComboBox *>(m_widget)->setWritingDirection(d);
597 }
598
599 void RenderSelect::setStyle(RenderStyle *s)
600 {
601     RenderFormElement::setStyle(s);
602     setWidgetWritingDirection();
603 }
604
605
606 void RenderSelect::updateFromElement()
607 {
608     m_ignoreSelectEvents = true;
609
610     // change widget type
611     bool oldMultiple = m_multiple;
612     unsigned oldSize = m_size;
613     bool oldListbox = m_useListBox;
614
615     m_multiple = static_cast<HTMLSelectElement*>(node())->multiple();
616     m_size = static_cast<HTMLSelectElement*>(node())->size();
617     m_useListBox = (m_multiple || m_size > 1);
618
619     if (oldMultiple != m_multiple || oldSize != m_size) {
620         if (m_useListBox != oldListbox) {
621             // type of select has changed
622             delete m_widget;
623
624             if (m_useListBox)
625                 setWidget(createListBox());
626             else
627                 setWidget(new QComboBox);
628             setWidgetWritingDirection();
629         }
630
631         if (m_useListBox && oldMultiple != m_multiple) {
632             static_cast<QListBox*>(m_widget)->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
633         }
634         m_selectionChanged = true;
635         m_optionsChanged = true;
636     }
637
638     // update contents listbox/combobox based on options in m_element
639     if ( m_optionsChanged ) {
640         if (static_cast<HTMLSelectElement*>(node())->m_recalcListItems)
641             static_cast<HTMLSelectElement*>(node())->recalcListItems();
642         DeprecatedArray<HTMLElement*> listItems = static_cast<HTMLSelectElement*>(node())->listItems();
643         int listIndex;
644
645         if (m_useListBox)
646             static_cast<QListBox*>(m_widget)->clear();
647         else
648             static_cast<QComboBox*>(m_widget)->clear();
649
650         bool groupEnabled = true;
651         for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) {
652             if (listItems[listIndex]->hasTagName(optgroupTag)) {
653                 HTMLOptGroupElement *optgroupElement = static_cast<HTMLOptGroupElement*>(listItems[listIndex]);
654                 DeprecatedString label = optgroupElement->getAttribute(labelAttr).deprecatedString();
655                 label.replace('\\', backslashAsCurrencySymbol());
656                 
657                 // In WinIE, an optgroup can't start or end with whitespace (other than the indent
658                 // we give it).  We match this behavior.
659                 label = label.stripWhiteSpace();
660                 // We want to collapse our whitespace too.  This will match other browsers.
661                 label = label.simplifyWhiteSpace();
662
663                 groupEnabled = optgroupElement->isEnabled();
664                 
665                 if (m_useListBox)
666                     static_cast<QListBox*>(m_widget)->appendGroupLabel(label, groupEnabled);
667                 else
668                     static_cast<QComboBox*>(m_widget)->appendGroupLabel(label);
669             }
670             else if (listItems[listIndex]->hasTagName(optionTag)) {
671                 HTMLOptionElement *optionElement = static_cast<HTMLOptionElement*>(listItems[listIndex]);
672                 DeprecatedString itemText;
673                 if (optionElement->hasAttribute(labelAttr))
674                     itemText = optionElement->getAttribute(labelAttr).deprecatedString();
675                 else
676                     itemText = optionElement->text().deprecatedString();
677                 
678                 itemText.replace('\\', backslashAsCurrencySymbol());
679
680                 // In WinIE, leading and trailing whitespace is ignored in options. We match this behavior.
681                 itemText = itemText.stripWhiteSpace();
682                 // We want to collapse our whitespace too.  This will match other browsers.
683                 itemText = itemText.simplifyWhiteSpace();
684                 
685                 if (listItems[listIndex]->parentNode()->hasTagName(optgroupTag))
686                     itemText.prepend("    ");
687
688                 if (m_useListBox)
689                     static_cast<QListBox*>(m_widget)->appendItem(itemText, groupEnabled && optionElement->isEnabled());
690                 else
691                     static_cast<QComboBox*>(m_widget)->appendItem(itemText, groupEnabled && optionElement->isEnabled());
692             }
693             else if (listItems[listIndex]->hasTagName(hrTag)) {
694                 if (!m_useListBox)
695                     static_cast<QComboBox*>(m_widget)->appendSeparator();
696             }
697             else
698                 KHTMLAssert(false);
699             m_selectionChanged = true;
700         }
701         if (m_useListBox)
702             static_cast<QListBox*>(m_widget)->doneAppendingItems();
703         setNeedsLayoutAndMinMaxRecalc();
704         m_optionsChanged = false;
705     }
706
707     // update selection
708     if (m_selectionChanged) {
709         updateSelection();
710     }
711
712     m_ignoreSelectEvents = false;
713
714     RenderFormElement::updateFromElement();
715 }
716
717
718 short RenderSelect::baselinePosition( bool f, bool isRootLineBox ) const
719 {
720     if (m_useListBox) {
721         // FIXME: Should get the hardcoded constant of 7 by calling a QListBox function,
722         // as we do for other widget classes.
723         return RenderWidget::baselinePosition( f, isRootLineBox ) - 7;
724     }
725     return RenderFormElement::baselinePosition( f, isRootLineBox );
726 }
727
728
729 void RenderSelect::calcMinMaxWidth()
730 {
731     KHTMLAssert( !minMaxKnown() );
732
733     if (m_optionsChanged)
734         updateFromElement();
735
736     // ### ugly HACK FIXME!!!
737     setMinMaxKnown();
738     layoutIfNeeded();
739     setNeedsLayoutAndMinMaxRecalc();
740     // ### end FIXME
741
742     RenderFormElement::calcMinMaxWidth();
743 }
744
745 void RenderSelect::layout( )
746 {
747     KHTMLAssert(needsLayout());
748     KHTMLAssert(minMaxKnown());
749
750     // ### maintain selection properly between type/size changes, and work
751     // out how to handle multiselect->singleselect (probably just select
752     // first selected one)
753
754     // calculate size
755     if(m_useListBox) {
756         QListBox* w = static_cast<QListBox*>(m_widget);
757
758
759         int size = m_size;
760         // check if multiple and size was not given or invalid
761         // Internet Exploder sets size to min(number of elements, 4)
762         // Netscape seems to simply set it to "number of elements"
763         // the average of that is IMHO min(number of elements, 10)
764         // so I did that ;-)
765         if(size < 1)
766             size = min(static_cast<QListBox*>(m_widget)->count(), 10U);
767
768         // Let the widget tell us how big it wants to be.
769         IntSize s(w->sizeForNumberOfLines(size));
770         setIntrinsicWidth( s.width() );
771         setIntrinsicHeight( s.height() );
772     }
773     else {
774         IntSize s(m_widget->sizeHint());
775         setIntrinsicWidth( s.width() );
776         setIntrinsicHeight( s.height() );
777     }
778
779     RenderFormElement::layout();
780
781     // and now disable the widget in case there is no <option> given
782     DeprecatedArray<HTMLElement*> listItems = static_cast<HTMLSelectElement*>(node())->listItems();
783
784     bool foundOption = false;
785     for (unsigned i = 0; i < listItems.size() && !foundOption; i++)
786         foundOption = (listItems[i]->hasTagName(optionTag));
787
788     m_widget->setEnabled(foundOption && ! static_cast<HTMLSelectElement*>(node())->disabled());
789 }
790
791 void RenderSelect::valueChanged(Widget*)
792 {
793     if (m_ignoreSelectEvents)
794         return;
795
796     KHTMLAssert(!m_useListBox);
797
798     int index = static_cast<QComboBox*>(m_widget)->currentItem();
799
800     DeprecatedArray<HTMLElement*> listItems = static_cast<HTMLSelectElement*>(node())->listItems();
801     if (index >= 0 && index < (int)listItems.size()) {
802         bool found = listItems[index]->hasTagName(optionTag);
803         if (!found) {
804             // this one is not selectable,  we need to find an option element
805             while ((unsigned) index < listItems.size()) {
806                 if (listItems[index]->hasTagName(optionTag)) {
807                     found = true;
808                     break;
809                 }
810                 ++index;
811             }
812
813             if (!found) {
814                 while (index >= 0) {
815                     if (listItems[index]->hasTagName(optionTag)) {
816                         found = true;
817                         break;
818                     }
819                     --index;
820                 }
821             }
822         }
823
824         if (found) {
825             if ( index != static_cast<QComboBox*>(m_widget)->currentItem() )
826                 static_cast<QComboBox*>(m_widget)->setCurrentItem( index );
827
828             for (unsigned i = 0; i < listItems.size(); ++i)
829                 if (listItems[i]->hasTagName(optionTag) && i != (unsigned int) index)
830                     static_cast<HTMLOptionElement*>( listItems[i] )->m_selected = false;
831
832             static_cast<HTMLOptionElement*>(listItems[index])->m_selected = true;
833         }
834     }
835
836     static_cast<HTMLSelectElement*>(node())->onChange();
837 }
838
839
840 void RenderSelect::selectionChanged(Widget*)
841 {
842     if (m_ignoreSelectEvents)
843         return;
844
845     // don't use listItems() here as we have to avoid recalculations - changing the
846     // option list will make use update options not in the way the user expects them
847     DeprecatedArray<HTMLElement*> listItems = static_cast<HTMLSelectElement*>(node())->m_listItems;
848     int j = 0;
849     for (unsigned i = 0; i < listItems.count(); i++) {
850         // don't use setSelected() here because it will cause us to be called
851         // again with updateSelection.
852         if (listItems[i]->hasTagName(optionTag))
853             static_cast<HTMLOptionElement*>( listItems[i] )
854                 ->m_selected = static_cast<QListBox*>( m_widget )->isSelected( j );
855         if (listItems[i]->hasTagName(optionTag) || listItems[i]->hasTagName(optgroupTag))
856             ++j;
857     }
858     static_cast<HTMLSelectElement*>(node())->onChange();
859 }
860
861
862 void RenderSelect::setOptionsChanged(bool _optionsChanged)
863 {
864     m_optionsChanged = _optionsChanged;
865 }
866
867 QListBox* RenderSelect::createListBox()
868 {
869     QListBox *lb = new QListBox();
870     lb->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
871     m_ignoreSelectEvents = false;
872     return lb;
873 }
874
875 void RenderSelect::updateSelection()
876 {
877     DeprecatedArray<HTMLElement*> listItems = static_cast<HTMLSelectElement*>(node())->listItems();
878     int i;
879     if (m_useListBox) {
880         // if multi-select, we select only the new selected index
881         QListBox *listBox = static_cast<QListBox*>(m_widget);
882         int j = 0;
883         for (i = 0; i < int(listItems.size()); i++) {
884             listBox->setSelected(j, listItems[i]->hasTagName(optionTag) &&
885                                 static_cast<HTMLOptionElement*>(listItems[i])->selected());
886             if (listItems[i]->hasTagName(optionTag) || listItems[i]->hasTagName(optgroupTag))
887                 ++j;
888             
889         }
890     }
891     else {
892         bool found = false;
893         unsigned firstOption = listItems.size();
894         i = listItems.size();
895         while (i--)
896             if (listItems[i]->hasTagName(optionTag)) {
897                 if (found)
898                     static_cast<HTMLOptionElement*>(listItems[i])->m_selected = false;
899                 else if (static_cast<HTMLOptionElement*>(listItems[i])->selected()) {
900                     static_cast<QComboBox*>( m_widget )->setCurrentItem(i);
901                     found = true;
902                 }
903                 firstOption = i;
904             }
905
906         ASSERT(firstOption == listItems.size() || found);
907     }
908
909     m_selectionChanged = false;
910 }
911
912
913 // -------------------------------------------------------------------------
914
915
916 // -------------------------------------------------------------------------
917
918 RenderTextArea::RenderTextArea(HTMLTextAreaElement *element)
919     : RenderFormElement(element), m_dirty(false), m_updating(false)
920 {
921     QTextEdit *edit = new QTextEdit(view());
922
923     if (element->wrap() != HTMLTextAreaElement::ta_NoWrap)
924         edit->setWordWrap(QTextEdit::WidgetWidth);
925     else
926         edit->setWordWrap(QTextEdit::NoWrap);
927
928     setWidget(edit);
929 }
930
931 void RenderTextArea::destroy()
932 {
933     static_cast<HTMLTextAreaElement*>(node())->rendererWillBeDestroyed();
934     RenderFormElement::destroy();
935 }
936
937 void RenderTextArea::calcMinMaxWidth()
938 {
939     KHTMLAssert( !minMaxKnown() );
940
941     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
942     IntSize size(w->sizeWithColumnsAndRows(
943         max(static_cast<HTMLTextAreaElement*>(node())->cols(), 1),
944         max(static_cast<HTMLTextAreaElement*>(node())->rows(), 1)));
945
946     setIntrinsicWidth( size.width() );
947     setIntrinsicHeight( size.height() );
948
949     RenderFormElement::calcMinMaxWidth();
950 }
951
952 void RenderTextArea::setStyle(RenderStyle *s)
953 {
954     RenderFormElement::setStyle(s);
955
956     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
957     w->setAlignment(textAlignment());
958     w->setLineHeight(RenderObject::lineHeight(true));
959
960     w->setWritingDirection(style()->direction() == RTL ? RTL : LTR);
961
962     ScrollBarMode scrollMode = ScrollBarAuto;
963     switch (style()->overflow()) {
964         case OAUTO:
965         case OMARQUEE: // makes no sense, map to auto
966         case OOVERLAY: // not implemented for text, map to auto
967         case OVISIBLE:
968             break;
969         case OHIDDEN:
970             scrollMode = ScrollBarAlwaysOff;
971             break;
972         case OSCROLL:
973             scrollMode = ScrollBarAlwaysOn;
974             break;
975     }
976     ScrollBarMode horizontalScrollMode = scrollMode;
977     if (static_cast<HTMLTextAreaElement*>(node())->wrap() != HTMLTextAreaElement::ta_NoWrap)
978         horizontalScrollMode = ScrollBarAlwaysOff;
979
980     w->setScrollBarModes(horizontalScrollMode, scrollMode);
981 }
982
983 void RenderTextArea::setEdited(bool x)
984 {
985     m_dirty = x;
986 }
987
988 void RenderTextArea::updateFromElement()
989 {
990     HTMLTextAreaElement* e = static_cast<HTMLTextAreaElement*>(node());
991     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
992
993     w->setReadOnly(e->isReadOnlyControl());
994     w->setDisabled(e->disabled());
995
996     String widgetText = text();
997     String text = e->value();
998     text.replace('\\', backslashAsCurrencySymbol());
999     if (widgetText != text) {
1000         int line, col;
1001         w->getCursorPosition( &line, &col );
1002         m_updating = true;
1003         w->setText(text);
1004         m_updating = false;
1005         w->setCursorPosition( line, col );
1006     }
1007     m_dirty = false;
1008
1009     w->setColors(style()->backgroundColor(), style()->color());
1010
1011     RenderFormElement::updateFromElement();
1012 }
1013
1014 String RenderTextArea::text()
1015 {
1016     String txt = static_cast<QTextEdit*>(m_widget)->text();
1017     return txt.replace(backslashAsCurrencySymbol(), '\\');
1018 }
1019
1020 String RenderTextArea::textWithHardLineBreaks()
1021 {
1022     String txt = static_cast<QTextEdit*>(m_widget)->textWithHardLineBreaks();
1023     return txt.replace(backslashAsCurrencySymbol(), '\\');
1024 }
1025
1026 void RenderTextArea::valueChanged(Widget*)
1027 {
1028     if (m_updating)
1029         return;
1030     static_cast<HTMLTextAreaElement*>(node())->invalidateValue();
1031     m_dirty = true;
1032 }
1033
1034 int RenderTextArea::selectionStart()
1035 {
1036     QTextEdit *textEdit = static_cast<QTextEdit *>(m_widget);
1037     return textEdit->selectionStart();
1038 }
1039
1040 int RenderTextArea::selectionEnd()
1041 {
1042     QTextEdit *textEdit = static_cast<QTextEdit *>(m_widget);
1043     return textEdit->selectionEnd();
1044 }
1045
1046 void RenderTextArea::setSelectionStart(int start)
1047 {
1048     QTextEdit *textEdit = static_cast<QTextEdit *>(m_widget);
1049     textEdit->setSelectionStart(start);
1050 }
1051
1052 void RenderTextArea::setSelectionEnd(int end)
1053 {
1054     QTextEdit *textEdit = static_cast<QTextEdit *>(m_widget);
1055     textEdit->setSelectionEnd(end);
1056 }
1057
1058 void RenderTextArea::select()
1059 {
1060     static_cast<QTextEdit *>(m_widget)->selectAll();
1061 }
1062
1063 void RenderTextArea::setSelectionRange(int start, int end)
1064 {
1065     QTextEdit *textEdit = static_cast<QTextEdit *>(m_widget);
1066     textEdit->setSelectionRange(start, end-start);
1067 }
1068
1069 void RenderTextArea::selectionChanged(Widget*)
1070 {
1071     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1072
1073     // We only want to call onselect if there actually is a selection
1074     if (!w->hasSelectedText())
1075         return;
1076     
1077     static_cast<HTMLTextAreaElement*>(node())->onSelect();
1078 }
1079
1080 // ---------------------------------------------------------------------------
1081
1082 RenderSlider::RenderSlider(HTMLInputElement* element)
1083     : RenderFormElement(element)
1084 {
1085     setWidget(new QSlider);
1086 }
1087
1088 void RenderSlider::calcMinMaxWidth()
1089 {
1090     KHTMLAssert(!minMaxKnown());
1091     
1092     // Let the widget tell us how big it wants to be.
1093     IntSize s(widget()->sizeHint());
1094     bool widthSet = !style()->width().isAuto();
1095     bool heightSet = !style()->height().isAuto();
1096     if (heightSet && !widthSet) {
1097         // Flip the intrinsic dimensions.
1098         int barLength = s.width();
1099         s = IntSize(s.height(), barLength);
1100     }
1101     setIntrinsicWidth(s.width());
1102     setIntrinsicHeight(s.height());
1103     
1104     RenderFormElement::calcMinMaxWidth();
1105 }
1106
1107 void RenderSlider::updateFromElement()
1108 {
1109     String value = static_cast<HTMLInputElement*>(node())->value();
1110     const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr);
1111     const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr);
1112     const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
1113     
1114     double minVal = minStr.isNull() ? 0.0 : minStr.deprecatedString().toDouble();
1115     double maxVal = maxStr.isNull() ? 100.0 : maxStr.deprecatedString().toDouble();
1116     minVal = min(minVal, maxVal); // Make sure the range is sane.
1117     
1118     double val = value.isNull() ? (maxVal + minVal)/2.0 : value.deprecatedString().toDouble();
1119     val = max(minVal, min(val, maxVal)); // Make sure val is within min/max.
1120     
1121     // Force integer value if not float.
1122     if (!equalIgnoringCase(precision, "float"))
1123         val = (int)(val + 0.5);
1124
1125     static_cast<HTMLInputElement*>(node())->setValue(String::number(val));
1126
1127     QSlider* slider = (QSlider*)widget();
1128      
1129     slider->setMinValue(minVal);
1130     slider->setMaxValue(maxVal);
1131     slider->setValue(val);
1132
1133     RenderFormElement::updateFromElement();
1134 }
1135
1136 void RenderSlider::valueChanged(Widget*)
1137 {
1138     QSlider* slider = static_cast<QSlider*>(widget());
1139
1140     double val = slider->value();
1141     const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
1142
1143     // Force integer value if not float.
1144     if (!equalIgnoringCase(precision, "float"))
1145         val = (int)(val + 0.5);
1146
1147     static_cast<HTMLInputElement*>(node())->setValue(String::number(val));
1148     
1149     // Fire the "input" DOM event.
1150     static_cast<HTMLInputElement*>(node())->dispatchHTMLEvent(inputEvent, true, false);
1151 }
1152
1153 }