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"
50 #include "KWQSlider.h"
53 using namespace khtml;
56 RenderFormElement::RenderFormElement(HTMLGenericFormElementImpl *element)
57 : RenderWidget(element)
62 RenderFormElement::~RenderFormElement()
66 short RenderFormElement::baselinePosition( bool f, bool isRootLineBox ) const
69 return marginTop() + widget()->baselinePosition(m_height);
71 return RenderWidget::baselinePosition( f, isRootLineBox ) - 2 - style()->fontMetrics().descent();
75 void RenderFormElement::setStyle(RenderStyle* s)
78 if (canHaveIntrinsicMargins())
79 addIntrinsicMarginsIfAllowed(s);
82 RenderWidget::setStyle(s);
85 // Do not paint a background or border for Aqua form elements
86 setShouldPaintBackgroundOrBorder(false);
89 m_widget->setFont(style()->font());
92 void RenderFormElement::updateFromElement()
94 m_widget->setEnabled(!element()->disabled());
97 m_widget->setPalette(QPalette(style()->backgroundColor(), style()->color()));
99 QColor color = style()->color();
100 QColor backgroundColor = style()->backgroundColor();
102 if ( color.isValid() || backgroundColor.isValid() ) {
103 QPalette pal(m_widget->palette());
105 int contrast_ = KGlobalSettings::contrast();
106 int highlightVal = 100 + (2*contrast_+4)*16/10;
107 int lowlightVal = 100 + (2*contrast_+4)*10;
109 if (backgroundColor.isValid()) {
110 for ( int i = 0; i < QPalette::NColorGroups; i++ ) {
111 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Background, backgroundColor );
112 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Light, backgroundColor.light(highlightVal) );
113 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Dark, backgroundColor.dark(lowlightVal) );
114 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Mid, backgroundColor.dark(120) );
115 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Midlight, backgroundColor.light(110) );
116 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Button, backgroundColor );
117 pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Base, backgroundColor );
120 if ( color.isValid() ) {
122 QPalette::ColorGroup cg;
123 QColorGroup::ColorRole cr;
125 const struct ColorSet toSet [] = {
126 { QPalette::Active, QColorGroup::Foreground },
127 { QPalette::Active, QColorGroup::ButtonText },
128 { QPalette::Active, QColorGroup::Text },
129 { QPalette::Inactive, QColorGroup::Foreground },
130 { QPalette::Inactive, QColorGroup::ButtonText },
131 { QPalette::Inactive, QColorGroup::Text },
132 { QPalette::Disabled,QColorGroup::ButtonText },
133 { QPalette::NColorGroups, QColorGroup::NColorRoles },
135 const ColorSet *set = toSet;
136 while( set->cg != QPalette::NColorGroups ) {
137 pal.setColor( set->cg, set->cr, color );
141 QColor disfg = color;
143 disfg.hsv( &h, &s, &v );
145 // dark bg, light fg - need a darker disabled fg
146 disfg = disfg.dark(lowlightVal);
147 else if (disfg != Qt::black)
148 // light bg, dark fg - need a lighter disabled fg - but only if !black
149 disfg = disfg.light(highlightVal);
151 // black fg - use darkgrey disabled fg
152 disfg = Qt::darkGray;
153 pal.setColor(QPalette::Disabled,QColorGroup::Foreground,disfg);
156 m_widget->setPalette(pal);
159 m_widget->unsetPalette();
163 void RenderFormElement::layout()
165 KHTMLAssert( needsLayout() );
166 KHTMLAssert( minMaxKnown() );
176 resizeWidget(m_widget,
177 m_width-borderLeft()-borderRight()-paddingLeft()-paddingRight(),
178 m_height-borderLeft()-borderRight()-paddingLeft()-paddingRight());
181 setNeedsLayout(false);
184 void RenderFormElement::slotClicked()
186 // FIXME: Should share code with KHTMLView::dispatchMouseEvent, which does a lot of the same stuff.
188 RenderArena *arena = ref();
191 QMouseEvent event(QEvent::MouseButtonRelease); // gets "current event"
192 element()->dispatchMouseEvent(&event, EventImpl::CLICK_EVENT, event.clickCount());
194 // We also send the KHTML_CLICK or KHTML_DBLCLICK event for
195 // CLICK. This is not part of the DOM specs, but is used for
196 // compatibility with the traditional onclick="" and ondblclick=""
197 // attributes, as there is no way to tell the difference between
198 // single & double clicks using DOM (only the click count is
199 // stored, which is not necessarily the same)
201 QMouseEvent e2(QEvent::MouseButtonRelease, m_mousePos, m_button, m_state);
202 element()->dispatchMouseEvent(&e2, EventImpl::CLICK_EVENT, m_clickCount);
203 element()->dispatchMouseEvent(&e2, m_isDoubleClick ? EventImpl::KHTML_DBLCLICK_EVENT : EventImpl::KHTML_CLICK_EVENT, m_clickCount);
209 Qt::AlignmentFlags RenderFormElement::textAlignment() const
211 switch (style()->textAlign()) {
222 // Just fall into the auto code for justify.
224 return style()->direction() == RTL ? AlignRight : AlignLeft;
226 assert(false); // Should never be reached.
232 void RenderFormElement::addIntrinsicMarginsIfAllowed(RenderStyle* _style)
234 // Cut out the intrinsic margins completely if we end up using mini controls.
235 if (_style->font().pixelSize() < 11)
238 int m = intrinsicMargin();
239 if (_style->width().isVariable()) {
240 if (_style->marginLeft().quirk)
241 _style->setMarginLeft(Length(m, Fixed));
242 if (_style->marginRight().quirk)
243 _style->setMarginRight(Length(m, Fixed));
246 if (_style->height().isVariable()) {
247 if (_style->marginTop().quirk)
248 _style->setMarginTop(Length(m, Fixed));
249 if (_style->marginBottom().quirk)
250 _style->setMarginBottom(Length(m, Fixed));
256 // -------------------------------------------------------------------------
258 RenderButton::RenderButton(HTMLGenericFormElementImpl *element)
259 : RenderFormElement(element)
263 short RenderButton::baselinePosition( bool f, bool isRootLineBox ) const
266 return RenderFormElement::baselinePosition( f, isRootLineBox );
268 return RenderWidget::baselinePosition( f, isRootLineBox ) - 2;
272 // -------------------------------------------------------------------------------
274 RenderCheckBox::RenderCheckBox(HTMLInputElementImpl *element)
275 : RenderButton(element)
277 QCheckBox* b = new QCheckBox(view()->viewport());
278 b->setAutoMask(true);
279 b->setMouseTracking(true);
281 connect(b,SIGNAL(stateChanged(int)),this,SLOT(slotStateChanged(int)));
282 connect(b, SIGNAL(clicked()), this, SLOT(slotClicked()));
285 void RenderCheckBox::calcMinMaxWidth()
287 KHTMLAssert( !minMaxKnown() );
290 // Let the widget tell us how big it wants to be.
291 QSize s(widget()->sizeHint());
293 QCheckBox *cb = static_cast<QCheckBox *>( m_widget );
294 QSize s( cb->style().pixelMetric( QStyle::PM_IndicatorWidth ),
295 cb->style().pixelMetric( QStyle::PM_IndicatorHeight ) );
297 setIntrinsicWidth( s.width() );
298 setIntrinsicHeight( s.height() );
300 RenderButton::calcMinMaxWidth();
303 void RenderCheckBox::updateFromElement()
305 widget()->setChecked(element()->checked());
307 RenderButton::updateFromElement();
310 // From the Qt documentation:
311 // state is 2 if the button is on, 1 if it is in the "no change" state or 0 if the button is off.
312 void RenderCheckBox::slotStateChanged(int state)
314 element()->setChecked(state == 2);
315 element()->onChange();
318 // -------------------------------------------------------------------------------
320 RenderRadioButton::RenderRadioButton(HTMLInputElementImpl *element)
321 : RenderButton(element)
323 QRadioButton* b = new QRadioButton(view()->viewport());
324 b->setAutoMask(true);
325 b->setMouseTracking(true);
327 connect(b, SIGNAL(clicked()), this, SLOT(slotClicked()));
330 void RenderRadioButton::updateFromElement()
332 widget()->setChecked(element()->checked());
334 RenderButton::updateFromElement();
337 void RenderRadioButton::slotClicked()
339 element()->setChecked(true);
341 // emit mouseClick event etc
342 RenderButton::slotClicked();
345 void RenderRadioButton::calcMinMaxWidth()
347 KHTMLAssert( !minMaxKnown() );
350 // Let the widget tell us how big it wants to be.
351 QSize s(widget()->sizeHint());
353 QRadioButton *rb = static_cast<QRadioButton *>( m_widget );
354 QSize s( rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorWidth ),
355 rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorHeight ) );
357 setIntrinsicWidth( s.width() );
358 setIntrinsicHeight( s.height() );
360 RenderButton::calcMinMaxWidth();
363 // -------------------------------------------------------------------------------
365 RenderSubmitButton::RenderSubmitButton(HTMLInputElementImpl *element)
366 : RenderButton(element)
368 QPushButton* p = new QPushButton(view()->viewport());
370 p->setAutoMask(true);
371 p->setMouseTracking(true);
372 connect(p, SIGNAL(clicked()), this, SLOT(slotClicked()));
375 QString RenderSubmitButton::rawText()
377 QString value = element()->valueWithDefault().string();
378 value = value.stripWhiteSpace();
379 value.replace(QChar('\\'), backslashAsCurrencySymbol());
384 for(unsigned int i = 0; i < value.length(); i++) {
393 void RenderSubmitButton::calcMinMaxWidth()
395 KHTMLAssert( !minMaxKnown() );
398 // Let the widget tell us how big it wants to be.
399 QSize s(widget()->sizeHint());
400 setIntrinsicWidth(s.width());
401 setIntrinsicHeight(s.height());
403 QString raw = rawText();
404 QPushButton* pb = static_cast<QPushButton*>(m_widget);
406 pb->setFont(style()->font());
408 bool empty = raw.isEmpty();
410 raw = QString::fromLatin1("XXXX");
411 QFontMetrics fm = pb->fontMetrics();
412 int margin = pb->style().pixelMetric( QStyle::PM_ButtonMargin, pb);
413 QSize s(pb->style().sizeFromContents(
414 QStyle::CT_PushButton, pb, fm.size( ShowPrefix, raw))
415 .expandedTo(QApplication::globalStrut()));
417 setIntrinsicWidth( s.width() - margin / 2 );
418 setIntrinsicHeight( s.height() - margin / 2);
421 RenderButton::calcMinMaxWidth();
426 void RenderSubmitButton::setStyle(RenderStyle *s)
428 RenderButton::setStyle(s);
430 QPushButton *w = static_cast<QPushButton*>(m_widget);
431 w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
436 void RenderSubmitButton::updateFromElement()
438 QPushButton *w = static_cast<QPushButton*>(m_widget);
440 QString oldText = w->text();
441 QString newText = rawText();
443 if ( oldText != newText )
444 setNeedsLayoutAndMinMaxRecalc();
445 RenderFormElement::updateFromElement();
448 short RenderSubmitButton::baselinePosition( bool f, bool isRootLineBox ) const
450 return RenderFormElement::baselinePosition( f, isRootLineBox );
453 // -------------------------------------------------------------------------------
455 RenderImageButton::RenderImageButton(HTMLInputElementImpl *element)
456 : RenderImage(element)
458 // ### support DOMActivate event when clicked
461 // -------------------------------------------------------------------------------
463 // FIXME: No real reason to need separate classes for RenderResetButton and
464 // RenderSubmitButton now that the default label is handled on the DOM side.
465 RenderResetButton::RenderResetButton(HTMLInputElementImpl *element)
466 : RenderSubmitButton(element)
470 // -------------------------------------------------------------------------------
472 // FIXME: No real reason to need separate classes for RenderPushButton and
473 // RenderSubmitButton now that the default label is handled on the DOM side.
474 RenderPushButton::RenderPushButton(HTMLInputElementImpl *element)
475 : RenderSubmitButton(element)
479 // -------------------------------------------------------------------------------
483 LineEditWidget::LineEditWidget(QWidget *parent)
486 setMouseTracking(true);
489 bool LineEditWidget::event( QEvent *e )
491 if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
492 QKeyEvent* ke = (QKeyEvent*) e;
493 if ( ke->state() & ControlButton ) {
494 switch ( ke->key() ) {
507 return KLineEdit::event( e );
512 // -----------------------------------------------------------------------------
514 RenderLineEdit::RenderLineEdit(HTMLInputElementImpl *element)
515 : RenderFormElement(element), m_updating(false)
518 QLineEdit::Type type;
519 switch (element->inputType()) {
520 case HTMLInputElementImpl::PASSWORD:
521 type = QLineEdit::Password;
523 case HTMLInputElementImpl::SEARCH:
524 type = QLineEdit::Search;
527 type = QLineEdit::Normal;
529 KLineEdit *edit = new KLineEdit(type);
530 if (type == QLineEdit::Search)
531 edit->setLiveSearch(false);
533 LineEditWidget *edit = new LineEditWidget(view()->viewport());
535 connect(edit,SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
536 connect(edit,SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
537 connect(edit,SIGNAL(clicked()),this,SLOT(slotClicked()));
540 connect(edit,SIGNAL(performSearch()), this, SLOT(slotPerformSearch()));
544 if(element->inputType() == HTMLInputElementImpl::PASSWORD)
545 edit->setEchoMode( QLineEdit::Password );
547 if ( element->autoComplete() ) {
548 QStringList completions = view()->formCompletionItems(element->name().string());
549 if (completions.count()) {
550 edit->completionObject()->setItems(completions);
551 edit->setContextMenuEnabled(true);
559 void RenderLineEdit::slotReturnPressed()
562 // don't submit the form when return was pressed in a completion-popup
563 KCompletionBox *box = widget()->completionBox(false);
564 if ( box && box->isVisible() && box->currentItem() != -1 )
568 // Emit onChange if necessary
569 // Works but might not be enough, dirk said he had another solution at
570 // hand (can't remember which) - David
573 HTMLFormElementImpl* fe = element()->form();
579 void RenderLineEdit::slotPerformSearch()
581 // Fire the "search" DOM event.
582 element()->dispatchHTMLEvent(EventImpl::SEARCH_EVENT, true, false);
585 void RenderLineEdit::addSearchResult()
588 widget()->addSearchResult();
592 void RenderLineEdit::handleFocusOut()
594 if ( widget() && widget()->edited() ) {
595 element()->onChange();
596 widget()->setEdited( false );
600 void RenderLineEdit::calcMinMaxWidth()
602 KHTMLAssert( !minMaxKnown() );
605 // Let the widget tell us how big it wants to be.
607 int size = element()->size();
608 QSize s(widget()->sizeForCharacterWidth(size > 0 ? size : 20));
611 const QFontMetrics &fm = style()->fontMetrics();
614 int size = element()->size();
616 int h = fm.lineSpacing();
617 int w = fm.width( 'x' ) * (size > 0 ? size : 17); // "some"
618 s = QSize(w + 2 + 2*widget()->frameWidth(),
619 QMAX(h, 14) + 2 + 2*widget()->frameWidth())
620 .expandedTo(QApplication::globalStrut());
623 setIntrinsicWidth( s.width() );
624 setIntrinsicHeight( s.height() );
626 RenderFormElement::calcMinMaxWidth();
629 void RenderLineEdit::setStyle(RenderStyle *s)
631 RenderFormElement::setStyle(s);
633 KLineEdit *w = widget();
634 w->setAlignment(textAlignment());
636 w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
640 void RenderLineEdit::updateFromElement()
642 HTMLInputElementImpl *e = element();
643 KLineEdit *w = widget();
645 int ml = e->maxLength();
646 if ( ml <= 0 || ml > 1024 )
648 if ( w->maxLength() != ml )
649 w->setMaxLength( ml );
651 if (!e->valueMatchesRenderer()) {
652 QString widgetText = w->text();
653 QString newText = e->value().string();
654 newText.replace(QChar('\\'), backslashAsCurrencySymbol());
655 if (widgetText != newText) {
656 w->blockSignals(true);
657 int pos = w->cursorPosition();
663 w->setEdited( false );
665 w->setCursorPosition(pos);
666 w->blockSignals(false);
668 e->setValueMatchesRenderer();
671 w->setReadOnly(e->readOnly());
674 // Handle updating the search attributes.
675 w->setPlaceholderString(e->getAttribute(ATTR_PLACEHOLDER).string());
676 if (w->type() == QLineEdit::Search) {
677 w->setLiveSearch(!e->getAttribute(ATTR_INCREMENTAL).isNull());
678 w->setAutoSaveName(e->getAttribute(ATTR_AUTOSAVE).string());
679 w->setMaxResults(e->maxResults());
683 RenderFormElement::updateFromElement();
686 void RenderLineEdit::slotTextChanged(const QString &string)
688 if (m_updating) // Don't alter the value if we are in the middle of initing the control, since
689 return; // we are getting the value from the DOM and it's not user input.
691 // A null string value is used to indicate that the form control has not altered the original
692 // default value. That means that we should never use the null string value when the user
693 // empties a textfield, but should always force an empty textfield to use the empty string.
694 QString newText = string.isNull() ? "" : string;
695 newText.replace(backslashAsCurrencySymbol(), QChar('\\'));
696 element()->setValueFromRenderer(newText);
699 void RenderLineEdit::select()
701 static_cast<KLineEdit*>(m_widget)->selectAll();
704 // ---------------------------------------------------------------------------
706 RenderFieldset::RenderFieldset(HTMLGenericFormElementImpl *element)
707 : RenderBlock(element)
711 RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren)
713 RenderObject* legend = findLegend();
715 if (relayoutChildren)
716 legend->setNeedsLayout(true);
717 legend->layoutIfNeeded();
719 int xPos = borderLeft() + paddingLeft() + legend->marginLeft();
720 if (style()->direction() == RTL)
721 xPos = m_width - paddingRight() - borderRight() - legend->width() - legend->marginRight();
723 int h = legend->height();
724 legend->setPos(xPos, QMAX((b-h)/2, 0));
725 m_height = QMAX(b,h) + paddingTop();
730 RenderObject* RenderFieldset::findLegend()
732 for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) {
733 if (!legend->isFloatingOrPositioned() && legend->element() &&
734 legend->element()->id() == ID_LEGEND)
740 void RenderFieldset::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
742 //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
745 int h = height() + borderTopExtra() + borderBottomExtra();
746 RenderObject* legend = findLegend();
748 return RenderBlock::paintBoxDecorations(i, _tx, _ty);
750 int yOff = (legend->yPos() > 0) ? 0 : (legend->height()-borderTop())/2;
752 _ty += yOff - borderTopExtra();
754 int my = kMax(_ty, i.r.y());
755 int end = kMin(i.r.y() + i.r.height(), _ty + h);
758 paintBackground(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
760 if (style()->hasBorder())
761 paintBorderMinusLegend(i.p, _tx, _ty, w, h, style(), legend->xPos(), legend->width());
764 void RenderFieldset::paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w, int h,
765 const RenderStyle* style, int lx, int lw)
768 const QColor& tc = style->borderTopColor();
769 const QColor& bc = style->borderBottomColor();
771 EBorderStyle ts = style->borderTopStyle();
772 EBorderStyle bs = style->borderBottomStyle();
773 EBorderStyle ls = style->borderLeftStyle();
774 EBorderStyle rs = style->borderRightStyle();
776 bool render_t = ts > BHIDDEN;
777 bool render_l = ls > BHIDDEN;
778 bool render_r = rs > BHIDDEN;
779 bool render_b = bs > BHIDDEN;
782 drawBorder(p, _tx, _ty, _tx + lx, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
783 (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0), 0);
784 drawBorder(p, _tx+lx+lw, _ty, _tx + w, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
785 0, (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
789 drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
790 (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0),
791 (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
795 const QColor& lc = style->borderLeftColor();
800 (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
805 (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
807 drawBorder(p, _tx, _ty, _tx + style->borderLeftWidth(), _ty + h, BSLeft, lc, style->color(), ls,
808 ignore_top?0:style->borderTopWidth(),
809 ignore_bottom?0:style->borderBottomWidth());
814 const QColor& rc = style->borderRightColor();
818 (rs >= DOTTED || rs == INSET) &&
819 (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
823 (rs >= DOTTED || rs == INSET) &&
824 (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
826 drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
827 ignore_top?0:style->borderTopWidth(),
828 ignore_bottom?0:style->borderBottomWidth());
832 void RenderFieldset::setStyle(RenderStyle* _style)
834 RenderBlock::setStyle(_style);
836 // WinIE renders fieldsets with display:inline like they're inline-blocks. For us,
837 // an inline-block is just a block element with replaced set to true and inline set
838 // to true. Ensure that if we ended up being inline that we set our replaced flag
839 // so that we're treated like an inline-block.
844 // -------------------------------------------------------------------------
846 RenderFileButton::RenderFileButton(HTMLInputElementImpl *element)
847 : RenderFormElement(element)
850 KWQFileButton *w = new KWQFileButton(view()->part());
851 connect(w, SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
852 connect(w, SIGNAL(clicked()), this, SLOT(slotClicked()));
855 QHBox *w = new QHBox(view()->viewport());
857 m_edit = new LineEditWidget(w);
859 connect(m_edit, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
860 connect(m_edit, SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
862 m_button = new QPushButton(i18n("Browse..."), w);
863 m_button->setFocusPolicy(QWidget::ClickFocus);
864 connect(m_button,SIGNAL(clicked()), this, SLOT(slotClicked()));
866 w->setStretchFactor(m_edit, 2);
867 w->setFocusProxy(m_edit);
873 void RenderFileButton::calcMinMaxWidth()
875 KHTMLAssert( !minMaxKnown() );
878 // Let the widget tell us how big it wants to be.
879 int size = element()->size();
880 QSize s(static_cast<KWQFileButton *>(widget())->sizeForCharacterWidth(size > 0 ? size : 20));
882 const QFontMetrics &fm = style()->fontMetrics();
884 int size = element()->size();
886 int h = fm.lineSpacing();
887 int w = fm.width( 'x' ) * (size > 0 ? size : 17); // "some"
888 w += 6 + fm.width( m_button->text() ) + 2*fm.width( ' ' );
889 s = QSize(w + 2 + 2*m_edit->frameWidth(),
890 QMAX(h, 14) + 2 + 2*m_edit->frameWidth())
891 .expandedTo(QApplication::globalStrut());
894 setIntrinsicWidth( s.width() );
895 setIntrinsicHeight( s.height() );
897 RenderFormElement::calcMinMaxWidth();
902 void RenderFileButton::handleFocusOut()
904 if ( m_edit && m_edit->edited() ) {
905 element()->onChange();
906 m_edit->setEdited( false );
912 void RenderFileButton::slotClicked()
915 RenderFormElement::slotClicked();
917 QString file_name = KFileDialog::getOpenFileName(QString::null, QString::null, 0, i18n("Browse..."));
918 if (!file_name.isNull()) {
919 element()->m_value = DOMString(file_name);
920 m_edit->setText(file_name);
925 void RenderFileButton::updateFromElement()
928 static_cast<KWQFileButton *>(widget())->setFilename(element()->value().string());
930 m_edit->blockSignals(true);
931 m_edit->setText(element()->value().string());
932 m_edit->blockSignals(false);
933 int ml = element()->maxLength();
934 if ( ml <= 0 || ml > 1024 )
936 m_edit->setMaxLength( ml );
937 m_edit->setEdited( false );
940 RenderFormElement::updateFromElement();
943 void RenderFileButton::slotReturnPressed()
945 if (element()->form())
946 element()->form()->prepareSubmit();
949 void RenderFileButton::slotTextChanged(const QString &string)
951 element()->m_value = DOMString(string);
952 element()->onChange();
955 void RenderFileButton::select()
964 void RenderFileButton::click(bool sendMouseEvents)
966 static_cast<KWQFileButton *>(widget())->click(sendMouseEvents);
971 // -------------------------------------------------------------------------
973 RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element)
974 : RenderFormElement(element)
979 // -------------------------------------------------------------------------
981 RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element)
982 : RenderBlock(element)
986 // -------------------------------------------------------------------------------
988 ComboBoxWidget::ComboBoxWidget(QWidget *parent)
989 : KComboBox(false, parent)
992 if (listBox()) listBox()->installEventFilter(this);
993 setMouseTracking(true);
996 bool ComboBoxWidget::event(QEvent *e)
999 if (e->type()==QEvent::KeyPress)
1001 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1010 return KComboBox::event(e);
1014 return KComboBox::event(e);
1017 bool ComboBoxWidget::eventFilter(QObject *dest, QEvent *e)
1020 if (dest==listBox() && e->type()==QEvent::KeyPress)
1022 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1023 bool forward = false;
1029 // ugly hack. emulate popdownlistbox() (private in QComboBox)
1030 // we re-use ke here to store the reference to the generated event.
1031 ke = new QKeyEvent(QEvent::KeyPress, Key_Escape, 0, 0);
1032 QApplication::sendEvent(dest,ke);
1033 focusNextPrevChild(forward);
1037 return KComboBox::eventFilter(dest, e);
1041 return KComboBox::eventFilter(dest, e);
1044 // -------------------------------------------------------------------------
1046 RenderSelect::RenderSelect(HTMLSelectElementImpl *element)
1047 : RenderFormElement(element)
1049 m_ignoreSelectEvents = false;
1050 m_multiple = element->multiple();
1051 m_size = element->size();
1052 m_useListBox = (m_multiple || m_size > 1);
1053 m_selectionChanged = true;
1054 m_optionsChanged = true;
1057 setQWidget(createListBox());
1059 setQWidget(createComboBox());
1064 void RenderSelect::setWidgetWritingDirection()
1066 QPainter::TextDirection d = style()->direction() == RTL ? QPainter::RTL : QPainter::LTR;
1068 static_cast<KListBox *>(m_widget)->setWritingDirection(d);
1070 static_cast<ComboBoxWidget *>(m_widget)->setWritingDirection(d);
1073 void RenderSelect::setStyle(RenderStyle *s)
1075 RenderFormElement::setStyle(s);
1076 setWidgetWritingDirection();
1081 void RenderSelect::updateFromElement()
1083 m_ignoreSelectEvents = true;
1085 // change widget type
1086 bool oldMultiple = m_multiple;
1087 unsigned oldSize = m_size;
1088 bool oldListbox = m_useListBox;
1090 m_multiple = element()->multiple();
1091 m_size = element()->size();
1092 m_useListBox = (m_multiple || m_size > 1);
1094 if (oldMultiple != m_multiple || oldSize != m_size) {
1095 if (m_useListBox != oldListbox) {
1096 // type of select has changed
1100 setQWidget(createListBox());
1102 setQWidget(createComboBox());
1104 setWidgetWritingDirection();
1108 if (m_useListBox && oldMultiple != m_multiple) {
1109 static_cast<KListBox*>(m_widget)->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
1111 m_selectionChanged = true;
1112 m_optionsChanged = true;
1115 // update contents listbox/combobox based on options in m_element
1116 if ( m_optionsChanged ) {
1117 if (element()->m_recalcListItems)
1118 element()->recalcListItems();
1119 QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1123 static_cast<KListBox*>(m_widget)->clear();
1125 static_cast<KComboBox*>(m_widget)->clear();
1127 for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) {
1128 if (listItems[listIndex]->id() == ID_OPTGROUP) {
1129 QString label = listItems[listIndex]->getAttribute(ATTR_LABEL).string();
1130 label.replace(QChar('\\'), backslashAsCurrencySymbol());
1132 // In WinIE, an optgroup can't start or end with whitespace (other than the indent
1133 // we give it). We match this behavior.
1134 label = label.stripWhiteSpace();
1138 static_cast<KListBox*>(m_widget)->appendGroupLabel(label);
1140 static_cast<KComboBox*>(m_widget)->appendGroupLabel(label);
1143 QListBoxText *item = new QListBoxText(label);
1144 static_cast<KListBox*>(m_widget)
1145 ->insertItem(item, listIndex);
1146 item->setSelectable(false);
1149 static_cast<KComboBox*>(m_widget)->insertItem(label, listIndex);
1152 else if (listItems[listIndex]->id() == ID_OPTION) {
1153 QString itemText = static_cast<HTMLOptionElementImpl*>(listItems[listIndex])->text().string();
1154 itemText.replace(QChar('\\'), backslashAsCurrencySymbol());
1156 // In WinIE, leading and trailing whitespace is ignored in options. We match this behavior.
1157 itemText = itemText.stripWhiteSpace();
1159 if (listItems[listIndex]->parentNode()->id() == ID_OPTGROUP)
1160 itemText.prepend(" ");
1164 static_cast<KListBox*>(m_widget)->appendItem(itemText);
1166 static_cast<KComboBox*>(m_widget)->appendItem(itemText);
1169 static_cast<KListBox*>(m_widget)->insertItem(itemText, listIndex);
1171 static_cast<KComboBox*>(m_widget)->insertItem(itemText, listIndex);
1176 m_selectionChanged = true;
1180 static_cast<KListBox*>(m_widget)->doneAppendingItems();
1182 setNeedsLayoutAndMinMaxRecalc();
1183 m_optionsChanged = false;
1187 if (m_selectionChanged) {
1191 m_ignoreSelectEvents = false;
1193 RenderFormElement::updateFromElement();
1198 short RenderSelect::baselinePosition( bool f, bool isRootLineBox ) const
1201 // FIXME: Should get the hardcoded constant of 7 by calling a QListBox function,
1202 // as we do for other widget classes.
1203 return RenderWidget::baselinePosition( f, isRootLineBox ) - 7;
1205 return RenderFormElement::baselinePosition( f, isRootLineBox );
1210 void RenderSelect::calcMinMaxWidth()
1212 KHTMLAssert( !minMaxKnown() );
1214 if (m_optionsChanged)
1215 updateFromElement();
1217 // ### ugly HACK FIXME!!!
1220 setNeedsLayoutAndMinMaxRecalc();
1223 RenderFormElement::calcMinMaxWidth();
1226 void RenderSelect::layout( )
1228 KHTMLAssert(needsLayout());
1229 KHTMLAssert(minMaxKnown());
1231 // ### maintain selection properly between type/size changes, and work
1232 // out how to handle multiselect->singleselect (probably just select
1233 // first selected one)
1237 KListBox* w = static_cast<KListBox*>(m_widget);
1240 QListBoxItem* p = w->firstItem();
1244 width = QMAX(width, p->width(p->listBox()));
1245 height = QMAX(height, p->height(p->listBox()));
1251 // check if multiple and size was not given or invalid
1252 // Internet Exploder sets size to QMIN(number of elements, 4)
1253 // Netscape seems to simply set it to "number of elements"
1254 // the average of that is IMHO QMIN(number of elements, 10)
1255 // so I did that ;-)
1257 size = QMIN(static_cast<KListBox*>(m_widget)->count(), 10);
1260 // Let the widget tell us how big it wants to be.
1261 QSize s(w->sizeForNumberOfLines(size));
1262 setIntrinsicWidth( s.width() );
1263 setIntrinsicHeight( s.height() );
1265 width += 2*w->frameWidth() + w->verticalScrollBar()->sizeHint().width();
1266 height = size*height + 2*w->frameWidth();
1268 setIntrinsicWidth( width );
1269 setIntrinsicHeight( height );
1273 QSize s(m_widget->sizeHint());
1274 setIntrinsicWidth( s.width() );
1275 setIntrinsicHeight( s.height() );
1278 /// uuh, ignore the following line..
1279 setNeedsLayout(true);
1280 RenderFormElement::layout();
1282 // and now disable the widget in case there is no <option> given
1283 QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1285 bool foundOption = false;
1286 for (uint i = 0; i < listItems.size() && !foundOption; i++)
1287 foundOption = (listItems[i]->id() == ID_OPTION);
1289 m_widget->setEnabled(foundOption && ! element()->disabled());
1292 void RenderSelect::slotSelected(int index)
1294 if ( m_ignoreSelectEvents ) return;
1296 KHTMLAssert( !m_useListBox );
1298 QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1299 if(index >= 0 && index < int(listItems.size()))
1301 bool found = ( listItems[index]->id() == ID_OPTION );
1304 // this one is not selectable, we need to find an option element
1305 while ( ( unsigned ) index < listItems.size() ) {
1306 if ( listItems[index]->id() == ID_OPTION ) {
1314 while ( index >= 0 ) {
1315 if ( listItems[index]->id() == ID_OPTION ) {
1325 if ( index != static_cast<ComboBoxWidget*>( m_widget )->currentItem() )
1326 static_cast<ComboBoxWidget*>( m_widget )->setCurrentItem( index );
1328 for ( unsigned int i = 0; i < listItems.size(); ++i )
1329 if ( listItems[i]->id() == ID_OPTION && i != (unsigned int) index )
1330 static_cast<HTMLOptionElementImpl*>( listItems[i] )->m_selected = false;
1332 static_cast<HTMLOptionElementImpl*>(listItems[index])->m_selected = true;
1336 element()->onChange();
1340 void RenderSelect::slotSelectionChanged()
1342 if ( m_ignoreSelectEvents ) return;
1344 // don't use listItems() here as we have to avoid recalculations - changing the
1345 // option list will make use update options not in the way the user expects them
1346 QMemArray<HTMLGenericFormElementImpl*> listItems = element()->m_listItems;
1347 for ( unsigned i = 0; i < listItems.count(); i++ )
1348 // don't use setSelected() here because it will cause us to be called
1349 // again with updateSelection.
1350 if ( listItems[i]->id() == ID_OPTION )
1351 static_cast<HTMLOptionElementImpl*>( listItems[i] )
1352 ->m_selected = static_cast<KListBox*>( m_widget )->isSelected( i );
1354 element()->onChange();
1358 void RenderSelect::setOptionsChanged(bool _optionsChanged)
1360 m_optionsChanged = _optionsChanged;
1363 KListBox* RenderSelect::createListBox()
1365 KListBox *lb = new KListBox(view()->viewport());
1366 lb->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
1368 //lb->setAutoMask(true);
1369 connect( lb, SIGNAL( selectionChanged() ), this, SLOT( slotSelectionChanged() ) );
1370 connect( lb, SIGNAL( clicked( QListBoxItem * ) ), this, SLOT( slotClicked() ) );
1371 m_ignoreSelectEvents = false;
1372 lb->setMouseTracking(true);
1377 ComboBoxWidget *RenderSelect::createComboBox()
1379 ComboBoxWidget *cb = new ComboBoxWidget(view()->viewport());
1380 connect(cb, SIGNAL(activated(int)), this, SLOT(slotSelected(int)));
1384 void RenderSelect::updateSelection()
1386 QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
1389 // if multi-select, we select only the new selected index
1390 KListBox *listBox = static_cast<KListBox*>(m_widget);
1391 for (i = 0; i < int(listItems.size()); i++)
1392 listBox->setSelected(i,listItems[i]->id() == ID_OPTION &&
1393 static_cast<HTMLOptionElementImpl*>(listItems[i])->selected());
1397 unsigned firstOption = listItems.size();
1398 i = listItems.size();
1400 if (listItems[i]->id() == ID_OPTION) {
1402 static_cast<HTMLOptionElementImpl*>(listItems[i])->m_selected = false;
1403 else if (static_cast<HTMLOptionElementImpl*>(listItems[i])->selected()) {
1404 static_cast<KComboBox*>( m_widget )->setCurrentItem(i);
1410 Q_ASSERT(firstOption == listItems.size() || found);
1413 m_selectionChanged = false;
1417 // -------------------------------------------------------------------------
1421 TextAreaWidget::TextAreaWidget(QWidget* parent)
1426 bool TextAreaWidget::event( QEvent *e )
1428 if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
1429 QKeyEvent* ke = (QKeyEvent*) e;
1430 if ( ke->state() & ControlButton ) {
1431 switch ( ke->key() ) {
1444 return KTextEdit::event( e );
1449 // -------------------------------------------------------------------------
1451 RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
1452 : RenderFormElement(element), m_dirty(false)
1455 QTextEdit *edit = new KTextEdit(view());
1457 QTextEdit *edit = new TextAreaWidget(view());
1460 if (element->wrap() != HTMLTextAreaElementImpl::ta_NoWrap)
1461 edit->setWordWrap(QTextEdit::WidgetWidth);
1463 edit->setWordWrap(QTextEdit::NoWrap);
1466 KCursor::setAutoHideCursor(edit->viewport(), true);
1467 edit->setTextFormat(QTextEdit::PlainText);
1468 edit->setAutoMask(true);
1469 edit->setMouseTracking(true);
1474 connect(edit,SIGNAL(textChanged()),this,SLOT(slotTextChanged()));
1475 connect(edit,SIGNAL(clicked()),this,SLOT(slotClicked()));
1478 void RenderTextArea::detach()
1480 element()->updateValue();
1481 RenderFormElement::detach();
1484 void RenderTextArea::handleFocusOut()
1486 if ( m_dirty && element() ) {
1487 element()->updateValue();
1488 element()->onChange();
1493 void RenderTextArea::calcMinMaxWidth()
1495 KHTMLAssert( !minMaxKnown() );
1497 QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1499 QSize size(w->sizeWithColumnsAndRows(QMAX(element()->cols(), 1), QMAX(element()->rows(), 1)));
1501 const QFontMetrics &m = style()->fontMetrics();
1502 w->setTabStopWidth(8 * m.width(" "));
1503 QSize size( QMAX(element()->cols(), 1)*m.width('x') + w->frameWidth() +
1504 w->verticalScrollBar()->sizeHint().width(),
1505 QMAX(element()->rows(), 1)*m.height() + w->frameWidth()*2 +
1506 (w->wordWrap() == QTextEdit::NoWrap ?
1507 w->horizontalScrollBar()->sizeHint().height() : 0)
1511 setIntrinsicWidth( size.width() );
1512 setIntrinsicHeight( size.height() );
1514 RenderFormElement::calcMinMaxWidth();
1517 void RenderTextArea::setStyle(RenderStyle *s)
1519 RenderFormElement::setStyle(s);
1521 QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1522 w->setAlignment(textAlignment());
1524 w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR);
1527 QScrollView::ScrollBarMode scrollMode = QScrollView::Auto;
1528 switch (style()->overflow()) {
1530 case OMARQUEE: // makes no sense, map to auto
1531 case OOVERLAY: // not implemented for text, map to auto
1535 scrollMode = QScrollView::AlwaysOff;
1538 scrollMode = QScrollView::AlwaysOn;
1541 QScrollView::ScrollBarMode horizontalScrollMode = scrollMode;
1542 if (element()->wrap() != HTMLTextAreaElementImpl::ta_NoWrap)
1543 horizontalScrollMode = QScrollView::AlwaysOff;
1546 w->setScrollBarModes(horizontalScrollMode, scrollMode);
1548 w->setHScrollBarMode(horizontalScrollMode);
1549 w->setVScrollBarMode(scrollMode);
1553 void RenderTextArea::updateFromElement()
1555 HTMLTextAreaElementImpl *e = element();
1556 QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1558 w->setReadOnly(e->readOnly());
1560 w->setDisabled(e->disabled());
1564 if (!e->valueMatchesRenderer()) {
1565 QString widgetText = text();
1566 QString text = e->value().string();
1567 text.replace(QChar('\\'), backslashAsCurrencySymbol());
1568 if (widgetText != text) {
1569 w->blockSignals(true);
1571 w->getCursorPosition( &line, &col );
1573 w->setCursorPosition( line, col );
1574 w->blockSignals(false);
1576 e->setValueMatchesRenderer();
1580 RenderFormElement::updateFromElement();
1583 QString RenderTextArea::text()
1586 QTextEdit* w = static_cast<QTextEdit*>(m_widget);
1588 if (element()->wrap() == HTMLTextAreaElementImpl::ta_Physical) {
1590 txt = w->textWithHardLineBreaks();
1592 // yeah, QTextEdit has no accessor for getting the visually wrapped text
1593 for (int p=0; p < w->paragraphs(); ++p) {
1594 int pl = w->paragraphLength(p);
1596 int lindex = w->lineOfChar(p, 0);
1597 QString paragraphText = w->text(p);
1598 for (int l = 0; l < pl; ++l) {
1599 if (lindex != w->lineOfChar(p, l)) {
1600 paragraphText.insert(l+ll++, QString::fromLatin1("\n"));
1601 lindex = w->lineOfChar(p, l);
1604 txt += paragraphText;
1605 if (p < w->paragraphs() - 1)
1606 txt += QString::fromLatin1("\n");
1613 txt.replace(backslashAsCurrencySymbol(), QChar('\\'));
1617 void RenderTextArea::slotTextChanged()
1619 element()->invalidateValue();
1623 void RenderTextArea::select()
1625 static_cast<QTextEdit *>(m_widget)->selectAll();
1628 // ---------------------------------------------------------------------------
1631 RenderSlider::RenderSlider(HTMLInputElementImpl* element)
1632 :RenderFormElement(element)
1634 QSlider* slider = new QSlider();
1636 connect(slider, SIGNAL(sliderValueChanged()), this, SLOT(slotSliderValueChanged()));
1637 connect(slider, SIGNAL(clicked()), this, SLOT(slotClicked()));
1640 void RenderSlider::calcMinMaxWidth()
1642 KHTMLAssert(!minMaxKnown());
1644 // Let the widget tell us how big it wants to be.
1645 QSize s(widget()->sizeHint());
1646 bool widthSet = !style()->width().isVariable();
1647 bool heightSet = !style()->height().isVariable();
1648 if (heightSet && !widthSet) {
1649 // Flip the intrinsic dimensions.
1650 int barLength = s.width();
1651 s = QSize(s.height(), barLength);
1653 setIntrinsicWidth(s.width());
1654 setIntrinsicHeight(s.height());
1656 RenderFormElement::calcMinMaxWidth();
1659 void RenderSlider::updateFromElement()
1661 const DOMString& value = element()->value();
1662 const DOMString& min = element()->getAttribute(ATTR_MIN);
1663 const DOMString& max = element()->getAttribute(ATTR_MAX);
1664 const DOMString& precision = element()->getAttribute(ATTR_PRECISION);
1666 double minVal = min.isNull() ? 0.0 : min.string().toDouble();
1667 double maxVal = max.isNull() ? 100.0 : max.string().toDouble();
1668 minVal = kMin(minVal, maxVal); // Make sure the range is sane.
1670 double val = value.isNull() ? (maxVal + minVal)/2.0 : value.string().toDouble();
1671 val = kMax(minVal, kMin(val, maxVal)); // Make sure val is within min/max.
1673 // Force integer value if not float (strcasecmp returns confusingly backward boolean).
1674 if (strcasecmp(precision, "float"))
1675 val = (int)(val + 0.5);
1677 element()->setValue(QString::number(val));
1679 QSlider* slider = (QSlider*)widget();
1681 slider->setMinValue(minVal);
1682 slider->setMaxValue(maxVal);
1683 slider->setValue(val);
1685 RenderFormElement::updateFromElement();
1688 void RenderSlider::slotSliderValueChanged()
1690 QSlider* slider = (QSlider*)widget();
1692 double val = slider->value();
1693 const DOMString& precision = element()->getAttribute(ATTR_PRECISION);
1695 // Force integer value if not float (strcasecmp returns confusingly backward boolean).
1696 if (strcasecmp(precision, "float"))
1697 val = (int)(val + 0.5);
1699 element()->setValue(QString::number(val));
1701 // Fire the "input" DOM event.
1702 element()->dispatchHTMLEvent(EventImpl::INPUT_EVENT, true, false);
1705 void RenderSlider::slotClicked()
1707 // emit mouseClick event etc
1708 RenderFormElement::slotClicked();
1713 #include "render_form.moc"