Reviewed by Dave Hyatt.
[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     // FIXME: Should share code with KHTMLView::dispatchMouseEvent, which does a lot of the same stuff.
187
188     RenderArena *arena = ref();
189
190 #if APPLE_CHANGES
191     QMouseEvent event(QEvent::MouseButtonRelease); // gets "current event"
192     element()->dispatchMouseEvent(&event, EventImpl::CLICK_EVENT, event.clickCount());
193 #else
194     // We also send the KHTML_CLICK or KHTML_DBLCLICK event for
195     // CLICK. This is not part of the DOM specs, but is used for
196     // compatibility with the traditional onclick="" and ondblclick=""
197     // attributes, as there is no way to tell the difference between
198     // single & double clicks using DOM (only the click count is
199     // stored, which is not necessarily the same)
200
201     QMouseEvent e2(QEvent::MouseButtonRelease, m_mousePos, m_button, m_state);
202     element()->dispatchMouseEvent(&e2, EventImpl::CLICK_EVENT, m_clickCount);
203     element()->dispatchMouseEvent(&e2, m_isDoubleClick ? EventImpl::KHTML_DBLCLICK_EVENT : EventImpl::KHTML_CLICK_EVENT, m_clickCount);
204 #endif
205
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 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()->valueWithDefault().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 short RenderSubmitButton::baselinePosition( bool f, bool isRootLineBox ) const
449 {
450     return RenderFormElement::baselinePosition( f, isRootLineBox );
451 }
452
453 // -------------------------------------------------------------------------------
454
455 RenderImageButton::RenderImageButton(HTMLInputElementImpl *element)
456     : RenderImage(element)
457 {
458     // ### support DOMActivate event when clicked
459 }
460
461 // -------------------------------------------------------------------------------
462
463 // FIXME: No real reason to need separate classes for RenderResetButton and
464 // RenderSubmitButton now that the default label is handled on the DOM side.
465 RenderResetButton::RenderResetButton(HTMLInputElementImpl *element)
466     : RenderSubmitButton(element)
467 {
468 }
469
470 // -------------------------------------------------------------------------------
471
472 // FIXME: No real reason to need separate classes for RenderPushButton and
473 // RenderSubmitButton now that the default label is handled on the DOM side.
474 RenderPushButton::RenderPushButton(HTMLInputElementImpl *element)
475     : RenderSubmitButton(element)
476 {
477 }
478
479 // -------------------------------------------------------------------------------
480
481 #if !APPLE_CHANGES
482
483 LineEditWidget::LineEditWidget(QWidget *parent)
484         : KLineEdit(parent)
485 {
486     setMouseTracking(true);
487 }
488
489 bool LineEditWidget::event( QEvent *e )
490 {
491     if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
492         QKeyEvent* ke = (QKeyEvent*) e;
493         if ( ke->state() & ControlButton ) {
494             switch ( ke->key() ) {
495                 case Key_Left:
496                 case Key_Right:
497                 case Key_Up:
498                 case Key_Down:
499                 case Key_Home:
500                 case Key_End:
501                     ke->accept();
502                 default:
503                 break;
504             }
505         }
506     }
507     return KLineEdit::event( e );
508 }
509
510 #endif
511
512 // -----------------------------------------------------------------------------
513
514 RenderLineEdit::RenderLineEdit(HTMLInputElementImpl *element)
515     : RenderFormElement(element), m_updating(false)
516 {
517 #if APPLE_CHANGES
518     QLineEdit::Type type;
519     switch (element->inputType()) {
520         case HTMLInputElementImpl::PASSWORD:
521             type = QLineEdit::Password;
522             break;
523         case HTMLInputElementImpl::SEARCH:
524             type = QLineEdit::Search;
525             break;
526         default:
527             type = QLineEdit::Normal;
528     }
529     KLineEdit *edit = new KLineEdit(type);
530     if (type == QLineEdit::Search)
531         edit->setLiveSearch(false);
532 #else
533     LineEditWidget *edit = new LineEditWidget(view()->viewport());
534 #endif
535     connect(edit,SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
536     connect(edit,SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
537     connect(edit,SIGNAL(clicked()),this,SLOT(slotClicked()));
538
539 #if APPLE_CHANGES
540     connect(edit,SIGNAL(performSearch()), this, SLOT(slotPerformSearch()));
541 #endif
542
543 #if !APPLE_CHANGES
544     if(element->inputType() == HTMLInputElementImpl::PASSWORD)
545         edit->setEchoMode( QLineEdit::Password );
546
547     if ( element->autoComplete() ) {
548         QStringList completions = view()->formCompletionItems(element->name().string());
549         if (completions.count()) {
550             edit->completionObject()->setItems(completions);
551             edit->setContextMenuEnabled(true);
552         }
553     }
554 #endif
555
556     setQWidget(edit);
557 }
558
559 void RenderLineEdit::slotReturnPressed()
560 {
561 #if !APPLE_CHANGES
562     // don't submit the form when return was pressed in a completion-popup
563     KCompletionBox *box = widget()->completionBox(false);
564     if ( box && box->isVisible() && box->currentItem() != -1 )
565         return;
566 #endif
567
568     // Emit onChange if necessary
569     // Works but might not be enough, dirk said he had another solution at
570     // hand (can't remember which) - David
571     handleFocusOut();
572
573     HTMLFormElementImpl* fe = element()->form();
574     if ( fe )
575         fe->submitClick();
576 }
577
578 #if APPLE_CHANGES
579 void RenderLineEdit::slotPerformSearch()
580 {
581     // Fire the "search" DOM event.
582     element()->dispatchHTMLEvent(EventImpl::SEARCH_EVENT, true, false);
583 }
584
585 void RenderLineEdit::addSearchResult()
586 {
587     if (widget())
588         widget()->addSearchResult();
589 }
590 #endif
591
592 void RenderLineEdit::handleFocusOut()
593 {
594     if ( widget() && widget()->edited() ) {
595         element()->onChange();
596         widget()->setEdited( false );
597     }
598 }
599
600 void RenderLineEdit::calcMinMaxWidth()
601 {
602     KHTMLAssert( !minMaxKnown() );
603
604 #if APPLE_CHANGES
605     // Let the widget tell us how big it wants to be.
606     m_updating = true;
607     int size = element()->size();
608     QSize s(widget()->sizeForCharacterWidth(size > 0 ? size : 20));
609     m_updating = false;
610 #else
611     const QFontMetrics &fm = style()->fontMetrics();
612     QSize s;
613
614     int size = element()->size();
615
616     int h = fm.lineSpacing();
617     int w = fm.width( 'x' ) * (size > 0 ? size : 17); // "some"
618     s = QSize(w + 2 + 2*widget()->frameWidth(),
619               kMax(h, 14) + 2 + 2*widget()->frameWidth())
620         .expandedTo(QApplication::globalStrut());
621 #endif
622
623     setIntrinsicWidth( s.width() );
624     setIntrinsicHeight( s.height() );
625
626     RenderFormElement::calcMinMaxWidth();
627 }
628
629 void RenderLineEdit::setStyle(RenderStyle *s)
630 {
631     RenderFormElement::setStyle(s);
632
633     KLineEdit *w = widget();
634     w->setAlignment(textAlignment());
635 #if APPLE_CHANGES
636     w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
637 #endif
638 }
639
640 void RenderLineEdit::updateFromElement()
641 {
642     HTMLInputElementImpl *e = element();
643     KLineEdit *w = widget();
644     
645     int ml = e->maxLength();
646     if ( ml <= 0 || ml > 1024 )
647         ml = 1024;
648     if ( w->maxLength() != ml )
649         w->setMaxLength( ml );
650
651     if (!e->valueMatchesRenderer()) {
652         QString widgetText = w->text();
653         QString newText = e->value().string();
654         newText.replace(QChar('\\'), backslashAsCurrencySymbol());
655         if (widgetText != newText) {
656             w->blockSignals(true);
657             int pos = w->cursorPosition();
658
659             m_updating = true;
660             w->setText(newText);
661             m_updating = false;
662             
663             w->setEdited( false );
664
665             w->setCursorPosition(pos);
666             w->blockSignals(false);
667         }
668         e->setValueMatchesRenderer();
669     }
670
671     w->setReadOnly(e->readOnly());
672     
673 #if APPLE_CHANGES
674     // Handle updating the search attributes.
675     w->setPlaceholderString(e->getAttribute(ATTR_PLACEHOLDER).string());
676     if (w->type() == QLineEdit::Search) {
677         w->setLiveSearch(!e->getAttribute(ATTR_INCREMENTAL).isNull());
678         w->setAutoSaveName(e->getAttribute(ATTR_AUTOSAVE).string());
679         w->setMaxResults(e->maxResults());
680     }
681 #endif
682
683     RenderFormElement::updateFromElement();
684 }
685
686 void RenderLineEdit::slotTextChanged(const QString &string)
687 {
688     if (m_updating) // Don't alter the value if we are in the middle of initing the control, since
689         return;     // we are getting the value from the DOM and it's not user input.
690
691     // A null string value is used to indicate that the form control has not altered the original
692     // default value.  That means that we should never use the null string value when the user
693     // empties a textfield, but should always force an empty textfield to use the empty string.
694     QString newText = string.isNull() ? "" : string;
695     newText.replace(backslashAsCurrencySymbol(), QChar('\\'));
696     element()->setValueFromRenderer(newText);
697 }
698
699 void RenderLineEdit::select()
700 {
701     static_cast<KLineEdit*>(m_widget)->selectAll();
702 }
703
704 // ---------------------------------------------------------------------------
705
706 RenderFieldset::RenderFieldset(HTMLGenericFormElementImpl *element)
707 : RenderBlock(element)
708 {
709 }
710
711 RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren)
712 {
713     RenderObject* legend = findLegend();
714     if (legend) {
715         if (relayoutChildren)
716             legend->setNeedsLayout(true);
717         legend->layoutIfNeeded();
718
719         int xPos = borderLeft() + paddingLeft() + legend->marginLeft();
720         if (style()->direction() == RTL)
721             xPos = m_width - paddingRight() - borderRight() - legend->width() - legend->marginRight();
722         int b = borderTop();
723         int h = legend->height();
724         legend->setPos(xPos, kMax((b-h)/2, 0));
725         m_height = kMax(b,h) + paddingTop();
726     }
727     return legend;
728 }
729
730 RenderObject* RenderFieldset::findLegend()
731 {
732     for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) {
733       if (!legend->isFloatingOrPositioned() && legend->element() &&
734           legend->element()->id() == ID_LEGEND)
735         return legend;
736     }
737     return 0;
738 }
739
740 void RenderFieldset::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
741 {
742     //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
743
744     int w = width();
745     int h = height() + borderTopExtra() + borderBottomExtra();
746     RenderObject* legend = findLegend();
747     if (!legend)
748         return RenderBlock::paintBoxDecorations(i, _tx, _ty);
749
750     int yOff = (legend->yPos() > 0) ? 0 : (legend->height()-borderTop())/2;
751     h -= yOff;
752     _ty += yOff - borderTopExtra();
753
754     int my = kMax(_ty, i.r.y());
755     int end = kMin(i.r.y() + i.r.height(),  _ty + h);
756     int mh = end - my;
757
758     paintBackground(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
759
760     if (style()->hasBorder())
761         paintBorderMinusLegend(i.p, _tx, _ty, w, h, style(), legend->xPos(), legend->width());
762 }
763
764 void RenderFieldset::paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w, int h,
765                                             const RenderStyle* style, int lx, int lw)
766 {
767
768     const QColor& tc = style->borderTopColor();
769     const QColor& bc = style->borderBottomColor();
770
771     EBorderStyle ts = style->borderTopStyle();
772     EBorderStyle bs = style->borderBottomStyle();
773     EBorderStyle ls = style->borderLeftStyle();
774     EBorderStyle rs = style->borderRightStyle();
775
776     bool render_t = ts > BHIDDEN;
777     bool render_l = ls > BHIDDEN;
778     bool render_r = rs > BHIDDEN;
779     bool render_b = bs > BHIDDEN;
780
781     if(render_t) {
782         drawBorder(p, _tx, _ty, _tx + lx, _ty +  style->borderTopWidth(), BSTop, tc, style->color(), ts,
783                    (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0), 0);
784         drawBorder(p, _tx+lx+lw, _ty, _tx + w, _ty +  style->borderTopWidth(), BSTop, tc, style->color(), ts,
785                    0, (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
786     }
787
788     if(render_b)
789         drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
790                    (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0),
791                    (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
792
793     if(render_l)
794     {
795         const QColor& lc = style->borderLeftColor();
796
797         bool ignore_top =
798             (tc == lc) &&
799             (ls >= OUTSET) &&
800             (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
801
802         bool ignore_bottom =
803             (bc == lc) &&
804             (ls >= OUTSET) &&
805             (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
806
807         drawBorder(p, _tx, _ty, _tx + style->borderLeftWidth(), _ty + h, BSLeft, lc, style->color(), ls,
808                    ignore_top?0:style->borderTopWidth(),
809                    ignore_bottom?0:style->borderBottomWidth());
810     }
811
812     if(render_r)
813     {
814         const QColor& rc = style->borderRightColor();
815
816         bool ignore_top =
817             (tc == rc) &&
818             (rs >= DOTTED || rs == INSET) &&
819             (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
820
821         bool ignore_bottom =
822             (bc == rc) &&
823             (rs >= DOTTED || rs == INSET) &&
824             (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
825
826         drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
827                    ignore_top?0:style->borderTopWidth(),
828                    ignore_bottom?0:style->borderBottomWidth());
829     }
830 }
831
832 void RenderFieldset::setStyle(RenderStyle* _style)
833 {
834     RenderBlock::setStyle(_style);
835
836     // WinIE renders fieldsets with display:inline like they're inline-blocks.  For us,
837     // an inline-block is just a block element with replaced set to true and inline set
838     // to true.  Ensure that if we ended up being inline that we set our replaced flag
839     // so that we're treated like an inline-block.
840     if (isInline())
841         setReplaced(true);
842 }
843
844 // -------------------------------------------------------------------------
845
846 RenderFileButton::RenderFileButton(HTMLInputElementImpl *element)
847     : RenderFormElement(element)
848 {
849 #if APPLE_CHANGES
850     KWQFileButton *w = new KWQFileButton(view()->part());
851     connect(w, SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
852     connect(w, SIGNAL(clicked()), this, SLOT(slotClicked()));
853     setQWidget(w);
854 #else
855     QHBox *w = new QHBox(view()->viewport());
856
857     m_edit = new LineEditWidget(w);
858
859     connect(m_edit, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
860     connect(m_edit, SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
861
862     m_button = new QPushButton(i18n("Browse..."), w);
863     m_button->setFocusPolicy(QWidget::ClickFocus);
864     connect(m_button,SIGNAL(clicked()), this, SLOT(slotClicked()));
865
866     w->setStretchFactor(m_edit, 2);
867     w->setFocusProxy(m_edit);
868
869     setQWidget(w);
870 #endif
871 }
872
873 void RenderFileButton::calcMinMaxWidth()
874 {
875     KHTMLAssert( !minMaxKnown() );
876
877 #if APPLE_CHANGES
878     // Let the widget tell us how big it wants to be.
879     int size = element()->size();
880     QSize s(static_cast<KWQFileButton *>(widget())->sizeForCharacterWidth(size > 0 ? size : 20));
881 #else
882     const QFontMetrics &fm = style()->fontMetrics();
883     QSize s;
884     int size = element()->size();
885
886     int h = fm.lineSpacing();
887     int w = fm.width( 'x' ) * (size > 0 ? size : 17); // "some"
888     w += 6 + fm.width( m_button->text() ) + 2*fm.width( ' ' );
889     s = QSize(w + 2 + 2*m_edit->frameWidth(),
890               kMax(h, 14) + 2 + 2*m_edit->frameWidth())
891         .expandedTo(QApplication::globalStrut());
892 #endif
893
894     setIntrinsicWidth( s.width() );
895     setIntrinsicHeight( s.height() );
896
897     RenderFormElement::calcMinMaxWidth();
898 }
899
900 #if !APPLE_CHANGES
901
902 void RenderFileButton::handleFocusOut()
903 {
904     if ( m_edit && m_edit->edited() ) {
905         element()->onChange();
906         m_edit->setEdited( false );
907     }
908 }
909
910 #endif
911
912 void RenderFileButton::slotClicked()
913 {
914 #if APPLE_CHANGES
915     RenderFormElement::slotClicked();
916 #else
917     QString file_name = KFileDialog::getOpenFileName(QString::null, QString::null, 0, i18n("Browse..."));
918     if (!file_name.isNull()) {
919         element()->m_value = DOMString(file_name);
920         m_edit->setText(file_name);
921     }
922 #endif
923 }
924
925 void RenderFileButton::updateFromElement()
926 {
927 #if APPLE_CHANGES
928     static_cast<KWQFileButton *>(widget())->setFilename(element()->value().string());
929 #else
930     m_edit->blockSignals(true);
931     m_edit->setText(element()->value().string());
932     m_edit->blockSignals(false);
933     int ml = element()->maxLength();
934     if ( ml <= 0 || ml > 1024 )
935         ml = 1024;
936     m_edit->setMaxLength( ml );
937     m_edit->setEdited( false );
938 #endif
939
940     RenderFormElement::updateFromElement();
941 }
942
943 void RenderFileButton::slotReturnPressed()
944 {
945     if (element()->form())
946         element()->form()->prepareSubmit();
947 }
948
949 void RenderFileButton::slotTextChanged(const QString &string)
950 {
951     element()->m_value = DOMString(string);
952     element()->onChange();
953 }
954
955 void RenderFileButton::select()
956 {
957 #if !APPLE_CHANGES
958     m_edit->selectAll();
959 #endif
960 }
961
962 #if APPLE_CHANGES
963
964 void RenderFileButton::click(bool sendMouseEvents)
965 {
966     static_cast<KWQFileButton *>(widget())->click(sendMouseEvents);
967 }
968
969 #endif
970
971 // -------------------------------------------------------------------------
972
973 RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element)
974     : RenderFormElement(element)
975 {
976
977 }
978
979 // -------------------------------------------------------------------------
980
981 RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element)
982 : RenderBlock(element)
983 {
984 }
985
986 // -------------------------------------------------------------------------------
987
988 ComboBoxWidget::ComboBoxWidget(QWidget *parent)
989     : KComboBox(false, parent)
990 {
991     setAutoMask(true);
992     if (listBox()) listBox()->installEventFilter(this);
993     setMouseTracking(true);
994 }
995
996 bool ComboBoxWidget::event(QEvent *e)
997 {
998 #if !APPLE_CHANGES
999     if (e->type()==QEvent::KeyPress)
1000     {
1001         QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1002         switch(ke->key())
1003         {
1004         case Key_Return:
1005         case Key_Enter:
1006             popup();
1007             ke->accept();
1008             return true;
1009         default:
1010             return KComboBox::event(e);
1011         }
1012     }
1013 #endif
1014     return KComboBox::event(e);
1015 }
1016
1017 bool ComboBoxWidget::eventFilter(QObject *dest, QEvent *e)
1018 {
1019 #if !APPLE_CHANGES
1020     if (dest==listBox() &&  e->type()==QEvent::KeyPress)
1021     {
1022         QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1023         bool forward = false;
1024         switch(ke->key())
1025         {
1026         case Key_Tab:
1027             forward=true;
1028         case Key_BackTab:
1029             // ugly hack. emulate popdownlistbox() (private in QComboBox)
1030             // we re-use ke here to store the reference to the generated event.
1031             ke = new QKeyEvent(QEvent::KeyPress, Key_Escape, 0, 0);
1032             QApplication::sendEvent(dest,ke);
1033             focusNextPrevChild(forward);
1034             delete ke;
1035             return true;
1036         default:
1037             return KComboBox::eventFilter(dest, e);
1038         }
1039     }
1040 #endif
1041     return KComboBox::eventFilter(dest, e);
1042 }
1043
1044 // -------------------------------------------------------------------------
1045
1046 RenderSelect::RenderSelect(HTMLSelectElementImpl *element)
1047     : RenderFormElement(element)
1048 {
1049     m_ignoreSelectEvents = false;
1050     m_multiple = element->multiple();
1051     m_size = element->size();
1052     m_useListBox = (m_multiple || m_size > 1);
1053     m_selectionChanged = true;
1054     m_optionsChanged = true;
1055
1056     if(m_useListBox)
1057         setQWidget(createListBox());
1058     else
1059         setQWidget(createComboBox());
1060 }
1061
1062 #if APPLE_CHANGES
1063
1064 void RenderSelect::setWidgetWritingDirection()
1065 {
1066     QPainter::TextDirection d = style()->direction() == RTL ? QPainter::RTL : QPainter::LTR;
1067     if (m_useListBox)
1068         static_cast<KListBox *>(m_widget)->setWritingDirection(d);
1069     else
1070         static_cast<ComboBoxWidget *>(m_widget)->setWritingDirection(d);
1071 }
1072
1073 void RenderSelect::setStyle(RenderStyle *s)
1074 {
1075     RenderFormElement::setStyle(s);
1076     setWidgetWritingDirection();
1077 }
1078
1079 #endif
1080
1081 void RenderSelect::updateFromElement()
1082 {
1083     m_ignoreSelectEvents = true;
1084
1085     // change widget type
1086     bool oldMultiple = m_multiple;
1087     unsigned oldSize = m_size;
1088     bool oldListbox = m_useListBox;
1089
1090     m_multiple = element()->multiple();
1091     m_size = element()->size();
1092     m_useListBox = (m_multiple || m_size > 1);
1093
1094     if (oldMultiple != m_multiple || oldSize != m_size) {
1095         if (m_useListBox != oldListbox) {
1096             // type of select has changed
1097             delete m_widget;
1098
1099             if(m_useListBox)
1100                 setQWidget(createListBox());
1101             else
1102                 setQWidget(createComboBox());
1103 #if APPLE_CHANGES
1104             setWidgetWritingDirection();
1105 #endif
1106         }
1107
1108         if (m_useListBox && oldMultiple != m_multiple) {
1109             static_cast<KListBox*>(m_widget)->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
1110         }
1111         m_selectionChanged = true;
1112         m_optionsChanged = true;
1113     }
1114
1115     // update contents listbox/combobox based on options in m_element
1116     if ( m_optionsChanged ) {
1117         if (element()->m_recalcListItems)
1118             element()->recalcListItems();
1119         QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1120         int listIndex;
1121
1122         if (m_useListBox)
1123             static_cast<KListBox*>(m_widget)->clear();
1124         else
1125             static_cast<KComboBox*>(m_widget)->clear();
1126
1127         for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) {
1128             if (listItems[listIndex]->id() == ID_OPTGROUP) {
1129                 QString label = listItems[listIndex]->getAttribute(ATTR_LABEL).string();
1130                 label.replace(QChar('\\'), backslashAsCurrencySymbol());
1131
1132                 // In WinIE, an optgroup can't start or end with whitespace (other than the indent
1133                 // we give it).  We match this behavior.
1134                 label = label.stripWhiteSpace();
1135                 
1136 #if APPLE_CHANGES
1137                 if (m_useListBox)
1138                     static_cast<KListBox*>(m_widget)->appendGroupLabel(label);
1139                 else
1140                     static_cast<KComboBox*>(m_widget)->appendGroupLabel(label);
1141 #else
1142                 if(m_useListBox) {
1143                     QListBoxText *item = new QListBoxText(label);
1144                     static_cast<KListBox*>(m_widget)
1145                         ->insertItem(item, listIndex);
1146                     item->setSelectable(false);
1147                 }
1148                 else
1149                     static_cast<KComboBox*>(m_widget)->insertItem(label, listIndex);
1150 #endif
1151             }
1152             else if (listItems[listIndex]->id() == ID_OPTION) {
1153                 QString itemText = static_cast<HTMLOptionElementImpl*>(listItems[listIndex])->text().string();
1154                 itemText.replace(QChar('\\'), backslashAsCurrencySymbol());
1155
1156                 // In WinIE, leading and trailing whitespace is ignored in options. We match this behavior.
1157                 itemText = itemText.stripWhiteSpace();
1158                 
1159                 if (listItems[listIndex]->parentNode()->id() == ID_OPTGROUP)
1160                     itemText.prepend("    ");
1161
1162 #if APPLE_CHANGES
1163                 if (m_useListBox)
1164                     static_cast<KListBox*>(m_widget)->appendItem(itemText);
1165                 else
1166                     static_cast<KComboBox*>(m_widget)->appendItem(itemText);
1167 #else
1168                 if(m_useListBox)
1169                     static_cast<KListBox*>(m_widget)->insertItem(itemText, listIndex);
1170                 else
1171                     static_cast<KComboBox*>(m_widget)->insertItem(itemText, listIndex);
1172 #endif
1173             }
1174             else
1175                 KHTMLAssert(false);
1176             m_selectionChanged = true;
1177         }
1178 #if APPLE_CHANGES
1179         if (m_useListBox)
1180             static_cast<KListBox*>(m_widget)->doneAppendingItems();
1181 #endif
1182         setNeedsLayoutAndMinMaxRecalc();
1183         m_optionsChanged = false;
1184     }
1185
1186     // update selection
1187     if (m_selectionChanged) {
1188         updateSelection();
1189     }
1190
1191     m_ignoreSelectEvents = false;
1192
1193     RenderFormElement::updateFromElement();
1194 }
1195
1196 #if APPLE_CHANGES
1197
1198 short RenderSelect::baselinePosition( bool f, bool isRootLineBox ) const
1199 {
1200     if (m_useListBox) {
1201         // FIXME: Should get the hardcoded constant of 7 by calling a QListBox function,
1202         // as we do for other widget classes.
1203         return RenderWidget::baselinePosition( f, isRootLineBox ) - 7;
1204     }
1205     return RenderFormElement::baselinePosition( f, isRootLineBox );
1206 }
1207
1208 #endif
1209
1210 void RenderSelect::calcMinMaxWidth()
1211 {
1212     KHTMLAssert( !minMaxKnown() );
1213
1214     if (m_optionsChanged)
1215         updateFromElement();
1216
1217     // ### ugly HACK FIXME!!!
1218     setMinMaxKnown();
1219     layoutIfNeeded();
1220     setNeedsLayoutAndMinMaxRecalc();
1221     // ### end FIXME
1222
1223     RenderFormElement::calcMinMaxWidth();
1224 }
1225
1226 void RenderSelect::layout( )
1227 {
1228     KHTMLAssert(needsLayout());
1229     KHTMLAssert(minMaxKnown());
1230
1231     // ### maintain selection properly between type/size changes, and work
1232     // out how to handle multiselect->singleselect (probably just select
1233     // first selected one)
1234
1235     // calculate size
1236     if(m_useListBox) {
1237         KListBox* w = static_cast<KListBox*>(m_widget);
1238
1239 #if !APPLE_CHANGES
1240         QListBoxItem* p = w->firstItem();
1241         int width = 0;
1242         int height = 0;
1243         while(p) {
1244             width = kMax(width, p->width(p->listBox()));
1245             height = kMax(height, p->height(p->listBox()));
1246             p = p->next();
1247         }
1248 #endif
1249
1250         int size = m_size;
1251         // check if multiple and size was not given or invalid
1252         // Internet Exploder sets size to kMin(number of elements, 4)
1253         // Netscape seems to simply set it to "number of elements"
1254         // the average of that is IMHO kMin(number of elements, 10)
1255         // so I did that ;-)
1256         if(size < 1)
1257             size = kMin(static_cast<KListBox*>(m_widget)->count(), 10U);
1258
1259 #if APPLE_CHANGES
1260         // Let the widget tell us how big it wants to be.
1261         QSize s(w->sizeForNumberOfLines(size));
1262         setIntrinsicWidth( s.width() );
1263         setIntrinsicHeight( s.height() );
1264 #else
1265         width += 2*w->frameWidth() + w->verticalScrollBar()->sizeHint().width();
1266         height = size*height + 2*w->frameWidth();
1267
1268         setIntrinsicWidth( width );
1269         setIntrinsicHeight( height );
1270 #endif
1271     }
1272     else {
1273         QSize s(m_widget->sizeHint());
1274         setIntrinsicWidth( s.width() );
1275         setIntrinsicHeight( s.height() );
1276     }
1277
1278     /// uuh, ignore the following line..
1279     setNeedsLayout(true);
1280     RenderFormElement::layout();
1281
1282     // and now disable the widget in case there is no <option> given
1283     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1284
1285     bool foundOption = false;
1286     for (uint i = 0; i < listItems.size() && !foundOption; i++)
1287         foundOption = (listItems[i]->id() == ID_OPTION);
1288
1289     m_widget->setEnabled(foundOption && ! element()->disabled());
1290 }
1291
1292 void RenderSelect::slotSelected(int index)
1293 {
1294     if ( m_ignoreSelectEvents ) return;
1295
1296     KHTMLAssert( !m_useListBox );
1297
1298     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1299     if(index >= 0 && index < int(listItems.size()))
1300     {
1301         bool found = ( listItems[index]->id() == ID_OPTION );
1302
1303         if ( !found ) {
1304             // this one is not selectable,  we need to find an option element
1305             while ( ( unsigned ) index < listItems.size() ) {
1306                 if ( listItems[index]->id() == ID_OPTION ) {
1307                     found = true;
1308                     break;
1309                 }
1310                 ++index;
1311             }
1312
1313             if ( !found ) {
1314                 while ( index >= 0 ) {
1315                     if ( listItems[index]->id() == ID_OPTION ) {
1316                         found = true;
1317                         break;
1318                     }
1319                     --index;
1320                 }
1321             }
1322         }
1323
1324         if ( found ) {
1325             if ( index != static_cast<ComboBoxWidget*>( m_widget )->currentItem() )
1326                 static_cast<ComboBoxWidget*>( m_widget )->setCurrentItem( index );
1327
1328             for ( unsigned int i = 0; i < listItems.size(); ++i )
1329                 if ( listItems[i]->id() == ID_OPTION && i != (unsigned int) index )
1330                     static_cast<HTMLOptionElementImpl*>( listItems[i] )->m_selected = false;
1331
1332             static_cast<HTMLOptionElementImpl*>(listItems[index])->m_selected = true;
1333         }
1334     }
1335
1336     element()->onChange();
1337 }
1338
1339
1340 void RenderSelect::slotSelectionChanged()
1341 {
1342     if ( m_ignoreSelectEvents ) return;
1343
1344     // don't use listItems() here as we have to avoid recalculations - changing the
1345     // option list will make use update options not in the way the user expects them
1346     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->m_listItems;
1347     for ( unsigned i = 0; i < listItems.count(); i++ )
1348         // don't use setSelected() here because it will cause us to be called
1349         // again with updateSelection.
1350         if ( listItems[i]->id() == ID_OPTION )
1351             static_cast<HTMLOptionElementImpl*>( listItems[i] )
1352                 ->m_selected = static_cast<KListBox*>( m_widget )->isSelected( i );
1353
1354     element()->onChange();
1355 }
1356
1357
1358 void RenderSelect::setOptionsChanged(bool _optionsChanged)
1359 {
1360     m_optionsChanged = _optionsChanged;
1361 }
1362
1363 KListBox* RenderSelect::createListBox()
1364 {
1365     KListBox *lb = new KListBox(view()->viewport());
1366     lb->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
1367     // ### looks broken
1368     //lb->setAutoMask(true);
1369     connect( lb, SIGNAL( selectionChanged() ), this, SLOT( slotSelectionChanged() ) );
1370     connect( lb, SIGNAL( clicked( QListBoxItem * ) ), this, SLOT( slotClicked() ) );
1371     m_ignoreSelectEvents = false;
1372     lb->setMouseTracking(true);
1373
1374     return lb;
1375 }
1376
1377 ComboBoxWidget *RenderSelect::createComboBox()
1378 {
1379     ComboBoxWidget *cb = new ComboBoxWidget(view()->viewport());
1380     connect(cb, SIGNAL(activated(int)), this, SLOT(slotSelected(int)));
1381     return cb;
1382 }
1383
1384 void RenderSelect::updateSelection()
1385 {
1386     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1387     int i;
1388     if (m_useListBox) {
1389         // if multi-select, we select only the new selected index
1390         KListBox *listBox = static_cast<KListBox*>(m_widget);
1391         for (i = 0; i < int(listItems.size()); i++)
1392             listBox->setSelected(i,listItems[i]->id() == ID_OPTION &&
1393                                 static_cast<HTMLOptionElementImpl*>(listItems[i])->selected());
1394     }
1395     else {
1396         bool found = false;
1397         unsigned firstOption = listItems.size();
1398         i = listItems.size();
1399         while (i--)
1400             if (listItems[i]->id() == ID_OPTION) {
1401                 if (found)
1402                     static_cast<HTMLOptionElementImpl*>(listItems[i])->m_selected = false;
1403                 else if (static_cast<HTMLOptionElementImpl*>(listItems[i])->selected()) {
1404                     static_cast<KComboBox*>( m_widget )->setCurrentItem(i);
1405                     found = true;
1406                 }
1407                 firstOption = i;
1408             }
1409
1410         Q_ASSERT(firstOption == listItems.size() || found);
1411     }
1412
1413     m_selectionChanged = false;
1414 }
1415
1416
1417 // -------------------------------------------------------------------------
1418
1419 #if !APPLE_CHANGES
1420
1421 TextAreaWidget::TextAreaWidget(QWidget* parent)
1422     : KTextEdit(parent)
1423 {
1424 }
1425
1426 bool TextAreaWidget::event( QEvent *e )
1427 {
1428     if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
1429         QKeyEvent* ke = (QKeyEvent*) e;
1430         if ( ke->state() & ControlButton ) {
1431             switch ( ke->key() ) {
1432                 case Key_Left:
1433                 case Key_Right:
1434                 case Key_Up:
1435                 case Key_Down:
1436                 case Key_Home:
1437                 case Key_End:
1438                     ke->accept();
1439                 default:
1440                 break;
1441             }
1442         }
1443     }
1444     return KTextEdit::event( e );
1445 }
1446
1447 #endif
1448
1449 // -------------------------------------------------------------------------
1450
1451 RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
1452     : RenderFormElement(element), m_dirty(false)
1453 {
1454 #if APPLE_CHANGES
1455     QTextEdit *edit = new KTextEdit(view());
1456 #else
1457     QTextEdit *edit = new TextAreaWidget(view());
1458 #endif
1459
1460     if (element->wrap() != HTMLTextAreaElementImpl::ta_NoWrap)
1461         edit->setWordWrap(QTextEdit::WidgetWidth);
1462     else
1463         edit->setWordWrap(QTextEdit::NoWrap);
1464
1465 #if !APPLE_CHANGES
1466     KCursor::setAutoHideCursor(edit->viewport(), true);
1467     edit->setTextFormat(QTextEdit::PlainText);
1468     edit->setAutoMask(true);
1469     edit->setMouseTracking(true);
1470 #endif
1471
1472     setQWidget(edit);
1473
1474     connect(edit,SIGNAL(textChanged()),this,SLOT(slotTextChanged()));
1475     connect(edit,SIGNAL(clicked()),this,SLOT(slotClicked()));
1476 }
1477
1478 void RenderTextArea::detach()
1479 {
1480     element()->updateValue();
1481     RenderFormElement::detach();
1482 }
1483
1484 void RenderTextArea::handleFocusOut()
1485 {
1486     if ( m_dirty && element() ) {
1487         element()->updateValue();
1488         element()->onChange();
1489     }
1490     m_dirty = false;
1491 }
1492
1493 void RenderTextArea::calcMinMaxWidth()
1494 {
1495     KHTMLAssert( !minMaxKnown() );
1496
1497     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1498 #if APPLE_CHANGES
1499     QSize size(w->sizeWithColumnsAndRows(kMax(element()->cols(), 1L), kMax(element()->rows(), 1L)));
1500 #else
1501     const QFontMetrics &m = style()->fontMetrics();
1502     w->setTabStopWidth(8 * m.width(" "));
1503     QSize size( kMax(element()->cols(), 1)*m.width('x') + w->frameWidth() +
1504                 w->verticalScrollBar()->sizeHint().width(),
1505                 kMax(element()->rows(), 1)*m.height() + w->frameWidth()*2 +
1506                 (w->wordWrap() == QTextEdit::NoWrap ?
1507                  w->horizontalScrollBar()->sizeHint().height() : 0)
1508         );
1509 #endif
1510
1511     setIntrinsicWidth( size.width() );
1512     setIntrinsicHeight( size.height() );
1513
1514     RenderFormElement::calcMinMaxWidth();
1515 }
1516
1517 void RenderTextArea::setStyle(RenderStyle *s)
1518 {
1519     RenderFormElement::setStyle(s);
1520
1521     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1522     w->setAlignment(textAlignment());
1523 #if APPLE_CHANGES
1524     w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
1525 #endif
1526
1527     QScrollView::ScrollBarMode scrollMode = QScrollView::Auto;
1528     switch (style()->overflow()) {
1529         case OAUTO:
1530         case OMARQUEE: // makes no sense, map to auto
1531         case OOVERLAY: // not implemented for text, map to auto
1532         case OVISIBLE:
1533             break;
1534         case OHIDDEN:
1535             scrollMode = QScrollView::AlwaysOff;
1536             break;
1537         case OSCROLL:
1538             scrollMode = QScrollView::AlwaysOn;
1539             break;
1540     }
1541     QScrollView::ScrollBarMode horizontalScrollMode = scrollMode;
1542     if (element()->wrap() != HTMLTextAreaElementImpl::ta_NoWrap)
1543         horizontalScrollMode = QScrollView::AlwaysOff;
1544
1545 #if APPLE_CHANGES
1546     w->setScrollBarModes(horizontalScrollMode, scrollMode);
1547 #else
1548     w->setHScrollBarMode(horizontalScrollMode);
1549     w->setVScrollBarMode(scrollMode);
1550 #endif
1551 }
1552
1553 void RenderTextArea::updateFromElement()
1554 {
1555     HTMLTextAreaElementImpl *e = element();
1556     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1557
1558     w->setReadOnly(e->readOnly());
1559 #if APPLE_CHANGES
1560     w->setDisabled(e->disabled());
1561 #endif
1562
1563     e->updateValue();
1564     if (!e->valueMatchesRenderer()) {
1565         QString widgetText = text();
1566         QString text = e->value().string();
1567         text.replace(QChar('\\'), backslashAsCurrencySymbol());
1568         if (widgetText != text) {
1569             w->blockSignals(true);
1570             int line, col;
1571             w->getCursorPosition( &line, &col );
1572             w->setText(text);
1573             w->setCursorPosition( line, col );
1574             w->blockSignals(false);
1575         }
1576         e->setValueMatchesRenderer();
1577         m_dirty = false;
1578     }
1579
1580     RenderFormElement::updateFromElement();
1581 }
1582
1583 QString RenderTextArea::text()
1584 {
1585     QString txt;
1586     QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1587
1588     if (element()->wrap() == HTMLTextAreaElementImpl::ta_Physical) {
1589 #if APPLE_CHANGES
1590         txt = w->textWithHardLineBreaks();
1591 #else
1592         // yeah, QTextEdit has no accessor for getting the visually wrapped text
1593         for (int p=0; p < w->paragraphs(); ++p) {
1594             int pl = w->paragraphLength(p);
1595             int ll = 0;
1596             int lindex = w->lineOfChar(p, 0);
1597             QString paragraphText = w->text(p);
1598             for (int l = 0; l < pl; ++l) {
1599                 if (lindex != w->lineOfChar(p, l)) {
1600                     paragraphText.insert(l+ll++, QString::fromLatin1("\n"));
1601                     lindex = w->lineOfChar(p, l);
1602                 }
1603             }
1604             txt += paragraphText;
1605             if (p < w->paragraphs() - 1)
1606                 txt += QString::fromLatin1("\n");
1607         }
1608 #endif
1609     }
1610     else
1611         txt = w->text();
1612
1613     txt.replace(backslashAsCurrencySymbol(), QChar('\\'));
1614     return txt;
1615 }
1616
1617 void RenderTextArea::slotTextChanged()
1618 {
1619     element()->invalidateValue();
1620     m_dirty = true;
1621 }
1622
1623 void RenderTextArea::select()
1624 {
1625     static_cast<QTextEdit *>(m_widget)->selectAll();
1626 }
1627
1628 // ---------------------------------------------------------------------------
1629
1630 #if APPLE_CHANGES
1631 RenderSlider::RenderSlider(HTMLInputElementImpl* element)
1632 :RenderFormElement(element)
1633 {
1634     QSlider* slider = new QSlider();
1635     setQWidget(slider);
1636     connect(slider, SIGNAL(sliderValueChanged()), this, SLOT(slotSliderValueChanged()));
1637     connect(slider, SIGNAL(clicked()), this, SLOT(slotClicked()));
1638 }
1639
1640 void RenderSlider::calcMinMaxWidth()
1641 {
1642     KHTMLAssert(!minMaxKnown());
1643     
1644     // Let the widget tell us how big it wants to be.
1645     QSize s(widget()->sizeHint());
1646     bool widthSet = !style()->width().isVariable();
1647     bool heightSet = !style()->height().isVariable();
1648     if (heightSet && !widthSet) {
1649         // Flip the intrinsic dimensions.
1650         int barLength = s.width();
1651         s = QSize(s.height(), barLength);
1652     }
1653     setIntrinsicWidth(s.width());
1654     setIntrinsicHeight(s.height());
1655     
1656     RenderFormElement::calcMinMaxWidth();
1657 }
1658
1659 void RenderSlider::updateFromElement()
1660 {
1661     const DOMString& value = element()->value();
1662     const DOMString& min = element()->getAttribute(ATTR_MIN);
1663     const DOMString& max = element()->getAttribute(ATTR_MAX);
1664     const DOMString& precision = element()->getAttribute(ATTR_PRECISION);
1665     
1666     double minVal = min.isNull() ? 0.0 : min.string().toDouble();
1667     double maxVal = max.isNull() ? 100.0 : max.string().toDouble();
1668     minVal = kMin(minVal, maxVal); // Make sure the range is sane.
1669     
1670     double val = value.isNull() ? (maxVal + minVal)/2.0 : value.string().toDouble();
1671     val = kMax(minVal, kMin(val, maxVal)); // Make sure val is within min/max.
1672     
1673     // Force integer value if not float (strcasecmp returns confusingly backward boolean).
1674     if (strcasecmp(precision, "float"))
1675         val = (int)(val + 0.5);
1676
1677     element()->setValue(QString::number(val));
1678
1679     QSlider* slider = (QSlider*)widget();
1680      
1681     slider->setMinValue(minVal);
1682     slider->setMaxValue(maxVal);
1683     slider->setValue(val);
1684
1685     RenderFormElement::updateFromElement();
1686 }
1687
1688 void RenderSlider::slotSliderValueChanged()
1689 {
1690     QSlider* slider = (QSlider*)widget();
1691
1692     double val = slider->value();
1693     const DOMString& precision = element()->getAttribute(ATTR_PRECISION);
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     // Fire the "input" DOM event.
1702     element()->dispatchHTMLEvent(EventImpl::INPUT_EVENT, true, false);
1703 }
1704
1705 void RenderSlider::slotClicked()
1706 {
1707     // emit mouseClick event etc
1708     RenderFormElement::slotClicked();
1709 }
1710
1711 #endif
1712
1713 #include "render_form.moc"