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