2 * This file is part of the DOM implementation for KDE.
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.
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.
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.
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.
28 #include <kfiledialog.h>
29 #include <kcompletionbox.h>
34 #include "misc/helper.h"
35 #include "xml/dom2_eventsimpl.h"
36 #include "html/html_formimpl.h"
37 #include "misc/htmlhashes.h"
39 #include "rendering/render_form.h"
42 #include "khtmlview.h"
43 #include "khtml_ext.h"
44 #include "xml/dom_docimpl.h"
49 #include "KWQFileButton.h"
52 using namespace khtml;
54 RenderFormElement::RenderFormElement(HTMLGenericFormElementImpl *element)
55 : RenderWidget(element)
57 // init RenderObject attributes
58 setInline(true); // our object is Inline
63 m_isDoubleClick = false;
66 RenderFormElement::~RenderFormElement()
70 short RenderFormElement::baselinePosition( bool f, bool isRootLineBox ) const
73 return marginTop() + widget()->baselinePosition(m_height);
75 return RenderWidget::baselinePosition( f, isRootLineBox ) - 2 - style()->fontMetrics().descent();
79 void RenderFormElement::setStyle(RenderStyle* s)
82 if (canHaveIntrinsicMargins())
83 addIntrinsicMarginsIfAllowed(s);
86 RenderWidget::setStyle(s);
89 // Do not paint a background or border for Aqua form elements
90 setShouldPaintBackgroundOrBorder(false);
93 m_widget->setFont(style()->font());
96 void RenderFormElement::updateFromElement()
98 m_widget->setEnabled(!element()->disabled());
101 QColor color = style()->color();
102 QColor backgroundColor = style()->backgroundColor();
104 if ( color.isValid() || backgroundColor.isValid() ) {
105 QPalette pal(m_widget->palette());
107 int contrast_ = KGlobalSettings::contrast();
108 int highlightVal = 100 + (2*contrast_+4)*16/10;
109 int lowlightVal = 100 + (2*contrast_+4)*10;
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 );
122 if ( color.isValid() ) {
124 QPalette::ColorGroup cg;
125 QColorGroup::ColorRole cr;
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 },
137 const ColorSet *set = toSet;
138 while( set->cg != QPalette::NColorGroups ) {
139 pal.setColor( set->cg, set->cr, color );
143 QColor disfg = color;
145 disfg.hsv( &h, &s, &v );
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);
153 // black fg - use darkgrey disabled fg
154 disfg = Qt::darkGray;
155 pal.setColor(QPalette::Disabled,QColorGroup::Foreground,disfg);
158 m_widget->setPalette(pal);
161 m_widget->unsetPalette();
165 void RenderFormElement::layout()
167 KHTMLAssert( needsLayout() );
168 KHTMLAssert( minMaxKnown() );
178 resizeWidget(m_widget,
179 m_width-borderLeft()-borderRight()-paddingLeft()-paddingRight(),
180 m_height-borderLeft()-borderRight()-paddingLeft()-paddingRight());
183 setNeedsLayout(false);
186 void RenderFormElement::slotClicked()
188 RenderArena *arena = ref();
189 QMouseEvent e2( QEvent::MouseButtonRelease, m_mousePos, m_button, m_state);
191 element()->dispatchMouseEvent(&e2, EventImpl::CLICK_EVENT, m_clickCount);
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);
201 m_isDoubleClick = false;
205 Qt::AlignmentFlags RenderFormElement::textAlignment() const
207 switch (style()->textAlign()) {
218 // Just fall into the auto code for justify.
220 return style()->direction() == RTL ? AlignRight : AlignLeft;
222 assert(false); // Should never be reached.
228 void RenderFormElement::addIntrinsicMarginsIfAllowed(RenderStyle* _style)
230 // Cut out the intrinsic margins completely if we end up using mini controls.
231 if (_style->font().pixelSize() < 11)
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));
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));
252 // -------------------------------------------------------------------------
254 RenderButton::RenderButton(HTMLGenericFormElementImpl *element)
255 : RenderFormElement(element)
259 short RenderButton::baselinePosition( bool f, bool isRootLineBox ) const
262 return RenderFormElement::baselinePosition( f, isRootLineBox );
264 return RenderWidget::baselinePosition( f, isRootLineBox ) - 2;
268 // -------------------------------------------------------------------------------
270 RenderCheckBox::RenderCheckBox(HTMLInputElementImpl *element)
271 : RenderButton(element)
273 QCheckBox* b = new QCheckBox(view()->viewport());
274 b->setAutoMask(true);
275 b->setMouseTracking(true);
277 connect(b,SIGNAL(stateChanged(int)),this,SLOT(slotStateChanged(int)));
278 connect(b, SIGNAL(clicked()), this, SLOT(slotClicked()));
281 void RenderCheckBox::calcMinMaxWidth()
283 KHTMLAssert( !minMaxKnown() );
286 // Let the widget tell us how big it wants to be.
287 QSize s(widget()->sizeHint());
289 QCheckBox *cb = static_cast<QCheckBox *>( m_widget );
290 QSize s( cb->style().pixelMetric( QStyle::PM_IndicatorWidth ),
291 cb->style().pixelMetric( QStyle::PM_IndicatorHeight ) );
293 setIntrinsicWidth( s.width() );
294 setIntrinsicHeight( s.height() );
296 RenderButton::calcMinMaxWidth();
299 void RenderCheckBox::updateFromElement()
301 widget()->setChecked(element()->checked());
303 RenderButton::updateFromElement();
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)
310 element()->setChecked(state == 2);
313 // -------------------------------------------------------------------------------
315 RenderRadioButton::RenderRadioButton(HTMLInputElementImpl *element)
316 : RenderButton(element)
318 QRadioButton* b = new QRadioButton(view()->viewport());
319 b->setAutoMask(true);
320 b->setMouseTracking(true);
322 connect(b, SIGNAL(clicked()), this, SLOT(slotClicked()));
325 void RenderRadioButton::updateFromElement()
327 widget()->setChecked(element()->checked());
329 RenderButton::updateFromElement();
332 void RenderRadioButton::slotClicked()
334 element()->setChecked(true);
336 // emit mouseClick event etc
337 RenderButton::slotClicked();
340 void RenderRadioButton::calcMinMaxWidth()
342 KHTMLAssert( !minMaxKnown() );
345 // Let the widget tell us how big it wants to be.
346 QSize s(widget()->sizeHint());
348 QRadioButton *rb = static_cast<QRadioButton *>( m_widget );
349 QSize s( rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorWidth ),
350 rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorHeight ) );
352 setIntrinsicWidth( s.width() );
353 setIntrinsicHeight( s.height() );
355 RenderButton::calcMinMaxWidth();
358 // -------------------------------------------------------------------------------
361 RenderSubmitButton::RenderSubmitButton(HTMLInputElementImpl *element)
362 : RenderButton(element)
364 QPushButton* p = new QPushButton(view()->viewport());
366 p->setAutoMask(true);
367 p->setMouseTracking(true);
368 connect(p, SIGNAL(clicked()), this, SLOT(slotClicked()));
371 QString RenderSubmitButton::rawText()
373 QString value = element()->value().isEmpty() ? defaultLabel() : element()->value().string();
374 value = value.stripWhiteSpace();
375 value.replace('\\', backslashAsCurrencySymbol());
380 for(unsigned int i = 0; i < value.length(); i++) {
389 void RenderSubmitButton::calcMinMaxWidth()
391 KHTMLAssert( !minMaxKnown() );
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());
399 QString raw = rawText();
400 QPushButton* pb = static_cast<QPushButton*>(m_widget);
402 pb->setFont(style()->font());
404 bool empty = raw.isEmpty();
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()));
413 setIntrinsicWidth( s.width() - margin / 2 );
414 setIntrinsicHeight( s.height() - margin / 2);
417 RenderButton::calcMinMaxWidth();
422 void RenderSubmitButton::setStyle(RenderStyle *s)
424 RenderButton::setStyle(s);
426 QPushButton *w = static_cast<QPushButton*>(m_widget);
427 w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
432 void RenderSubmitButton::updateFromElement()
434 QPushButton *w = static_cast<QPushButton*>(m_widget);
436 QString oldText = w->text();
437 QString newText = rawText();
439 if ( oldText != newText )
440 setNeedsLayoutAndMinMaxRecalc();
441 RenderFormElement::updateFromElement();
444 QString RenderSubmitButton::defaultLabel()
447 return submitButtonDefaultLabel();
449 return i18n("Submit");
453 short RenderSubmitButton::baselinePosition( bool f, bool isRootLineBox ) const
455 return RenderFormElement::baselinePosition( f, isRootLineBox );
458 // -------------------------------------------------------------------------------
460 RenderImageButton::RenderImageButton(HTMLInputElementImpl *element)
461 : RenderImage(element)
463 // ### support DOMActivate event when clicked
466 // -------------------------------------------------------------------------------
468 RenderResetButton::RenderResetButton(HTMLInputElementImpl *element)
469 : RenderSubmitButton(element)
473 QString RenderResetButton::defaultLabel()
476 return resetButtonDefaultLabel();
478 return i18n("Reset");
483 // -------------------------------------------------------------------------------
485 RenderPushButton::RenderPushButton(HTMLInputElementImpl *element)
486 : RenderSubmitButton(element)
490 QString RenderPushButton::defaultLabel()
492 return QString::null;
495 // -------------------------------------------------------------------------------
499 LineEditWidget::LineEditWidget(QWidget *parent)
502 setMouseTracking(true);
505 bool LineEditWidget::event( QEvent *e )
507 if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
508 QKeyEvent* ke = (QKeyEvent*) e;
509 if ( ke->state() & ControlButton ) {
510 switch ( ke->key() ) {
523 return KLineEdit::event( e );
528 // -----------------------------------------------------------------------------
530 RenderLineEdit::RenderLineEdit(HTMLInputElementImpl *element)
531 : RenderFormElement(element), m_updating(false)
534 KLineEdit *edit = new KLineEdit(view()->viewport());
536 LineEditWidget *edit = new LineEditWidget(view()->viewport());
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()));
542 if(element->inputType() == HTMLInputElementImpl::PASSWORD)
543 edit->setEchoMode( QLineEdit::Password );
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);
558 void RenderLineEdit::slotReturnPressed()
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 )
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
572 HTMLFormElementImpl* fe = element()->form();
577 void RenderLineEdit::handleFocusOut()
579 if ( widget() && widget()->edited() ) {
580 element()->onChange();
581 widget()->setEdited( false );
585 void RenderLineEdit::calcMinMaxWidth()
587 KHTMLAssert( !minMaxKnown() );
590 // Let the widget tell us how big it wants to be.
592 int size = element()->size();
593 QSize s(widget()->sizeForCharacterWidth(size > 0 ? size : 20));
596 const QFontMetrics &fm = style()->fontMetrics();
599 int size = element()->size();
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());
608 setIntrinsicWidth( s.width() );
609 setIntrinsicHeight( s.height() );
611 RenderFormElement::calcMinMaxWidth();
614 void RenderLineEdit::setStyle(RenderStyle *s)
616 RenderFormElement::setStyle(s);
618 KLineEdit *w = widget();
619 w->setAlignment(textAlignment());
621 w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
625 void RenderLineEdit::updateFromElement()
627 KLineEdit *w = widget();
629 int ml = element()->maxLength();
630 if ( ml <= 0 || ml > 1024 )
632 if ( w->maxLength() != ml )
633 w->setMaxLength( ml );
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());
642 if (newText != widgetText) {
643 w->blockSignals(true);
644 int pos = w->cursorPosition();
650 w->setEdited( false );
652 w->setCursorPosition(pos);
653 w->blockSignals(false);
655 w->setReadOnly(element()->readOnly());
657 RenderFormElement::updateFromElement();
660 void RenderLineEdit::slotTextChanged(const QString &string)
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.
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;
674 void RenderLineEdit::select()
676 static_cast<KLineEdit*>(m_widget)->selectAll();
679 // ---------------------------------------------------------------------------
681 RenderFieldset::RenderFieldset(HTMLGenericFormElementImpl *element)
682 : RenderBlock(element)
686 RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren)
688 RenderObject* legend = findLegend();
690 if (relayoutChildren)
691 legend->setNeedsLayout(true);
692 legend->layoutIfNeeded();
694 int xPos = borderLeft() + paddingLeft() + legend->marginLeft();
695 if (style()->direction() == RTL)
696 xPos = m_width - paddingRight() - borderRight() - legend->width() - legend->marginRight();
698 int h = legend->height();
699 legend->setPos(xPos, QMAX((b-h)/2, 0));
700 m_height = QMAX(b,h) + paddingTop();
705 RenderObject* RenderFieldset::findLegend()
707 for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) {
708 if (!legend->isFloatingOrPositioned() && legend->element() &&
709 legend->element()->id() == ID_LEGEND)
715 void RenderFieldset::paintBoxDecorations(QPainter *p,int _x, int _y,
716 int _w, int _h, int _tx, int _ty)
718 //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
721 int h = height() + borderTopExtra() + borderBottomExtra();
722 RenderObject* legend = findLegend();
724 return RenderBlock::paintBoxDecorations(p, _x, _y, _w, _h, _tx, _ty);
726 int yOff = (legend->yPos() > 0) ? 0 : (legend->height()-borderTop())/2;
728 _ty += yOff - borderTopExtra();
730 int my = QMAX(_ty,_y);
731 int end = QMIN( _y + _h, _ty + h );
734 paintBackground(p, style()->backgroundColor(), style()->backgroundImage(), my, mh, _tx, _ty, w, h);
736 if (style()->hasBorder())
737 paintBorderMinusLegend(p, _tx, _ty, w, h, style(), legend->xPos(), legend->width());
740 void RenderFieldset::paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w, int h,
741 const RenderStyle* style, int lx, int lw)
744 const QColor& tc = style->borderTopColor();
745 const QColor& bc = style->borderBottomColor();
747 EBorderStyle ts = style->borderTopStyle();
748 EBorderStyle bs = style->borderBottomStyle();
749 EBorderStyle ls = style->borderLeftStyle();
750 EBorderStyle rs = style->borderRightStyle();
752 bool render_t = ts > BHIDDEN;
753 bool render_l = ls > BHIDDEN;
754 bool render_r = rs > BHIDDEN;
755 bool render_b = bs > BHIDDEN;
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));
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));
771 const QColor& lc = style->borderLeftColor();
776 (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
781 (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
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());
790 const QColor& rc = style->borderRightColor();
794 (rs >= DOTTED || rs == INSET) &&
795 (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
799 (rs >= DOTTED || rs == INSET) &&
800 (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
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());
808 void RenderFieldset::setStyle(RenderStyle* _style)
810 RenderBlock::setStyle(_style);
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.
820 // -------------------------------------------------------------------------
822 RenderFileButton::RenderFileButton(HTMLInputElementImpl *element)
823 : RenderFormElement(element)
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()));
831 QHBox *w = new QHBox(view()->viewport());
833 m_edit = new LineEditWidget(w);
835 connect(m_edit, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
836 connect(m_edit, SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
838 m_button = new QPushButton(i18n("Browse..."), w);
839 m_button->setFocusPolicy(QWidget::ClickFocus);
840 connect(m_button,SIGNAL(clicked()), this, SLOT(slotClicked()));
842 w->setStretchFactor(m_edit, 2);
843 w->setFocusProxy(m_edit);
849 void RenderFileButton::calcMinMaxWidth()
851 KHTMLAssert( !minMaxKnown() );
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));
858 const QFontMetrics &fm = style()->fontMetrics();
860 int size = element()->size();
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());
870 setIntrinsicWidth( s.width() );
871 setIntrinsicHeight( s.height() );
873 RenderFormElement::calcMinMaxWidth();
878 void RenderFileButton::handleFocusOut()
880 if ( m_edit && m_edit->edited() ) {
881 element()->onChange();
882 m_edit->setEdited( false );
888 void RenderFileButton::slotClicked()
891 RenderFormElement::slotClicked();
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);
901 void RenderFileButton::updateFromElement()
904 static_cast<KWQFileButton *>(widget())->setFilename(element()->value().string());
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 )
912 m_edit->setMaxLength( ml );
913 m_edit->setEdited( false );
916 RenderFormElement::updateFromElement();
919 void RenderFileButton::slotReturnPressed()
921 if (element()->form())
922 element()->form()->prepareSubmit();
925 void RenderFileButton::slotTextChanged(const QString &string)
927 element()->m_value = DOMString(string);
928 element()->onChange();
931 void RenderFileButton::select()
940 void RenderFileButton::click()
942 static_cast<KWQFileButton *>(widget())->click();
947 // -------------------------------------------------------------------------
949 RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element)
950 : RenderFormElement(element)
955 // -------------------------------------------------------------------------
957 RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element)
958 : RenderBlock(element)
962 // -------------------------------------------------------------------------------
964 ComboBoxWidget::ComboBoxWidget(QWidget *parent)
965 : KComboBox(false, parent)
968 if (listBox()) listBox()->installEventFilter(this);
969 setMouseTracking(true);
972 bool ComboBoxWidget::event(QEvent *e)
975 if (e->type()==QEvent::KeyPress)
977 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
986 return KComboBox::event(e);
990 return KComboBox::event(e);
993 bool ComboBoxWidget::eventFilter(QObject *dest, QEvent *e)
996 if (dest==listBox() && e->type()==QEvent::KeyPress)
998 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
999 bool forward = false;
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);
1013 return KComboBox::eventFilter(dest, e);
1017 return KComboBox::eventFilter(dest, e);
1020 // -------------------------------------------------------------------------
1022 RenderSelect::RenderSelect(HTMLSelectElementImpl *element)
1023 : RenderFormElement(element)
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;
1033 setQWidget(createListBox());
1035 setQWidget(createComboBox());
1040 void RenderSelect::setWidgetWritingDirection()
1042 QPainter::TextDirection d = style()->direction() == RTL ? QPainter::RTL : QPainter::LTR;
1044 static_cast<KListBox *>(m_widget)->setWritingDirection(d);
1046 static_cast<ComboBoxWidget *>(m_widget)->setWritingDirection(d);
1049 void RenderSelect::setStyle(RenderStyle *s)
1051 RenderFormElement::setStyle(s);
1052 setWidgetWritingDirection();
1057 void RenderSelect::updateFromElement()
1059 m_ignoreSelectEvents = true;
1061 // change widget type
1062 bool oldMultiple = m_multiple;
1063 unsigned oldSize = m_size;
1064 bool oldListbox = m_useListBox;
1066 m_multiple = element()->multiple();
1067 m_size = element()->size();
1068 m_useListBox = (m_multiple || m_size > 1);
1070 if (oldMultiple != m_multiple || oldSize != m_size) {
1071 if (m_useListBox != oldListbox) {
1072 // type of select has changed
1076 setQWidget(createListBox());
1078 setQWidget(createComboBox());
1080 setWidgetWritingDirection();
1084 if (m_useListBox && oldMultiple != m_multiple) {
1085 static_cast<KListBox*>(m_widget)->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
1087 m_selectionChanged = true;
1088 m_optionsChanged = true;
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();
1099 static_cast<KListBox*>(m_widget)->clear();
1101 static_cast<KComboBox*>(m_widget)->clear();
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());
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();
1114 static_cast<KListBox*>(m_widget)->appendGroupLabel(label);
1116 static_cast<KComboBox*>(m_widget)->appendItem(label);
1119 QListBoxText *item = new QListBoxText(label);
1120 static_cast<KListBox*>(m_widget)
1121 ->insertItem(item, listIndex);
1122 item->setSelectable(false);
1125 static_cast<KComboBox*>(m_widget)->insertItem(label, listIndex);
1128 else if (listItems[listIndex]->id() == ID_OPTION) {
1129 QString itemText = static_cast<HTMLOptionElementImpl*>(listItems[listIndex])->text().string();
1130 itemText.replace('\\', backslashAsCurrencySymbol());
1132 // In WinIE, leading and trailing whitespace is ignored in options. We match this behavior.
1133 itemText = itemText.stripWhiteSpace();
1135 if (listItems[listIndex]->parentNode()->id() == ID_OPTGROUP)
1136 itemText.prepend(" ");
1140 static_cast<KListBox*>(m_widget)->appendItem(itemText);
1142 static_cast<KComboBox*>(m_widget)->appendItem(itemText);
1145 static_cast<KListBox*>(m_widget)->insertItem(itemText, listIndex);
1147 static_cast<KComboBox*>(m_widget)->insertItem(itemText, listIndex);
1152 m_selectionChanged = true;
1156 static_cast<KListBox*>(m_widget)->doneAppendingItems();
1158 setNeedsLayoutAndMinMaxRecalc();
1159 m_optionsChanged = false;
1163 if (m_selectionChanged) {
1167 m_ignoreSelectEvents = false;
1169 RenderFormElement::updateFromElement();
1174 short RenderSelect::baselinePosition( bool f, bool isRootLineBox ) const
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;
1181 return RenderFormElement::baselinePosition( f, isRootLineBox );
1186 void RenderSelect::calcMinMaxWidth()
1188 KHTMLAssert( !minMaxKnown() );
1190 if (m_optionsChanged)
1191 updateFromElement();
1193 // ### ugly HACK FIXME!!!
1196 setNeedsLayoutAndMinMaxRecalc();
1199 RenderFormElement::calcMinMaxWidth();
1202 void RenderSelect::layout( )
1204 KHTMLAssert(needsLayout());
1205 KHTMLAssert(minMaxKnown());
1207 // ### maintain selection properly between type/size changes, and work
1208 // out how to handle multiselect->singleselect (probably just select
1209 // first selected one)
1213 KListBox* w = static_cast<KListBox*>(m_widget);
1216 QListBoxItem* p = w->firstItem();
1220 width = QMAX(width, p->width(p->listBox()));
1221 height = QMAX(height, p->height(p->listBox()));
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 ;-)
1233 size = QMIN(static_cast<KListBox*>(m_widget)->count(), 10);
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() );
1241 width += 2*w->frameWidth() + w->verticalScrollBar()->sizeHint().width();
1242 height = size*height + 2*w->frameWidth();
1244 setIntrinsicWidth( width );
1245 setIntrinsicHeight( height );
1249 QSize s(m_widget->sizeHint());
1250 setIntrinsicWidth( s.width() );
1251 setIntrinsicHeight( s.height() );
1254 /// uuh, ignore the following line..
1255 setNeedsLayout(true);
1256 RenderFormElement::layout();
1258 // and now disable the widget in case there is no <option> given
1259 QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1261 bool foundOption = false;
1262 for (uint i = 0; i < listItems.size() && !foundOption; i++)
1263 foundOption = (listItems[i]->id() == ID_OPTION);
1265 m_widget->setEnabled(foundOption && ! element()->disabled());
1268 void RenderSelect::slotSelected(int index)
1270 if ( m_ignoreSelectEvents ) return;
1272 KHTMLAssert( !m_useListBox );
1274 QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1275 if(index >= 0 && index < int(listItems.size()))
1277 bool found = ( listItems[index]->id() == ID_OPTION );
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 ) {
1290 while ( index >= 0 ) {
1291 if ( listItems[index]->id() == ID_OPTION ) {
1301 if ( index != static_cast<ComboBoxWidget*>( m_widget )->currentItem() )
1302 static_cast<ComboBoxWidget*>( m_widget )->setCurrentItem( index );
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;
1308 static_cast<HTMLOptionElementImpl*>(listItems[index])->m_selected = true;
1312 element()->onChange();
1316 void RenderSelect::slotSelectionChanged()
1318 if ( m_ignoreSelectEvents ) return;
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 );
1330 element()->onChange();
1334 void RenderSelect::setOptionsChanged(bool _optionsChanged)
1336 m_optionsChanged = _optionsChanged;
1339 KListBox* RenderSelect::createListBox()
1341 KListBox *lb = new KListBox(view()->viewport());
1342 lb->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
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);
1353 ComboBoxWidget *RenderSelect::createComboBox()
1355 ComboBoxWidget *cb = new ComboBoxWidget(view()->viewport());
1356 connect(cb, SIGNAL(activated(int)), this, SLOT(slotSelected(int)));
1360 void RenderSelect::updateSelection()
1362 QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
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());
1373 unsigned firstOption = listItems.size();
1374 i = listItems.size();
1376 if (listItems[i]->id() == ID_OPTION) {
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);
1386 Q_ASSERT(firstOption == listItems.size() || found);
1389 m_selectionChanged = false;
1393 // -------------------------------------------------------------------------
1395 TextAreaWidget::TextAreaWidget(int wrap, QWidget* parent)
1398 if(wrap != DOM::HTMLTextAreaElementImpl::ta_NoWrap) {
1399 setWordWrap(QTextEdit::WidgetWidth);
1401 setHScrollBarMode( AlwaysOff );
1402 setVScrollBarMode( AlwaysOn );
1406 setWordWrap(QTextEdit::NoWrap);
1408 setHScrollBarMode( Auto );
1409 setVScrollBarMode( Auto );
1412 KCursor::setAutoHideCursor(viewport(), true);
1413 setTextFormat(QTextEdit::PlainText);
1415 setMouseTracking(true);
1418 bool TextAreaWidget::event( QEvent *e )
1421 if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
1422 QKeyEvent* ke = (QKeyEvent*) e;
1423 if ( ke->state() & ControlButton ) {
1424 switch ( ke->key() ) {
1438 return KTextEdit::event( e );
1441 // -------------------------------------------------------------------------
1443 RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
1444 : RenderFormElement(element)
1446 TextAreaWidget *edit = new TextAreaWidget(element->wrap(), view());
1449 connect(edit,SIGNAL(textChanged()),this,SLOT(slotTextChanged()));
1450 connect(edit,SIGNAL(clicked()),this,SLOT(slotClicked()));
1453 void RenderTextArea::detach()
1455 if ( element()->m_dirtyvalue ) {
1456 element()->m_value = text();
1457 element()->m_dirtyvalue = false;
1459 RenderFormElement::detach();
1462 void RenderTextArea::handleFocusOut()
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();
1472 void RenderTextArea::calcMinMaxWidth()
1474 KHTMLAssert( !minMaxKnown() );
1476 TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
1477 const QFontMetrics &m = style()->fontMetrics();
1478 w->setTabStopWidth(8 * m.width(" "));
1480 QSize size(w->sizeWithColumnsAndRows(QMAX(element()->cols(), 1), QMAX(element()->rows(), 1)));
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)
1490 setIntrinsicWidth( size.width() );
1491 setIntrinsicHeight( size.height() );
1493 RenderFormElement::calcMinMaxWidth();
1496 void RenderTextArea::setStyle(RenderStyle *s)
1498 RenderFormElement::setStyle(s);
1500 TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
1501 w->setAlignment(textAlignment());
1503 w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
1507 void RenderTextArea::updateFromElement()
1509 TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
1510 w->setReadOnly(element()->readOnly());
1512 w->setDisabled(element()->disabled());
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);
1524 w->getCursorPosition( &line, &col );
1526 w->setCursorPosition( line, col );
1527 w->blockSignals(false);
1529 element()->m_dirtyvalue = false;
1531 RenderFormElement::updateFromElement();
1534 void RenderTextArea::close( )
1536 element()->setValue( element()->defaultValue() );
1538 RenderFormElement::close();
1541 QString RenderTextArea::text()
1544 TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
1546 if(element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical) {
1548 txt = w->textWithHardLineBreaks();
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);
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);
1562 txt += paragraphText;
1563 if (p < w->paragraphs() - 1)
1564 txt += QString::fromLatin1("\n");
1571 txt.replace(backslashAsCurrencySymbol(), '\\');
1575 void RenderTextArea::slotTextChanged()
1577 element()->m_dirtyvalue = true;
1580 void RenderTextArea::select()
1582 static_cast<TextAreaWidget *>(m_widget)->selectAll();
1585 // ---------------------------------------------------------------------------
1587 #include "render_form.moc"