Reviewed by Dave.
[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 #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             static_cast<KListBox*>(m_widget)->clear();
1100         else
1101             static_cast<KComboBox*>(m_widget)->clear();
1102
1103         for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) {
1104             if (listItems[listIndex]->id() == ID_OPTGROUP) {
1105                 QString label = listItems[listIndex]->getAttribute(ATTR_LABEL).string();
1106                 label.replace('\\', backslashAsCurrencySymbol());
1107
1108                 // In WinIE, an optgroup can't start or end with whitespace (other than the indent
1109                 // we give it).  We match this behavior.
1110                 label = label.stripWhiteSpace();
1111                 
1112 #if APPLE_CHANGES
1113                 if (m_useListBox)
1114                     static_cast<KListBox*>(m_widget)->appendGroupLabel(label);
1115                 else
1116                     static_cast<KComboBox*>(m_widget)->appendItem(label);
1117 #else
1118                 if(m_useListBox) {
1119                     QListBoxText *item = new QListBoxText(label);
1120                     static_cast<KListBox*>(m_widget)
1121                         ->insertItem(item, listIndex);
1122                     item->setSelectable(false);
1123                 }
1124                 else
1125                     static_cast<KComboBox*>(m_widget)->insertItem(label, listIndex);
1126 #endif
1127             }
1128             else if (listItems[listIndex]->id() == ID_OPTION) {
1129                 QString itemText = static_cast<HTMLOptionElementImpl*>(listItems[listIndex])->text().string();
1130                 itemText.replace('\\', backslashAsCurrencySymbol());
1131
1132                 // In WinIE, leading and trailing whitespace is ignored in options. We match this behavior.
1133                 itemText = itemText.stripWhiteSpace();
1134                 
1135                 if (listItems[listIndex]->parentNode()->id() == ID_OPTGROUP)
1136                     itemText.prepend("    ");
1137
1138 #if APPLE_CHANGES
1139                 if (m_useListBox)
1140                     static_cast<KListBox*>(m_widget)->appendItem(itemText);
1141                 else
1142                     static_cast<KComboBox*>(m_widget)->appendItem(itemText);
1143 #else
1144                 if(m_useListBox)
1145                     static_cast<KListBox*>(m_widget)->insertItem(itemText, listIndex);
1146                 else
1147                     static_cast<KComboBox*>(m_widget)->insertItem(itemText, listIndex);
1148 #endif
1149             }
1150             else
1151                 KHTMLAssert(false);
1152             m_selectionChanged = true;
1153         }
1154 #if APPLE_CHANGES
1155         if (m_useListBox)
1156             static_cast<KListBox*>(m_widget)->doneAppendingItems();
1157 #endif
1158         setNeedsLayoutAndMinMaxRecalc();
1159         m_optionsChanged = false;
1160     }
1161
1162     // update selection
1163     if (m_selectionChanged) {
1164         updateSelection();
1165     }
1166
1167     m_ignoreSelectEvents = false;
1168
1169     RenderFormElement::updateFromElement();
1170 }
1171
1172 #if APPLE_CHANGES
1173
1174 short RenderSelect::baselinePosition( bool f, bool isRootLineBox ) const
1175 {
1176     if (m_useListBox) {
1177         // FIXME: Should get the hardcoded constant of 7 by calling a QListBox function,
1178         // as we do for other widget classes.
1179         return RenderWidget::baselinePosition( f, isRootLineBox ) - 7;
1180     }
1181     return RenderFormElement::baselinePosition( f, isRootLineBox );
1182 }
1183
1184 #endif
1185
1186 void RenderSelect::calcMinMaxWidth()
1187 {
1188     KHTMLAssert( !minMaxKnown() );
1189
1190     if (m_optionsChanged)
1191         updateFromElement();
1192
1193     // ### ugly HACK FIXME!!!
1194     setMinMaxKnown();
1195     layoutIfNeeded();
1196     setNeedsLayoutAndMinMaxRecalc();
1197     // ### end FIXME
1198
1199     RenderFormElement::calcMinMaxWidth();
1200 }
1201
1202 void RenderSelect::layout( )
1203 {
1204     KHTMLAssert(needsLayout());
1205     KHTMLAssert(minMaxKnown());
1206
1207     // ### maintain selection properly between type/size changes, and work
1208     // out how to handle multiselect->singleselect (probably just select
1209     // first selected one)
1210
1211     // calculate size
1212     if(m_useListBox) {
1213         KListBox* w = static_cast<KListBox*>(m_widget);
1214
1215 #if !APPLE_CHANGES
1216         QListBoxItem* p = w->firstItem();
1217         int width = 0;
1218         int height = 0;
1219         while(p) {
1220             width = QMAX(width, p->width(p->listBox()));
1221             height = QMAX(height, p->height(p->listBox()));
1222             p = p->next();
1223         }
1224 #endif
1225
1226         int size = m_size;
1227         // check if multiple and size was not given or invalid
1228         // Internet Exploder sets size to QMIN(number of elements, 4)
1229         // Netscape seems to simply set it to "number of elements"
1230         // the average of that is IMHO QMIN(number of elements, 10)
1231         // so I did that ;-)
1232         if(size < 1)
1233             size = QMIN(static_cast<KListBox*>(m_widget)->count(), 10);
1234
1235 #if APPLE_CHANGES
1236         // Let the widget tell us how big it wants to be.
1237         QSize s(w->sizeForNumberOfLines(size));
1238         setIntrinsicWidth( s.width() );
1239         setIntrinsicHeight( s.height() );
1240 #else
1241         width += 2*w->frameWidth() + w->verticalScrollBar()->sizeHint().width();
1242         height = size*height + 2*w->frameWidth();
1243
1244         setIntrinsicWidth( width );
1245         setIntrinsicHeight( height );
1246 #endif
1247     }
1248     else {
1249         QSize s(m_widget->sizeHint());
1250         setIntrinsicWidth( s.width() );
1251         setIntrinsicHeight( s.height() );
1252     }
1253
1254     /// uuh, ignore the following line..
1255     setNeedsLayout(true);
1256     RenderFormElement::layout();
1257
1258     // and now disable the widget in case there is no <option> given
1259     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1260
1261     bool foundOption = false;
1262     for (uint i = 0; i < listItems.size() && !foundOption; i++)
1263         foundOption = (listItems[i]->id() == ID_OPTION);
1264
1265     m_widget->setEnabled(foundOption && ! element()->disabled());
1266 }
1267
1268 void RenderSelect::slotSelected(int index)
1269 {
1270     if ( m_ignoreSelectEvents ) return;
1271
1272     KHTMLAssert( !m_useListBox );
1273
1274     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1275     if(index >= 0 && index < int(listItems.size()))
1276     {
1277         bool found = ( listItems[index]->id() == ID_OPTION );
1278
1279         if ( !found ) {
1280             // this one is not selectable,  we need to find an option element
1281             while ( ( unsigned ) index < listItems.size() ) {
1282                 if ( listItems[index]->id() == ID_OPTION ) {
1283                     found = true;
1284                     break;
1285                 }
1286                 ++index;
1287             }
1288
1289             if ( !found ) {
1290                 while ( index >= 0 ) {
1291                     if ( listItems[index]->id() == ID_OPTION ) {
1292                         found = true;
1293                         break;
1294                     }
1295                     --index;
1296                 }
1297             }
1298         }
1299
1300         if ( found ) {
1301             if ( index != static_cast<ComboBoxWidget*>( m_widget )->currentItem() )
1302                 static_cast<ComboBoxWidget*>( m_widget )->setCurrentItem( index );
1303
1304             for ( unsigned int i = 0; i < listItems.size(); ++i )
1305                 if ( listItems[i]->id() == ID_OPTION && i != (unsigned int) index )
1306                     static_cast<HTMLOptionElementImpl*>( listItems[i] )->m_selected = false;
1307
1308             static_cast<HTMLOptionElementImpl*>(listItems[index])->m_selected = true;
1309         }
1310     }
1311
1312     element()->onChange();
1313 }
1314
1315
1316 void RenderSelect::slotSelectionChanged()
1317 {
1318     if ( m_ignoreSelectEvents ) return;
1319
1320     // don't use listItems() here as we have to avoid recalculations - changing the
1321     // option list will make use update options not in the way the user expects them
1322     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->m_listItems;
1323     for ( unsigned i = 0; i < listItems.count(); i++ )
1324         // don't use setSelected() here because it will cause us to be called
1325         // again with updateSelection.
1326         if ( listItems[i]->id() == ID_OPTION )
1327             static_cast<HTMLOptionElementImpl*>( listItems[i] )
1328                 ->m_selected = static_cast<KListBox*>( m_widget )->isSelected( i );
1329
1330     element()->onChange();
1331 }
1332
1333
1334 void RenderSelect::setOptionsChanged(bool _optionsChanged)
1335 {
1336     m_optionsChanged = _optionsChanged;
1337 }
1338
1339 KListBox* RenderSelect::createListBox()
1340 {
1341     KListBox *lb = new KListBox(view()->viewport());
1342     lb->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
1343     // ### looks broken
1344     //lb->setAutoMask(true);
1345     connect( lb, SIGNAL( selectionChanged() ), this, SLOT( slotSelectionChanged() ) );
1346     connect( lb, SIGNAL( clicked( QListBoxItem * ) ), this, SLOT( slotClicked() ) );
1347     m_ignoreSelectEvents = false;
1348     lb->setMouseTracking(true);
1349
1350     return lb;
1351 }
1352
1353 ComboBoxWidget *RenderSelect::createComboBox()
1354 {
1355     ComboBoxWidget *cb = new ComboBoxWidget(view()->viewport());
1356     connect(cb, SIGNAL(activated(int)), this, SLOT(slotSelected(int)));
1357     return cb;
1358 }
1359
1360 void RenderSelect::updateSelection()
1361 {
1362     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1363     int i;
1364     if (m_useListBox) {
1365         // if multi-select, we select only the new selected index
1366         KListBox *listBox = static_cast<KListBox*>(m_widget);
1367         for (i = 0; i < int(listItems.size()); i++)
1368             listBox->setSelected(i,listItems[i]->id() == ID_OPTION &&
1369                                 static_cast<HTMLOptionElementImpl*>(listItems[i])->selected());
1370     }
1371     else {
1372         bool found = false;
1373         unsigned firstOption = listItems.size();
1374         i = listItems.size();
1375         while (i--)
1376             if (listItems[i]->id() == ID_OPTION) {
1377                 if (found)
1378                     static_cast<HTMLOptionElementImpl*>(listItems[i])->m_selected = false;
1379                 else if (static_cast<HTMLOptionElementImpl*>(listItems[i])->selected()) {
1380                     static_cast<KComboBox*>( m_widget )->setCurrentItem(i);
1381                     found = true;
1382                 }
1383                 firstOption = i;
1384             }
1385
1386         Q_ASSERT(firstOption == listItems.size() || found);
1387     }
1388
1389     m_selectionChanged = false;
1390 }
1391
1392
1393 // -------------------------------------------------------------------------
1394
1395 TextAreaWidget::TextAreaWidget(int wrap, QWidget* parent)
1396     : KTextEdit(parent)
1397 {
1398     if(wrap != DOM::HTMLTextAreaElementImpl::ta_NoWrap) {
1399         setWordWrap(QTextEdit::WidgetWidth);
1400 #if !APPLE_CHANGES
1401         setHScrollBarMode( AlwaysOff );
1402         setVScrollBarMode( AlwaysOn );
1403 #endif
1404     }
1405     else {
1406         setWordWrap(QTextEdit::NoWrap);
1407 #if !APPLE_CHANGES
1408         setHScrollBarMode( Auto );
1409         setVScrollBarMode( Auto );
1410 #endif
1411     }
1412     KCursor::setAutoHideCursor(viewport(), true);
1413     setTextFormat(QTextEdit::PlainText);
1414     setAutoMask(true);
1415     setMouseTracking(true);
1416 }
1417
1418 bool TextAreaWidget::event( QEvent *e )
1419 {
1420 #if !APPLE_CHANGES
1421     if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
1422         QKeyEvent* ke = (QKeyEvent*) e;
1423         if ( ke->state() & ControlButton ) {
1424             switch ( ke->key() ) {
1425                 case Key_Left:
1426                 case Key_Right:
1427                 case Key_Up:
1428                 case Key_Down:
1429                 case Key_Home:
1430                 case Key_End:
1431                     ke->accept();
1432                 default:
1433                 break;
1434             }
1435         }
1436     }
1437 #endif
1438     return KTextEdit::event( e );
1439 }
1440
1441 // -------------------------------------------------------------------------
1442
1443 RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
1444     : RenderFormElement(element)
1445 {
1446     TextAreaWidget *edit = new TextAreaWidget(element->wrap(), view());
1447     setQWidget(edit);
1448
1449     connect(edit,SIGNAL(textChanged()),this,SLOT(slotTextChanged()));
1450     connect(edit,SIGNAL(clicked()),this,SLOT(slotClicked()));
1451 }
1452
1453 void RenderTextArea::detach()
1454 {
1455     if ( element()->m_dirtyvalue ) {
1456         element()->m_value = text();
1457         element()->m_dirtyvalue = false;
1458     }
1459     RenderFormElement::detach();
1460 }
1461
1462 void RenderTextArea::handleFocusOut()
1463 {
1464     TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
1465     if ( w && element() && element()->m_dirtyvalue ) {
1466         element()->m_value = text();
1467         element()->m_dirtyvalue = false;
1468         element()->onChange();
1469     }
1470 }
1471
1472 void RenderTextArea::calcMinMaxWidth()
1473 {
1474     KHTMLAssert( !minMaxKnown() );
1475
1476     TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
1477     const QFontMetrics &m = style()->fontMetrics();
1478     w->setTabStopWidth(8 * m.width(" "));
1479 #if APPLE_CHANGES
1480     QSize size(w->sizeWithColumnsAndRows(QMAX(element()->cols(), 1), QMAX(element()->rows(), 1)));
1481 #else
1482     QSize size( QMAX(element()->cols(), 1)*m.width('x') + w->frameWidth() +
1483                 w->verticalScrollBar()->sizeHint().width(),
1484                 QMAX(element()->rows(), 1)*m.height() + w->frameWidth()*2 +
1485                 (w->wordWrap() == QTextEdit::NoWrap ?
1486                  w->horizontalScrollBar()->sizeHint().height() : 0)
1487         );
1488 #endif
1489
1490     setIntrinsicWidth( size.width() );
1491     setIntrinsicHeight( size.height() );
1492
1493     RenderFormElement::calcMinMaxWidth();
1494 }
1495
1496 void RenderTextArea::setStyle(RenderStyle *s)
1497 {
1498     RenderFormElement::setStyle(s);
1499
1500     TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
1501     w->setAlignment(textAlignment());
1502 #if APPLE_CHANGES
1503     w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
1504 #endif
1505 }
1506
1507 void RenderTextArea::updateFromElement()
1508 {
1509     TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
1510     w->setReadOnly(element()->readOnly());
1511 #if APPLE_CHANGES
1512     w->setDisabled(element()->disabled());
1513 #endif
1514     
1515     // Call w->text() before calling element()->value(), because in the case of inline
1516     // input such as Hiragana, w->text() has a side effect of sending the notification
1517     // that we use in slotTextChanged to update element()->m_value
1518     QString widgetText = w->text();
1519     QString text = element()->value().string();
1520     text.replace('\\', backslashAsCurrencySymbol());
1521     if (widgetText != text) {
1522         w->blockSignals(true);
1523         int line, col;
1524         w->getCursorPosition( &line, &col );
1525         w->setText(text);
1526         w->setCursorPosition( line, col );
1527         w->blockSignals(false);
1528     }
1529     element()->m_dirtyvalue = false;
1530
1531     RenderFormElement::updateFromElement();
1532 }
1533
1534 void RenderTextArea::close( )
1535 {
1536     element()->setValue( element()->defaultValue() );
1537
1538     RenderFormElement::close();
1539 }
1540
1541 QString RenderTextArea::text()
1542 {
1543     QString txt;
1544     TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
1545
1546     if(element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical) {
1547 #if APPLE_CHANGES
1548         txt = w->textWithHardLineBreaks();
1549 #else
1550         // yeah, QTextEdit has no accessor for getting the visually wrapped text
1551         for (int p=0; p < w->paragraphs(); ++p) {
1552             int pl = w->paragraphLength(p);
1553             int ll = 0;
1554             int lindex = w->lineOfChar(p, 0);
1555             QString paragraphText = w->text(p);
1556             for (int l = 0; l < pl; ++l) {
1557                 if (lindex != w->lineOfChar(p, l)) {
1558                     paragraphText.insert(l+ll++, QString::fromLatin1("\n"));
1559                     lindex = w->lineOfChar(p, l);
1560                 }
1561             }
1562             txt += paragraphText;
1563             if (p < w->paragraphs() - 1)
1564                 txt += QString::fromLatin1("\n");
1565         }
1566 #endif
1567     }
1568     else
1569         txt = w->text();
1570
1571     txt.replace(backslashAsCurrencySymbol(), '\\');
1572     return txt;
1573 }
1574
1575 void RenderTextArea::slotTextChanged()
1576 {
1577     element()->m_dirtyvalue = true;
1578 }
1579
1580 void RenderTextArea::select()
1581 {
1582     static_cast<TextAreaWidget *>(m_widget)->selectAll();
1583 }
1584
1585 // ---------------------------------------------------------------------------
1586
1587 #include "render_form.moc"