Fix for 3936571, placeholder attribute should work for normal inputs for Dashboard.
[WebKit-https.git] / WebCore / khtml / 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 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 <kdebug.h>
27 #include <klocale.h>
28 #include <kfiledialog.h>
29 #include <kcompletionbox.h>
30 #include <kcursor.h>
31
32 #include <qstyle.h>
33
34 #include "misc/helper.h"
35 #include "xml/dom2_eventsimpl.h"
36 #include "html/html_formimpl.h"
37 #include "misc/htmlhashes.h"
38
39 #include "rendering/render_form.h"
40 #include <assert.h>
41
42 #include "khtmlview.h"
43 #include "khtml_ext.h"
44 #include "xml/dom_docimpl.h"
45
46 #include <kdebug.h>
47
48 #if APPLE_CHANGES
49 #include "KWQFileButton.h"
50 #include "KWQSlider.h"
51 #endif
52
53 using namespace khtml;
54 using namespace DOM;
55
56 RenderFormElement::RenderFormElement(HTMLGenericFormElementImpl *element)
57     : RenderWidget(element)
58 {
59     setInline(true);
60 }
61
62 RenderFormElement::~RenderFormElement()
63 {
64 }
65
66 short RenderFormElement::baselinePosition( bool f, bool isRootLineBox ) const
67 {
68 #if APPLE_CHANGES
69     return marginTop() + widget()->baselinePosition(m_height);
70 #else
71     return RenderWidget::baselinePosition( f, isRootLineBox ) - 2 - style()->fontMetrics().descent();
72 #endif
73 }
74
75 void RenderFormElement::setStyle(RenderStyle* s)
76 {
77 #if APPLE_CHANGES
78     if (canHaveIntrinsicMargins())
79         addIntrinsicMarginsIfAllowed(s);
80 #endif
81
82     RenderWidget::setStyle(s);
83
84 #if APPLE_CHANGES
85     // Do not paint a background or border for Aqua form elements
86     setShouldPaintBackgroundOrBorder(false);
87 #endif
88
89     m_widget->setFont(style()->font());
90 }
91
92 void RenderFormElement::updateFromElement()
93 {
94     m_widget->setEnabled(!element()->disabled());
95
96 #if APPLE_CHANGES
97     m_widget->setPalette(QPalette(style()->backgroundColor(), style()->color()));
98 #else
99     QColor color = style()->color();
100     QColor backgroundColor = style()->backgroundColor();
101
102     if ( color.isValid() || backgroundColor.isValid() ) {
103         QPalette pal(m_widget->palette());
104
105         int contrast_ = KGlobalSettings::contrast();
106         int highlightVal = 100 + (2*contrast_+4)*16/10;
107         int lowlightVal = 100 + (2*contrast_+4)*10;
108
109         if (backgroundColor.isValid()) {
110             for ( int i = 0; i < QPalette::NColorGroups; i++ ) {
111                 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Background, backgroundColor );
112                 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Light, backgroundColor.light(highlightVal) );
113                 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Dark, backgroundColor.dark(lowlightVal) );
114                 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Mid, backgroundColor.dark(120) );
115                 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Midlight, backgroundColor.light(110) );
116                 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Button, backgroundColor );
117                 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Base, backgroundColor );
118             }
119         }
120         if ( color.isValid() ) {
121             struct ColorSet {
122                 QPalette::ColorGroup cg;
123                 QColorGroup::ColorRole cr;
124             };
125             const struct ColorSet toSet [] = {
126                 { QPalette::Active, QColorGroup::Foreground },
127                 { QPalette::Active, QColorGroup::ButtonText },
128                 { QPalette::Active, QColorGroup::Text },
129                 { QPalette::Inactive, QColorGroup::Foreground },
130                 { QPalette::Inactive, QColorGroup::ButtonText },
131                 { QPalette::Inactive, QColorGroup::Text },
132                 { QPalette::Disabled,QColorGroup::ButtonText },
133                 { QPalette::NColorGroups, QColorGroup::NColorRoles },
134             };
135             const ColorSet *set = toSet;
136             while( set->cg != QPalette::NColorGroups ) {
137                 pal.setColor( set->cg, set->cr, color );
138                 ++set;
139             }
140
141             QColor disfg = color;
142             int h, s, v;
143             disfg.hsv( &h, &s, &v );
144             if (v > 128)
145                 // dark bg, light fg - need a darker disabled fg
146                 disfg = disfg.dark(lowlightVal);
147             else if (disfg != Qt::black)
148                 // light bg, dark fg - need a lighter disabled fg - but only if !black
149                 disfg = disfg.light(highlightVal);
150             else
151                 // black fg - use darkgrey disabled fg
152                 disfg = Qt::darkGray;
153             pal.setColor(QPalette::Disabled,QColorGroup::Foreground,disfg);
154         }
155
156         m_widget->setPalette(pal);
157     }
158     else
159         m_widget->unsetPalette();
160 #endif
161 }
162
163 void RenderFormElement::layout()
164 {
165     KHTMLAssert( needsLayout() );
166     KHTMLAssert( minMaxKnown() );
167
168     // minimum height
169     m_height = 0;
170
171     calcWidth();
172     calcHeight();
173
174 #if !APPLE_CHANGES
175     if ( m_widget )
176         resizeWidget(m_widget,
177                      m_width-borderLeft()-borderRight()-paddingLeft()-paddingRight(),
178                      m_height-borderLeft()-borderRight()-paddingLeft()-paddingRight());
179 #endif
180     
181     setNeedsLayout(false);
182 }
183
184 void RenderFormElement::slotClicked()
185 {
186     RenderArena *arena = ref();
187
188     // We also send the KHTML_CLICK or KHTML_DBLCLICK event for
189     // CLICK. This is not part of the DOM specs, but is used for
190     // compatibility with the traditional onclick="" and ondblclick=""
191     // attributes, as there is no way to tell the difference between
192     // single & double clicks using DOM (only the click count is
193     // stored, which is not necessarily the same)
194
195 #if APPLE_CHANGES
196     QMouseEvent event(QEvent::MouseButtonRelease); // gets "current event"
197     element()->dispatchMouseEvent(&event, EventImpl::CLICK_EVENT, event.clickCount());
198     element()->dispatchMouseEvent(&event, event.isDoubleClick() ? EventImpl::KHTML_DBLCLICK_EVENT : EventImpl::KHTML_CLICK_EVENT, event.clickCount());
199 #else
200     QMouseEvent e2(QEvent::MouseButtonRelease, m_mousePos, m_button, m_state);
201     element()->dispatchMouseEvent(&e2, EventImpl::CLICK_EVENT, m_clickCount);
202     element()->dispatchMouseEvent(&e2, m_isDoubleClick ? EventImpl::KHTML_DBLCLICK_EVENT : EventImpl::KHTML_CLICK_EVENT, m_clickCount);
203 #endif
204
205     deref(arena);
206 }
207
208 Qt::AlignmentFlags RenderFormElement::textAlignment() const
209 {
210     switch (style()->textAlign()) {
211         case LEFT:
212         case KHTML_LEFT:
213             return AlignLeft;
214         case RIGHT:
215         case KHTML_RIGHT:
216             return AlignRight;
217         case CENTER:
218         case KHTML_CENTER:
219             return AlignHCenter;
220         case JUSTIFY:
221             // Just fall into the auto code for justify.
222         case TAAUTO:
223             return style()->direction() == RTL ? AlignRight : AlignLeft;
224     }
225     assert(false); // Should never be reached.
226     return AlignLeft;
227 }
228
229 #if APPLE_CHANGES
230
231 void RenderFormElement::addIntrinsicMarginsIfAllowed(RenderStyle* _style)
232 {
233     // Cut out the intrinsic margins completely if we end up using mini controls.
234     if (_style->font().pixelSize() < 11)
235         return;
236     
237     int m = intrinsicMargin();
238     if (_style->width().isVariable()) {
239         if (_style->marginLeft().quirk)
240             _style->setMarginLeft(Length(m, Fixed));
241         if (_style->marginRight().quirk)
242             _style->setMarginRight(Length(m, Fixed));
243     }
244
245     if (_style->height().isVariable()) {
246         if (_style->marginTop().quirk)
247             _style->setMarginTop(Length(m, Fixed));
248         if (_style->marginBottom().quirk)
249             _style->setMarginBottom(Length(m, Fixed));
250     }
251 }
252
253 #endif
254
255 // -------------------------------------------------------------------------
256
257 RenderButton::RenderButton(HTMLGenericFormElementImpl *element)
258     : RenderFormElement(element)
259 {
260 }
261
262 short RenderButton::baselinePosition( bool f, bool isRootLineBox ) const
263 {
264 #if APPLE_CHANGES
265     return RenderFormElement::baselinePosition( f, isRootLineBox );
266 #else
267     return RenderWidget::baselinePosition( f, isRootLineBox ) - 2;
268 #endif
269 }
270
271 // -------------------------------------------------------------------------------
272
273 RenderCheckBox::RenderCheckBox(HTMLInputElementImpl *element)
274     : RenderButton(element)
275 {
276     QCheckBox* b = new QCheckBox(view()->viewport());
277     b->setAutoMask(true);
278     b->setMouseTracking(true);
279     setQWidget(b);
280     connect(b,SIGNAL(stateChanged(int)),this,SLOT(slotStateChanged(int)));
281     connect(b, SIGNAL(clicked()), this, SLOT(slotClicked()));
282 }
283
284 void RenderCheckBox::calcMinMaxWidth()
285 {
286     KHTMLAssert( !minMaxKnown() );
287
288 #if APPLE_CHANGES
289     // Let the widget tell us how big it wants to be.
290     QSize s(widget()->sizeHint());
291 #else
292     QCheckBox *cb = static_cast<QCheckBox *>( m_widget );
293     QSize s( cb->style().pixelMetric( QStyle::PM_IndicatorWidth ),
294              cb->style().pixelMetric( QStyle::PM_IndicatorHeight ) );
295 #endif
296     setIntrinsicWidth( s.width() );
297     setIntrinsicHeight( s.height() );
298
299     RenderButton::calcMinMaxWidth();
300 }
301
302 void RenderCheckBox::updateFromElement()
303 {
304     widget()->setChecked(element()->checked());
305
306     RenderButton::updateFromElement();
307 }
308
309 // From the Qt documentation:
310 // state is 2 if the button is on, 1 if it is in the "no change" state or 0 if the button is off. 
311 void RenderCheckBox::slotStateChanged(int state)
312 {
313     element()->setChecked(state == 2);
314     element()->onChange();
315 }
316
317 // -------------------------------------------------------------------------------
318
319 RenderRadioButton::RenderRadioButton(HTMLInputElementImpl *element)
320     : RenderButton(element)
321 {
322     QRadioButton* b = new QRadioButton(view()->viewport());
323     b->setAutoMask(true);
324     b->setMouseTracking(true);
325     setQWidget(b);
326     connect(b, SIGNAL(clicked()), this, SLOT(slotClicked()));
327 }
328
329 void RenderRadioButton::updateFromElement()
330 {
331     widget()->setChecked(element()->checked());
332
333     RenderButton::updateFromElement();
334 }
335
336 void RenderRadioButton::slotClicked()
337 {
338     element()->setChecked(true);
339
340     // emit mouseClick event etc
341     RenderButton::slotClicked();
342 }
343
344 void RenderRadioButton::calcMinMaxWidth()
345 {
346     KHTMLAssert( !minMaxKnown() );
347
348 #if APPLE_CHANGES
349     // Let the widget tell us how big it wants to be.
350     QSize s(widget()->sizeHint());
351 #else
352     QRadioButton *rb = static_cast<QRadioButton *>( m_widget );
353     QSize s( rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorWidth ),
354              rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorHeight ) );
355 #endif
356     setIntrinsicWidth( s.width() );
357     setIntrinsicHeight( s.height() );
358
359     RenderButton::calcMinMaxWidth();
360 }
361
362 // -------------------------------------------------------------------------------
363
364
365 RenderSubmitButton::RenderSubmitButton(HTMLInputElementImpl *element)
366     : RenderButton(element)
367 {
368     QPushButton* p = new QPushButton(view()->viewport());
369     setQWidget(p);
370     p->setAutoMask(true);
371     p->setMouseTracking(true);
372     connect(p, SIGNAL(clicked()), this, SLOT(slotClicked()));
373 }
374
375 QString RenderSubmitButton::rawText()
376 {
377     QString value = element()->value().isEmpty() ? defaultLabel() : element()->value().string();
378     value = value.stripWhiteSpace();
379     value.replace(QChar('\\'), backslashAsCurrencySymbol());
380 #if APPLE_CHANGES
381     return value;
382 #else
383     QString raw;
384     for(unsigned int i = 0; i < value.length(); i++) {
385         raw += value[i];
386         if(value[i] == '&')
387             raw += '&';
388     }
389     return raw;
390 #endif
391 }
392
393 void RenderSubmitButton::calcMinMaxWidth()
394 {
395     KHTMLAssert( !minMaxKnown() );
396
397 #if APPLE_CHANGES
398     // Let the widget tell us how big it wants to be.
399     QSize s(widget()->sizeHint());
400     setIntrinsicWidth(s.width());
401     setIntrinsicHeight(s.height());
402 #else
403     QString raw = rawText();
404     QPushButton* pb = static_cast<QPushButton*>(m_widget);
405     pb->setText(raw);
406     pb->setFont(style()->font());
407
408     bool empty = raw.isEmpty();
409     if ( empty )
410         raw = QString::fromLatin1("XXXX");
411     QFontMetrics fm = pb->fontMetrics();
412     int margin = pb->style().pixelMetric( QStyle::PM_ButtonMargin, pb);
413     QSize s(pb->style().sizeFromContents(
414                 QStyle::CT_PushButton, pb, fm.size( ShowPrefix, raw))
415             .expandedTo(QApplication::globalStrut()));
416     
417     setIntrinsicWidth( s.width() - margin / 2 );
418     setIntrinsicHeight( s.height() - margin / 2);
419 #endif
420
421     RenderButton::calcMinMaxWidth();
422 }
423
424 #if APPLE_CHANGES
425
426 void RenderSubmitButton::setStyle(RenderStyle *s)
427 {
428     RenderButton::setStyle(s);
429     
430     QPushButton *w = static_cast<QPushButton*>(m_widget);
431     w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
432 }
433
434 #endif
435
436 void RenderSubmitButton::updateFromElement()
437 {
438     QPushButton *w = static_cast<QPushButton*>(m_widget);
439
440     QString oldText = w->text();
441     QString newText = rawText();
442     w->setText(newText);
443     if ( oldText != newText )
444         setNeedsLayoutAndMinMaxRecalc();
445     RenderFormElement::updateFromElement();
446 }
447
448 QString RenderSubmitButton::defaultLabel()
449 {
450 #if APPLE_CHANGES
451     return submitButtonDefaultLabel();
452 #else
453     return i18n("Submit");
454 #endif
455 }
456
457 short RenderSubmitButton::baselinePosition( bool f, bool isRootLineBox ) const
458 {
459     return RenderFormElement::baselinePosition( f, isRootLineBox );
460 }
461
462 // -------------------------------------------------------------------------------
463
464 RenderImageButton::RenderImageButton(HTMLInputElementImpl *element)
465     : RenderImage(element)
466 {
467     // ### support DOMActivate event when clicked
468 }
469
470 // -------------------------------------------------------------------------------
471
472 RenderResetButton::RenderResetButton(HTMLInputElementImpl *element)
473     : RenderSubmitButton(element)
474 {
475 }
476
477 QString RenderResetButton::defaultLabel()
478 {
479 #if APPLE_CHANGES
480     return resetButtonDefaultLabel();
481 #else
482     return i18n("Reset");
483 #endif
484 }
485
486
487 // -------------------------------------------------------------------------------
488
489 RenderPushButton::RenderPushButton(HTMLInputElementImpl *element)
490     : RenderSubmitButton(element)
491 {
492 }
493
494 QString RenderPushButton::defaultLabel()
495 {
496     return QString::null;
497 }
498
499 // -------------------------------------------------------------------------------
500
501 #if !APPLE_CHANGES
502
503 LineEditWidget::LineEditWidget(QWidget *parent)
504         : KLineEdit(parent)
505 {
506     setMouseTracking(true);
507 }
508
509 bool LineEditWidget::event( QEvent *e )
510 {
511     if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
512         QKeyEvent* ke = (QKeyEvent*) e;
513         if ( ke->state() & ControlButton ) {
514             switch ( ke->key() ) {
515                 case Key_Left:
516                 case Key_Right:
517                 case Key_Up:
518                 case Key_Down:
519                 case Key_Home:
520                 case Key_End:
521                     ke->accept();
522                 default:
523                 break;
524             }
525         }
526     }
527     return KLineEdit::event( e );
528 }
529
530 #endif
531
532 // -----------------------------------------------------------------------------
533
534 RenderLineEdit::RenderLineEdit(HTMLInputElementImpl *element)
535     : RenderFormElement(element), m_updating(false)
536 {
537 #if APPLE_CHANGES
538     QLineEdit::Type type;
539     switch (element->inputType()) {
540         case HTMLInputElementImpl::PASSWORD:
541             type = QLineEdit::Password;
542             break;
543         case HTMLInputElementImpl::SEARCH:
544             type = QLineEdit::Search;
545             break;
546         default:
547             type = QLineEdit::Normal;
548     }
549     KLineEdit *edit = new KLineEdit(type);
550     if (type == QLineEdit::Search)
551         edit->setLiveSearch(false);
552 #else
553     LineEditWidget *edit = new LineEditWidget(view()->viewport());
554 #endif
555     connect(edit,SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
556     connect(edit,SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
557     connect(edit,SIGNAL(clicked()),this,SLOT(slotClicked()));
558
559 #if APPLE_CHANGES
560     connect(edit,SIGNAL(performSearch()), this, SLOT(slotPerformSearch()));
561 #endif
562
563 #if !APPLE_CHANGES
564     if(element->inputType() == HTMLInputElementImpl::PASSWORD)
565         edit->setEchoMode( QLineEdit::Password );
566
567     if ( element->autoComplete() ) {
568         QStringList completions = view()->formCompletionItems(element->name().string());
569         if (completions.count()) {
570             edit->completionObject()->setItems(completions);
571             edit->setContextMenuEnabled(true);
572         }
573     }
574 #endif
575
576     setQWidget(edit);
577 }
578
579 void RenderLineEdit::slotReturnPressed()
580 {
581 #if !APPLE_CHANGES
582     // don't submit the form when return was pressed in a completion-popup
583     KCompletionBox *box = widget()->completionBox(false);
584     if ( box && box->isVisible() && box->currentItem() != -1 )
585         return;
586 #endif
587
588     // Emit onChange if necessary
589     // Works but might not be enough, dirk said he had another solution at
590     // hand (can't remember which) - David
591     handleFocusOut();
592
593     HTMLFormElementImpl* fe = element()->form();
594     if ( fe )
595         fe->submitClick();
596 }
597
598 #if APPLE_CHANGES
599 void RenderLineEdit::slotPerformSearch()
600 {
601     // Fire the "search" DOM event.
602     element()->dispatchHTMLEvent(EventImpl::SEARCH_EVENT, true, false);
603 }
604
605 void RenderLineEdit::addSearchResult()
606 {
607     if (widget())
608         widget()->addSearchResult();
609 }
610 #endif
611
612 void RenderLineEdit::handleFocusOut()
613 {
614     if ( widget() && widget()->edited() ) {
615         element()->onChange();
616         widget()->setEdited( false );
617     }
618 }
619
620 void RenderLineEdit::calcMinMaxWidth()
621 {
622     KHTMLAssert( !minMaxKnown() );
623
624 #if APPLE_CHANGES
625     // Let the widget tell us how big it wants to be.
626     m_updating = true;
627     int size = element()->size();
628     QSize s(widget()->sizeForCharacterWidth(size > 0 ? size : 20));
629     m_updating = false;
630 #else
631     const QFontMetrics &fm = style()->fontMetrics();
632     QSize s;
633
634     int size = element()->size();
635
636     int h = fm.lineSpacing();
637     int w = fm.width( 'x' ) * (size > 0 ? size : 17); // "some"
638     s = QSize(w + 2 + 2*widget()->frameWidth(),
639               QMAX(h, 14) + 2 + 2*widget()->frameWidth())
640         .expandedTo(QApplication::globalStrut());
641 #endif
642
643     setIntrinsicWidth( s.width() );
644     setIntrinsicHeight( s.height() );
645
646     RenderFormElement::calcMinMaxWidth();
647 }
648
649 void RenderLineEdit::setStyle(RenderStyle *s)
650 {
651     RenderFormElement::setStyle(s);
652
653     KLineEdit *w = widget();
654     w->setAlignment(textAlignment());
655 #if APPLE_CHANGES
656     w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
657 #endif
658 }
659
660 void RenderLineEdit::updateFromElement()
661 {
662     KLineEdit *w = widget();
663     
664     int ml = element()->maxLength();
665     if ( ml <= 0 || ml > 1024 )
666         ml = 1024;
667     if ( w->maxLength() != ml )
668         w->setMaxLength( ml );
669
670     // Call w->text() before calling element()->value(), because in the case of inline
671     // input such as Hiragana, w->text() has a side effect of sending the notification
672     // that we use in slotTextChanged to update element()->m_value
673     QString widgetText = w->text();
674     QString newText = element()->value().string();
675     newText.replace(QChar('\\'), backslashAsCurrencySymbol());
676
677     if (newText != widgetText) {
678         w->blockSignals(true);
679         int pos = w->cursorPosition();
680
681         m_updating = true;
682         w->setText(newText);
683         m_updating = false;
684         
685         w->setEdited( false );
686
687         w->setCursorPosition(pos);
688         w->blockSignals(false);
689     }
690     w->setReadOnly(element()->readOnly());
691     
692 #if APPLE_CHANGES
693     // Handle updating the search attributes.
694     w->setPlaceholderString(element()->getAttribute(ATTR_PLACEHOLDER).string());
695     if (w->type() == QLineEdit::Search) {
696         w->setLiveSearch(!element()->getAttribute(ATTR_INCREMENTAL).isNull());
697         w->setAutoSaveName(element()->getAttribute(ATTR_AUTOSAVE).string());
698         w->setMaxResults(element()->maxResults());
699     }
700 #endif
701
702     RenderFormElement::updateFromElement();
703 }
704
705 void RenderLineEdit::slotTextChanged(const QString &string)
706 {
707     // don't use setValue here!
708     if (m_updating) // Don't alter m_value if we are in the middle of initing the control, since
709         return;     // we may have gotten our initial value from the attribute.
710
711     // A null string value is used to indicate that the form control has not altered the original
712     // default value.  That means that we should never use the null string value when the user
713     // empties a textfield, but should always force an empty textfield to use the empty string.
714     QString newText = string.isNull() ? "" : string;
715     newText.replace(backslashAsCurrencySymbol(), QChar('\\'));
716     element()->m_value = newText;
717     
718     // Fire the "input" DOM event.
719     element()->dispatchHTMLEvent(EventImpl::INPUT_EVENT, true, false);
720 }
721
722 void RenderLineEdit::select()
723 {
724     static_cast<KLineEdit*>(m_widget)->selectAll();
725 }
726
727 // ---------------------------------------------------------------------------
728
729 RenderFieldset::RenderFieldset(HTMLGenericFormElementImpl *element)
730 : RenderBlock(element)
731 {
732 }
733
734 RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren)
735 {
736     RenderObject* legend = findLegend();
737     if (legend) {
738         if (relayoutChildren)
739             legend->setNeedsLayout(true);
740         legend->layoutIfNeeded();
741
742         int xPos = borderLeft() + paddingLeft() + legend->marginLeft();
743         if (style()->direction() == RTL)
744             xPos = m_width - paddingRight() - borderRight() - legend->width() - legend->marginRight();
745         int b = borderTop();
746         int h = legend->height();
747         legend->setPos(xPos, QMAX((b-h)/2, 0));
748         m_height = QMAX(b,h) + paddingTop();
749     }
750     return legend;
751 }
752
753 RenderObject* RenderFieldset::findLegend()
754 {
755     for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) {
756       if (!legend->isFloatingOrPositioned() && legend->element() &&
757           legend->element()->id() == ID_LEGEND)
758         return legend;
759     }
760     return 0;
761 }
762
763 void RenderFieldset::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
764 {
765     //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
766
767     int w = width();
768     int h = height() + borderTopExtra() + borderBottomExtra();
769     RenderObject* legend = findLegend();
770     if (!legend)
771         return RenderBlock::paintBoxDecorations(i, _tx, _ty);
772
773     int yOff = (legend->yPos() > 0) ? 0 : (legend->height()-borderTop())/2;
774     h -= yOff;
775     _ty += yOff - borderTopExtra();
776
777     int my = kMax(_ty, i.r.y());
778     int end = kMin(i.r.y() + i.r.height(),  _ty + h);
779     int mh = end - my;
780
781     paintBackground(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
782
783     if (style()->hasBorder())
784         paintBorderMinusLegend(i.p, _tx, _ty, w, h, style(), legend->xPos(), legend->width());
785 }
786
787 void RenderFieldset::paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w, int h,
788                                             const RenderStyle* style, int lx, int lw)
789 {
790
791     const QColor& tc = style->borderTopColor();
792     const QColor& bc = style->borderBottomColor();
793
794     EBorderStyle ts = style->borderTopStyle();
795     EBorderStyle bs = style->borderBottomStyle();
796     EBorderStyle ls = style->borderLeftStyle();
797     EBorderStyle rs = style->borderRightStyle();
798
799     bool render_t = ts > BHIDDEN;
800     bool render_l = ls > BHIDDEN;
801     bool render_r = rs > BHIDDEN;
802     bool render_b = bs > BHIDDEN;
803
804     if(render_t) {
805         drawBorder(p, _tx, _ty, _tx + lx, _ty +  style->borderTopWidth(), BSTop, tc, style->color(), ts,
806                    (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0), 0);
807         drawBorder(p, _tx+lx+lw, _ty, _tx + w, _ty +  style->borderTopWidth(), BSTop, tc, style->color(), ts,
808                    0, (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
809     }
810
811     if(render_b)
812         drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
813                    (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0),
814                    (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
815
816     if(render_l)
817     {
818         const QColor& lc = style->borderLeftColor();
819
820         bool ignore_top =
821             (tc == lc) &&
822             (ls >= OUTSET) &&
823             (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
824
825         bool ignore_bottom =
826             (bc == lc) &&
827             (ls >= OUTSET) &&
828             (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
829
830         drawBorder(p, _tx, _ty, _tx + style->borderLeftWidth(), _ty + h, BSLeft, lc, style->color(), ls,
831                    ignore_top?0:style->borderTopWidth(),
832                    ignore_bottom?0:style->borderBottomWidth());
833     }
834
835     if(render_r)
836     {
837         const QColor& rc = style->borderRightColor();
838
839         bool ignore_top =
840             (tc == rc) &&
841             (rs >= DOTTED || rs == INSET) &&
842             (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
843
844         bool ignore_bottom =
845             (bc == rc) &&
846             (rs >= DOTTED || rs == INSET) &&
847             (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
848
849         drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
850                    ignore_top?0:style->borderTopWidth(),
851                    ignore_bottom?0:style->borderBottomWidth());
852     }
853 }
854
855 void RenderFieldset::setStyle(RenderStyle* _style)
856 {
857     RenderBlock::setStyle(_style);
858
859     // WinIE renders fieldsets with display:inline like they're inline-blocks.  For us,
860     // an inline-block is just a block element with replaced set to true and inline set
861     // to true.  Ensure that if we ended up being inline that we set our replaced flag
862     // so that we're treated like an inline-block.
863     if (isInline())
864         setReplaced(true);
865 }
866
867 // -------------------------------------------------------------------------
868
869 RenderFileButton::RenderFileButton(HTMLInputElementImpl *element)
870     : RenderFormElement(element)
871 {
872 #if APPLE_CHANGES
873     KWQFileButton *w = new KWQFileButton(view()->part());
874     connect(w, SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
875     connect(w, SIGNAL(clicked()), this, SLOT(slotClicked()));
876     setQWidget(w);
877 #else
878     QHBox *w = new QHBox(view()->viewport());
879
880     m_edit = new LineEditWidget(w);
881
882     connect(m_edit, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
883     connect(m_edit, SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
884
885     m_button = new QPushButton(i18n("Browse..."), w);
886     m_button->setFocusPolicy(QWidget::ClickFocus);
887     connect(m_button,SIGNAL(clicked()), this, SLOT(slotClicked()));
888
889     w->setStretchFactor(m_edit, 2);
890     w->setFocusProxy(m_edit);
891
892     setQWidget(w);
893 #endif
894 }
895
896 void RenderFileButton::calcMinMaxWidth()
897 {
898     KHTMLAssert( !minMaxKnown() );
899
900 #if APPLE_CHANGES
901     // Let the widget tell us how big it wants to be.
902     int size = element()->size();
903     QSize s(static_cast<KWQFileButton *>(widget())->sizeForCharacterWidth(size > 0 ? size : 20));
904 #else
905     const QFontMetrics &fm = style()->fontMetrics();
906     QSize s;
907     int size = element()->size();
908
909     int h = fm.lineSpacing();
910     int w = fm.width( 'x' ) * (size > 0 ? size : 17); // "some"
911     w += 6 + fm.width( m_button->text() ) + 2*fm.width( ' ' );
912     s = QSize(w + 2 + 2*m_edit->frameWidth(),
913               QMAX(h, 14) + 2 + 2*m_edit->frameWidth())
914         .expandedTo(QApplication::globalStrut());
915 #endif
916
917     setIntrinsicWidth( s.width() );
918     setIntrinsicHeight( s.height() );
919
920     RenderFormElement::calcMinMaxWidth();
921 }
922
923 #if !APPLE_CHANGES
924
925 void RenderFileButton::handleFocusOut()
926 {
927     if ( m_edit && m_edit->edited() ) {
928         element()->onChange();
929         m_edit->setEdited( false );
930     }
931 }
932
933 #endif
934
935 void RenderFileButton::slotClicked()
936 {
937 #if APPLE_CHANGES
938     RenderFormElement::slotClicked();
939 #else
940     QString file_name = KFileDialog::getOpenFileName(QString::null, QString::null, 0, i18n("Browse..."));
941     if (!file_name.isNull()) {
942         element()->m_value = DOMString(file_name);
943         m_edit->setText(file_name);
944     }
945 #endif
946 }
947
948 void RenderFileButton::updateFromElement()
949 {
950 #if APPLE_CHANGES
951     static_cast<KWQFileButton *>(widget())->setFilename(element()->value().string());
952 #else
953     m_edit->blockSignals(true);
954     m_edit->setText(element()->value().string());
955     m_edit->blockSignals(false);
956     int ml = element()->maxLength();
957     if ( ml <= 0 || ml > 1024 )
958         ml = 1024;
959     m_edit->setMaxLength( ml );
960     m_edit->setEdited( false );
961 #endif
962
963     RenderFormElement::updateFromElement();
964 }
965
966 void RenderFileButton::slotReturnPressed()
967 {
968     if (element()->form())
969         element()->form()->prepareSubmit();
970 }
971
972 void RenderFileButton::slotTextChanged(const QString &string)
973 {
974     element()->m_value = DOMString(string);
975     element()->onChange();
976 }
977
978 void RenderFileButton::select()
979 {
980 #if !APPLE_CHANGES
981     m_edit->selectAll();
982 #endif
983 }
984
985 #if APPLE_CHANGES
986
987 void RenderFileButton::click()
988 {
989     static_cast<KWQFileButton *>(widget())->click();
990 }
991
992 #endif
993
994 // -------------------------------------------------------------------------
995
996 RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element)
997     : RenderFormElement(element)
998 {
999
1000 }
1001
1002 // -------------------------------------------------------------------------
1003
1004 RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element)
1005 : RenderBlock(element)
1006 {
1007 }
1008
1009 // -------------------------------------------------------------------------------
1010
1011 ComboBoxWidget::ComboBoxWidget(QWidget *parent)
1012     : KComboBox(false, parent)
1013 {
1014     setAutoMask(true);
1015     if (listBox()) listBox()->installEventFilter(this);
1016     setMouseTracking(true);
1017 }
1018
1019 bool ComboBoxWidget::event(QEvent *e)
1020 {
1021 #if !APPLE_CHANGES
1022     if (e->type()==QEvent::KeyPress)
1023     {
1024         QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1025         switch(ke->key())
1026         {
1027         case Key_Return:
1028         case Key_Enter:
1029             popup();
1030             ke->accept();
1031             return true;
1032         default:
1033             return KComboBox::event(e);
1034         }
1035     }
1036 #endif
1037     return KComboBox::event(e);
1038 }
1039
1040 bool ComboBoxWidget::eventFilter(QObject *dest, QEvent *e)
1041 {
1042 #if !APPLE_CHANGES
1043     if (dest==listBox() &&  e->type()==QEvent::KeyPress)
1044     {
1045         QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1046         bool forward = false;
1047         switch(ke->key())
1048         {
1049         case Key_Tab:
1050             forward=true;
1051         case Key_BackTab:
1052             // ugly hack. emulate popdownlistbox() (private in QComboBox)
1053             // we re-use ke here to store the reference to the generated event.
1054             ke = new QKeyEvent(QEvent::KeyPress, Key_Escape, 0, 0);
1055             QApplication::sendEvent(dest,ke);
1056             focusNextPrevChild(forward);
1057             delete ke;
1058             return true;
1059         default:
1060             return KComboBox::eventFilter(dest, e);
1061         }
1062     }
1063 #endif
1064     return KComboBox::eventFilter(dest, e);
1065 }
1066
1067 // -------------------------------------------------------------------------
1068
1069 RenderSelect::RenderSelect(HTMLSelectElementImpl *element)
1070     : RenderFormElement(element)
1071 {
1072     m_ignoreSelectEvents = false;
1073     m_multiple = element->multiple();
1074     m_size = element->size();
1075     m_useListBox = (m_multiple || m_size > 1);
1076     m_selectionChanged = true;
1077     m_optionsChanged = true;
1078
1079     if(m_useListBox)
1080         setQWidget(createListBox());
1081     else
1082         setQWidget(createComboBox());
1083 }
1084
1085 #if APPLE_CHANGES
1086
1087 void RenderSelect::setWidgetWritingDirection()
1088 {
1089     QPainter::TextDirection d = style()->direction() == RTL ? QPainter::RTL : QPainter::LTR;
1090     if (m_useListBox)
1091         static_cast<KListBox *>(m_widget)->setWritingDirection(d);
1092     else
1093         static_cast<ComboBoxWidget *>(m_widget)->setWritingDirection(d);
1094 }
1095
1096 void RenderSelect::setStyle(RenderStyle *s)
1097 {
1098     RenderFormElement::setStyle(s);
1099     setWidgetWritingDirection();
1100 }
1101
1102 #endif
1103
1104 void RenderSelect::updateFromElement()
1105 {
1106     m_ignoreSelectEvents = true;
1107
1108     // change widget type
1109     bool oldMultiple = m_multiple;
1110     unsigned oldSize = m_size;
1111     bool oldListbox = m_useListBox;
1112
1113     m_multiple = element()->multiple();
1114     m_size = element()->size();
1115     m_useListBox = (m_multiple || m_size > 1);
1116
1117     if (oldMultiple != m_multiple || oldSize != m_size) {
1118         if (m_useListBox != oldListbox) {
1119             // type of select has changed
1120             delete m_widget;
1121
1122             if(m_useListBox)
1123                 setQWidget(createListBox());
1124             else
1125                 setQWidget(createComboBox());
1126 #if APPLE_CHANGES
1127             setWidgetWritingDirection();
1128 #endif
1129         }
1130
1131         if (m_useListBox && oldMultiple != m_multiple) {
1132             static_cast<KListBox*>(m_widget)->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
1133         }
1134         m_selectionChanged = true;
1135         m_optionsChanged = true;
1136     }
1137
1138     // update contents listbox/combobox based on options in m_element
1139     if ( m_optionsChanged ) {
1140         if (element()->m_recalcListItems)
1141             element()->recalcListItems();
1142         QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1143         int listIndex;
1144
1145         if (m_useListBox)
1146             static_cast<KListBox*>(m_widget)->clear();
1147         else
1148             static_cast<KComboBox*>(m_widget)->clear();
1149
1150         for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) {
1151             if (listItems[listIndex]->id() == ID_OPTGROUP) {
1152                 QString label = listItems[listIndex]->getAttribute(ATTR_LABEL).string();
1153                 label.replace(QChar('\\'), backslashAsCurrencySymbol());
1154
1155                 // In WinIE, an optgroup can't start or end with whitespace (other than the indent
1156                 // we give it).  We match this behavior.
1157                 label = label.stripWhiteSpace();
1158                 
1159 #if APPLE_CHANGES
1160                 if (m_useListBox)
1161                     static_cast<KListBox*>(m_widget)->appendGroupLabel(label);
1162                 else
1163                     static_cast<KComboBox*>(m_widget)->appendGroupLabel(label);
1164 #else
1165                 if(m_useListBox) {
1166                     QListBoxText *item = new QListBoxText(label);
1167                     static_cast<KListBox*>(m_widget)
1168                         ->insertItem(item, listIndex);
1169                     item->setSelectable(false);
1170                 }
1171                 else
1172                     static_cast<KComboBox*>(m_widget)->insertItem(label, listIndex);
1173 #endif
1174             }
1175             else if (listItems[listIndex]->id() == ID_OPTION) {
1176                 QString itemText = static_cast<HTMLOptionElementImpl*>(listItems[listIndex])->text().string();
1177                 itemText.replace(QChar('\\'), backslashAsCurrencySymbol());
1178
1179                 // In WinIE, leading and trailing whitespace is ignored in options. We match this behavior.
1180                 itemText = itemText.stripWhiteSpace();
1181                 
1182                 if (listItems[listIndex]->parentNode()->id() == ID_OPTGROUP)
1183                     itemText.prepend("    ");
1184
1185 #if APPLE_CHANGES
1186                 if (m_useListBox)
1187                     static_cast<KListBox*>(m_widget)->appendItem(itemText);
1188                 else
1189                     static_cast<KComboBox*>(m_widget)->appendItem(itemText);
1190 #else
1191                 if(m_useListBox)
1192                     static_cast<KListBox*>(m_widget)->insertItem(itemText, listIndex);
1193                 else
1194                     static_cast<KComboBox*>(m_widget)->insertItem(itemText, listIndex);
1195 #endif
1196             }
1197             else
1198                 KHTMLAssert(false);
1199             m_selectionChanged = true;
1200         }
1201 #if APPLE_CHANGES
1202         if (m_useListBox)
1203             static_cast<KListBox*>(m_widget)->doneAppendingItems();
1204 #endif
1205         setNeedsLayoutAndMinMaxRecalc();
1206         m_optionsChanged = false;
1207     }
1208
1209     // update selection
1210     if (m_selectionChanged) {
1211         updateSelection();
1212     }
1213
1214     m_ignoreSelectEvents = false;
1215
1216     RenderFormElement::updateFromElement();
1217 }
1218
1219 #if APPLE_CHANGES
1220
1221 short RenderSelect::baselinePosition( bool f, bool isRootLineBox ) const
1222 {
1223     if (m_useListBox) {
1224         // FIXME: Should get the hardcoded constant of 7 by calling a QListBox function,
1225         // as we do for other widget classes.
1226         return RenderWidget::baselinePosition( f, isRootLineBox ) - 7;
1227     }
1228     return RenderFormElement::baselinePosition( f, isRootLineBox );
1229 }
1230
1231 #endif
1232
1233 void RenderSelect::calcMinMaxWidth()
1234 {
1235     KHTMLAssert( !minMaxKnown() );
1236
1237     if (m_optionsChanged)
1238         updateFromElement();
1239
1240     // ### ugly HACK FIXME!!!
1241     setMinMaxKnown();
1242     layoutIfNeeded();
1243     setNeedsLayoutAndMinMaxRecalc();
1244     // ### end FIXME
1245
1246     RenderFormElement::calcMinMaxWidth();
1247 }
1248
1249 void RenderSelect::layout( )
1250 {
1251     KHTMLAssert(needsLayout());
1252     KHTMLAssert(minMaxKnown());
1253
1254     // ### maintain selection properly between type/size changes, and work
1255     // out how to handle multiselect->singleselect (probably just select
1256     // first selected one)
1257
1258     // calculate size
1259     if(m_useListBox) {
1260         KListBox* w = static_cast<KListBox*>(m_widget);
1261
1262 #if !APPLE_CHANGES
1263         QListBoxItem* p = w->firstItem();
1264         int width = 0;
1265         int height = 0;
1266         while(p) {
1267             width = QMAX(width, p->width(p->listBox()));
1268             height = QMAX(height, p->height(p->listBox()));
1269             p = p->next();
1270         }
1271 #endif
1272
1273         int size = m_size;
1274         // check if multiple and size was not given or invalid
1275         // Internet Exploder sets size to QMIN(number of elements, 4)
1276         // Netscape seems to simply set it to "number of elements"
1277         // the average of that is IMHO QMIN(number of elements, 10)
1278         // so I did that ;-)
1279         if(size < 1)
1280             size = QMIN(static_cast<KListBox*>(m_widget)->count(), 10);
1281
1282 #if APPLE_CHANGES
1283         // Let the widget tell us how big it wants to be.
1284         QSize s(w->sizeForNumberOfLines(size));
1285         setIntrinsicWidth( s.width() );
1286         setIntrinsicHeight( s.height() );
1287 #else
1288         width += 2*w->frameWidth() + w->verticalScrollBar()->sizeHint().width();
1289         height = size*height + 2*w->frameWidth();
1290
1291         setIntrinsicWidth( width );
1292         setIntrinsicHeight( height );
1293 #endif
1294     }
1295     else {
1296         QSize s(m_widget->sizeHint());
1297         setIntrinsicWidth( s.width() );
1298         setIntrinsicHeight( s.height() );
1299     }
1300
1301     /// uuh, ignore the following line..
1302     setNeedsLayout(true);
1303     RenderFormElement::layout();
1304
1305     // and now disable the widget in case there is no <option> given
1306     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1307
1308     bool foundOption = false;
1309     for (uint i = 0; i < listItems.size() && !foundOption; i++)
1310         foundOption = (listItems[i]->id() == ID_OPTION);
1311
1312     m_widget->setEnabled(foundOption && ! element()->disabled());
1313 }
1314
1315 void RenderSelect::slotSelected(int index)
1316 {
1317     if ( m_ignoreSelectEvents ) return;
1318
1319     KHTMLAssert( !m_useListBox );
1320
1321     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1322     if(index >= 0 && index < int(listItems.size()))
1323     {
1324         bool found = ( listItems[index]->id() == ID_OPTION );
1325
1326         if ( !found ) {
1327             // this one is not selectable,  we need to find an option element
1328             while ( ( unsigned ) index < listItems.size() ) {
1329                 if ( listItems[index]->id() == ID_OPTION ) {
1330                     found = true;
1331                     break;
1332                 }
1333                 ++index;
1334             }
1335
1336             if ( !found ) {
1337                 while ( index >= 0 ) {
1338                     if ( listItems[index]->id() == ID_OPTION ) {
1339                         found = true;
1340                         break;
1341                     }
1342                     --index;
1343                 }
1344             }
1345         }
1346
1347         if ( found ) {
1348             if ( index != static_cast<ComboBoxWidget*>( m_widget )->currentItem() )
1349                 static_cast<ComboBoxWidget*>( m_widget )->setCurrentItem( index );
1350
1351             for ( unsigned int i = 0; i < listItems.size(); ++i )
1352                 if ( listItems[i]->id() == ID_OPTION && i != (unsigned int) index )
1353                     static_cast<HTMLOptionElementImpl*>( listItems[i] )->m_selected = false;
1354
1355             static_cast<HTMLOptionElementImpl*>(listItems[index])->m_selected = true;
1356         }
1357     }
1358
1359     element()->onChange();
1360 }
1361
1362
1363 void RenderSelect::slotSelectionChanged()
1364 {
1365     if ( m_ignoreSelectEvents ) return;
1366
1367     // don't use listItems() here as we have to avoid recalculations - changing the
1368     // option list will make use update options not in the way the user expects them
1369     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->m_listItems;
1370     for ( unsigned i = 0; i < listItems.count(); i++ )
1371         // don't use setSelected() here because it will cause us to be called
1372         // again with updateSelection.
1373         if ( listItems[i]->id() == ID_OPTION )
1374             static_cast<HTMLOptionElementImpl*>( listItems[i] )
1375                 ->m_selected = static_cast<KListBox*>( m_widget )->isSelected( i );
1376
1377     element()->onChange();
1378 }
1379
1380
1381 void RenderSelect::setOptionsChanged(bool _optionsChanged)
1382 {
1383     m_optionsChanged = _optionsChanged;
1384 }
1385
1386 KListBox* RenderSelect::createListBox()
1387 {
1388     KListBox *lb = new KListBox(view()->viewport());
1389     lb->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
1390     // ### looks broken
1391     //lb->setAutoMask(true);
1392     connect( lb, SIGNAL( selectionChanged() ), this, SLOT( slotSelectionChanged() ) );
1393     connect( lb, SIGNAL( clicked( QListBoxItem * ) ), this, SLOT( slotClicked() ) );
1394     m_ignoreSelectEvents = false;
1395     lb->setMouseTracking(true);
1396
1397     return lb;
1398 }
1399
1400 ComboBoxWidget *RenderSelect::createComboBox()
1401 {
1402     ComboBoxWidget *cb = new ComboBoxWidget(view()->viewport());
1403     connect(cb, SIGNAL(activated(int)), this, SLOT(slotSelected(int)));
1404     return cb;
1405 }
1406
1407 void RenderSelect::updateSelection()
1408 {
1409     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1410     int i;
1411     if (m_useListBox) {
1412         // if multi-select, we select only the new selected index
1413         KListBox *listBox = static_cast<KListBox*>(m_widget);
1414         for (i = 0; i < int(listItems.size()); i++)
1415             listBox->setSelected(i,listItems[i]->id() == ID_OPTION &&
1416                                 static_cast<HTMLOptionElementImpl*>(listItems[i])->selected());
1417     }
1418     else {
1419         bool found = false;
1420         unsigned firstOption = listItems.size();
1421         i = listItems.size();
1422         while (i--)
1423             if (listItems[i]->id() == ID_OPTION) {
1424                 if (found)
1425                     static_cast<HTMLOptionElementImpl*>(listItems[i])->m_selected = false;
1426                 else if (static_cast<HTMLOptionElementImpl*>(listItems[i])->selected()) {
1427                     static_cast<KComboBox*>( m_widget )->setCurrentItem(i);
1428                     found = true;
1429                 }
1430                 firstOption = i;
1431             }
1432
1433         Q_ASSERT(firstOption == listItems.size() || found);
1434     }
1435
1436     m_selectionChanged = false;
1437 }
1438
1439
1440 // -------------------------------------------------------------------------
1441
1442 #if !APPLE_CHANGES
1443
1444 TextAreaWidget::TextAreaWidget(QWidget* parent)
1445     : KTextEdit(parent)
1446 {
1447 }
1448
1449 bool TextAreaWidget::event( QEvent *e )
1450 {
1451     if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
1452         QKeyEvent* ke = (QKeyEvent*) e;
1453         if ( ke->state() & ControlButton ) {
1454             switch ( ke->key() ) {
1455                 case Key_Left:
1456                 case Key_Right:
1457                 case Key_Up:
1458                 case Key_Down:
1459                 case Key_Home:
1460                 case Key_End:
1461                     ke->accept();
1462                 default:
1463                 break;
1464             }
1465         }
1466     }
1467     return KTextEdit::event( e );
1468 }
1469
1470 #endif
1471
1472 // -------------------------------------------------------------------------
1473
1474 RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
1475     : RenderFormElement(element)
1476 {
1477 #if APPLE_CHANGES
1478     QTextEdit *edit = new KTextEdit(view());
1479 #else
1480     QTextEdit *edit = new TextAreaWidget(view());
1481 #endif
1482
1483     if (element->wrap() != HTMLTextAreaElementImpl::ta_NoWrap)
1484         edit->setWordWrap(QTextEdit::WidgetWidth);
1485     else
1486         edit->setWordWrap(QTextEdit::NoWrap);
1487
1488 #if !APPLE_CHANGES
1489     KCursor::setAutoHideCursor(edit->viewport(), true);
1490     edit->setTextFormat(QTextEdit::PlainText);
1491     edit->setAutoMask(true);
1492     edit->setMouseTracking(true);
1493 #endif
1494
1495     setQWidget(edit);
1496
1497     connect(edit,SIGNAL(textChanged()),this,SLOT(slotTextChanged()));
1498     connect(edit,SIGNAL(clicked()),this,SLOT(slotClicked()));
1499 }
1500
1501 void RenderTextArea::detach()
1502 {
1503     if ( element()->m_dirtyvalue ) {
1504         element()->m_value = text();
1505         element()->m_dirtyvalue = false;
1506     }
1507     RenderFormElement::detach();
1508 }
1509
1510 void RenderTextArea::handleFocusOut()
1511 {
1512     if ( m_widget && element() && element()->m_dirtyvalue ) {
1513         element()->m_value = text();
1514         element()->m_dirtyvalue = false;
1515         element()->onChange();
1516     }
1517 }
1518
1519 void RenderTextArea::calcMinMaxWidth()
1520 {
1521     KHTMLAssert( !minMaxKnown() );
1522
1523     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1524 #if APPLE_CHANGES
1525     QSize size(w->sizeWithColumnsAndRows(QMAX(element()->cols(), 1), QMAX(element()->rows(), 1)));
1526 #else
1527     const QFontMetrics &m = style()->fontMetrics();
1528     w->setTabStopWidth(8 * m.width(" "));
1529     QSize size( QMAX(element()->cols(), 1)*m.width('x') + w->frameWidth() +
1530                 w->verticalScrollBar()->sizeHint().width(),
1531                 QMAX(element()->rows(), 1)*m.height() + w->frameWidth()*2 +
1532                 (w->wordWrap() == QTextEdit::NoWrap ?
1533                  w->horizontalScrollBar()->sizeHint().height() : 0)
1534         );
1535 #endif
1536
1537     setIntrinsicWidth( size.width() );
1538     setIntrinsicHeight( size.height() );
1539
1540     RenderFormElement::calcMinMaxWidth();
1541 }
1542
1543 void RenderTextArea::setStyle(RenderStyle *s)
1544 {
1545     RenderFormElement::setStyle(s);
1546
1547     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1548     w->setAlignment(textAlignment());
1549 #if APPLE_CHANGES
1550     w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
1551 #endif
1552
1553     QScrollView::ScrollBarMode scrollMode = QScrollView::Auto;
1554     switch (style()->overflow()) {
1555         case OAUTO:
1556         case OMARQUEE: // makes no sense, map to auto
1557         case OOVERLAY: // not implemented for text, map to auto
1558         case OVISIBLE:
1559             break;
1560         case OHIDDEN:
1561             scrollMode = QScrollView::AlwaysOff;
1562             break;
1563         case OSCROLL:
1564             scrollMode = QScrollView::AlwaysOn;
1565             break;
1566     }
1567     QScrollView::ScrollBarMode horizontalScrollMode = scrollMode;
1568     if (element()->wrap() != HTMLTextAreaElementImpl::ta_NoWrap)
1569         horizontalScrollMode = QScrollView::AlwaysOff;
1570
1571 #if APPLE_CHANGES
1572     w->setScrollBarModes(horizontalScrollMode, scrollMode);
1573 #else
1574     w->setHScrollBarMode(horizontalScrollMode);
1575     w->setVScrollBarMode(scrollMode);
1576 #endif
1577 }
1578
1579 void RenderTextArea::updateFromElement()
1580 {
1581     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1582     w->setReadOnly(element()->readOnly());
1583 #if APPLE_CHANGES
1584     w->setDisabled(element()->disabled());
1585 #endif
1586     
1587     // Call text() before calling element()->value(), because in the case of inline
1588     // input such as Hiragana, w->text() has a side effect of sending the notification
1589     // that we use in slotTextChanged to update element()->m_value
1590     QString widgetText = text();
1591     QString text = element()->value().string();
1592     text.replace(QChar('\\'), backslashAsCurrencySymbol());
1593     if (widgetText != text) {
1594         w->blockSignals(true);
1595         int line, col;
1596         w->getCursorPosition( &line, &col );
1597         w->setText(text);
1598         w->setCursorPosition( line, col );
1599         w->blockSignals(false);
1600     }
1601     element()->m_dirtyvalue = false;
1602
1603     RenderFormElement::updateFromElement();
1604 }
1605
1606 QString RenderTextArea::text()
1607 {
1608     QString txt;
1609     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1610
1611     if (element()->wrap() == HTMLTextAreaElementImpl::ta_Physical) {
1612 #if APPLE_CHANGES
1613         txt = w->textWithHardLineBreaks();
1614 #else
1615         // yeah, QTextEdit has no accessor for getting the visually wrapped text
1616         for (int p=0; p < w->paragraphs(); ++p) {
1617             int pl = w->paragraphLength(p);
1618             int ll = 0;
1619             int lindex = w->lineOfChar(p, 0);
1620             QString paragraphText = w->text(p);
1621             for (int l = 0; l < pl; ++l) {
1622                 if (lindex != w->lineOfChar(p, l)) {
1623                     paragraphText.insert(l+ll++, QString::fromLatin1("\n"));
1624                     lindex = w->lineOfChar(p, l);
1625                 }
1626             }
1627             txt += paragraphText;
1628             if (p < w->paragraphs() - 1)
1629                 txt += QString::fromLatin1("\n");
1630         }
1631 #endif
1632     }
1633     else
1634         txt = w->text();
1635
1636     txt.replace(backslashAsCurrencySymbol(), QChar('\\'));
1637     return txt;
1638 }
1639
1640 void RenderTextArea::slotTextChanged()
1641 {
1642     element()->m_dirtyvalue = true;
1643 }
1644
1645 void RenderTextArea::select()
1646 {
1647     static_cast<QTextEdit *>(m_widget)->selectAll();
1648 }
1649
1650 // ---------------------------------------------------------------------------
1651
1652 #if APPLE_CHANGES
1653 RenderSlider::RenderSlider(HTMLInputElementImpl* element)
1654 :RenderFormElement(element)
1655 {
1656     QSlider* slider = new QSlider();
1657     setQWidget(slider);
1658     connect(slider, SIGNAL(sliderValueChanged()), this, SLOT(slotSliderValueChanged()));
1659     connect(slider, SIGNAL(clicked()), this, SLOT(slotClicked()));
1660 }
1661
1662 void RenderSlider::calcMinMaxWidth()
1663 {
1664     KHTMLAssert(!minMaxKnown());
1665     
1666     // Let the widget tell us how big it wants to be.
1667     QSize s(widget()->sizeHint());
1668     bool widthSet = !style()->width().isVariable();
1669     bool heightSet = !style()->height().isVariable();
1670     if (heightSet && !widthSet) {
1671         // Flip the intrinsic dimensions.
1672         int barLength = s.width();
1673         s = QSize(s.height(), barLength);
1674     }
1675     setIntrinsicWidth(s.width());
1676     setIntrinsicHeight(s.height());
1677     
1678     RenderFormElement::calcMinMaxWidth();
1679 }
1680
1681 void RenderSlider::updateFromElement()
1682 {
1683     const DOMString& value = element()->value();
1684     const DOMString& min = element()->getAttribute(ATTR_MIN);
1685     const DOMString& max = element()->getAttribute(ATTR_MAX);
1686     const DOMString& precision = element()->getAttribute(ATTR_PRECISION);
1687     
1688     double minVal = min.isNull() ? 0.0 : min.string().toDouble();
1689     double maxVal = max.isNull() ? 100.0 : max.string().toDouble();
1690     minVal = kMin(minVal, maxVal); // Make sure the range is sane.
1691     
1692     double val = value.isNull() ? (maxVal + minVal)/2.0 : value.string().toDouble();
1693     val = kMax(minVal, kMin(val, maxVal)); // Make sure val is within min/max.
1694     
1695     // Force integer value if not float (strcasecmp returns confusingly backward boolean).
1696     if (strcasecmp(precision, "float"))
1697         val = (int)(val + 0.5);
1698
1699     element()->setValue(QString::number(val));
1700
1701     QSlider* slider = (QSlider*)widget();
1702      
1703     slider->setMinValue(minVal);
1704     slider->setMaxValue(maxVal);
1705     slider->setValue(val);
1706
1707     RenderFormElement::updateFromElement();
1708 }
1709
1710 void RenderSlider::slotSliderValueChanged()
1711 {
1712     QSlider* slider = (QSlider*)widget();
1713
1714     double val = slider->value();
1715     const DOMString& precision = element()->getAttribute(ATTR_PRECISION);
1716
1717     // Force integer value if not float (strcasecmp returns confusingly backward boolean).
1718     if (strcasecmp(precision, "float"))
1719         val = (int)(val + 0.5);
1720
1721     element()->setValue(QString::number(val));
1722     
1723     // Fire the "input" DOM event.
1724     element()->dispatchHTMLEvent(EventImpl::INPUT_EVENT, true, false);
1725 }
1726
1727 void RenderSlider::slotClicked()
1728 {
1729     // emit mouseClick event etc
1730     RenderFormElement::slotClicked();
1731 }
1732
1733 #endif
1734
1735 #include "render_form.moc"