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