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