File indexing completed on 2024-04-28 11:38:44

0001 /*
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
0005  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
0006  *           (C) 2000 Dirk Mueller (mueller@kde.org)
0007  *           (C) 2006 Maksim Orlovich (maksim@kde.org)
0008  *           (C) 2007-2009 Germain Garand (germain@ebooksfrance.org)
0009  *           (C) 2007 Mitz Pettel (mitz@webkit.org)
0010  *           (C) 2007 Charles Samuels (charles@kde.org)
0011  *
0012  * This library is free software; you can redistribute it and/or
0013  * modify it under the terms of the GNU Library General Public
0014  * License as published by the Free Software Foundation; either
0015  * version 2 of the License, or (at your option) any later version.
0016  *
0017  * This library is distributed in the hope that it will be useful,
0018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0020  * Library General Public License for more details.
0021  *
0022  * You should have received a copy of the GNU Library General Public License
0023  * along with this library; see the file COPYING.LIB.  If not, write to
0024  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0025  * Boston, MA 02110-1301, USA.
0026  *
0027  */
0028 
0029 #include "render_form.h"
0030 
0031 #include <kcompletionbox.h>
0032 #include <kcursor.h>
0033 #include "khtml_debug.h"
0034 #include <kfind.h>
0035 #include <kfinddialog.h>
0036 #include <klocalizedstring.h>
0037 #include <kmessagebox.h>
0038 #include <kreplace.h>
0039 #include <kreplacedialog.h>
0040 #include <sonnet/dialog.h>
0041 #include <kurlcompletion.h>
0042 #include <kwindowsystem.h>
0043 #include <kstandardaction.h>
0044 #include <kactioncollection.h>
0045 #include <kdesktopfile.h>
0046 #include <kconfiggroup.h>
0047 #include <kbuildsycocaprogressdialog.h>
0048 #include <kservicetypetrader.h>
0049 #include <kservice.h>
0050 #include <sonnet/backgroundchecker.h>
0051 #include <sonnet/dialog.h>
0052 
0053 #include <QAbstractItemView>
0054 #include <QAbstractTextDocumentLayout>
0055 #include <QDialog>
0056 #include <QDialogButtonBox>
0057 #include <QDir>
0058 #include <QStyle>
0059 #include <QStyleOptionButton>
0060 #include <QLabel>
0061 #include <QStyleOptionFrame>
0062 #include <QStandardItemModel>
0063 
0064 #include <misc/helper.h>
0065 #include <xml/dom2_eventsimpl.h>
0066 #include <html/html_formimpl.h>
0067 #include <html/html_miscimpl.h>
0068 
0069 #include <assert.h>
0070 
0071 #include <khtmlview.h>
0072 #include <khtml_ext.h>
0073 #include <xml/dom_docimpl.h>
0074 
0075 #include <QMenu>
0076 #include <QBitmap>
0077 #include <QHBoxLayout>
0078 #include <QVBoxLayout>
0079 
0080 using namespace khtml;
0081 using namespace DOM;
0082 
0083 // ----------------- proxy style used to apply some CSS properties to native Qt widgets -----------------
0084 
0085 struct KHTMLProxyStyle : public QProxyStyle {
0086     KHTMLProxyStyle(QStyle *parent)
0087         : QProxyStyle(parent)
0088     {
0089         noBorder = false;
0090         left = right = top = bottom = 0;
0091         clearButtonOverlay = 0;
0092     }
0093 
0094     QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const override
0095     {
0096         QRect r = QProxyStyle::subElementRect(element, option, widget);
0097         switch (element) {
0098         case QStyle::SE_PushButtonContents:
0099         case QStyle::SE_LineEditContents:
0100         case QStyle::SE_ShapedFrameContents:
0101             r.adjust(left, top, -qMax(0, right - clearButtonOverlay), -bottom);
0102         default:
0103             break;
0104         }
0105         return r;
0106     }
0107 
0108     void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const override
0109     {
0110         if (element == QStyle::CE_ComboBoxLabel) {
0111             const QStyleOptionComboBox *o = qstyleoption_cast<const QStyleOptionComboBox *>(option);
0112             if (o) {
0113                 QStyleOptionComboBox comboOpt = *o;
0114                 comboOpt.currentText = comboOpt.currentText.trimmed();
0115                 // by default combobox label is drawn left justified, vertical centered
0116                 // translate it to reflect padding values
0117                 comboOpt.rect.translate(left, (top - bottom) / 2);
0118                 if (noBorder) {
0119                     // Need to expand a bit for some styles
0120                     comboOpt.rect.adjust(-1, -2, 1, 2);
0121                     comboOpt.state &= ~State_On;
0122                 }
0123                 return QProxyStyle::drawControl(element, &comboOpt, painter, widget);
0124             }
0125         }
0126 
0127         QProxyStyle::drawControl(element, option, painter, widget);
0128     }
0129 
0130     void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *painter, const QWidget *widget) const override
0131     {
0132         if ((cc == QStyle::CC_ComboBox) && noBorder) {
0133             if (const QStyleOptionComboBox *cbOpt = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
0134                 bool enabled = (cbOpt->state & State_Enabled);
0135                 QColor color = cbOpt->palette.color(QPalette::ButtonText);
0136                 painter->save();
0137                 painter->setBackgroundMode(Qt::TransparentMode);
0138                 painter->setPen(color);
0139                 painter->setRenderHint(QPainter::Antialiasing);
0140                 // Drop down indicator
0141                 QRect arrowRect = QProxyStyle::subControlRect(cc, opt, SC_ComboBoxArrow, widget);
0142                 arrowRect.setTop(cbOpt->rect.top());
0143                 arrowRect.setBottom(cbOpt->rect.bottom());
0144                 arrowRect.setRight(cbOpt->rect.right() - 1);
0145                 if (enabled && (cbOpt->state & State_On)) {
0146                     arrowRect.translate(1, 1);    // push effect
0147                 }
0148                 //if (!enabled) color = color.lighter();
0149                 painter->setBrush(enabled ? QBrush(color, Qt::SolidPattern) : Qt::NoBrush);
0150                 QPolygon cbArrowDown;
0151                 cbArrowDown.setPoints(6,  3, -2, 4, -2, 0, 2, -4, -2, -3, -2, 0, 1);
0152                 cbArrowDown.translate((arrowRect.x() + (arrowRect.width() >> 1)), (arrowRect.y() + (arrowRect.height() >> 1)));
0153                 painter->drawPolygon(cbArrowDown);
0154                 // Focus rect (from qcleanlooksstyle)
0155                 if (enabled && (cbOpt->state & State_HasFocus)) {
0156                     QRect focusRect = QProxyStyle::subElementRect(SE_ComboBoxFocusRect, cbOpt, widget);
0157                     focusRect.adjust(0, -2, 0, 2);
0158                     painter->setBrush(QBrush(color, Qt::Dense4Pattern));
0159                     painter->setBrushOrigin(focusRect.topLeft());
0160                     painter->setPen(Qt::NoPen);
0161                     const QRect rects[4] = {
0162                         QRect(focusRect.left(), focusRect.top(), focusRect.width(), 1),    // Top
0163                         QRect(focusRect.left(), focusRect.bottom(), focusRect.width(), 1), // Bottom
0164                         QRect(focusRect.left(), focusRect.top(), 1, focusRect.height()),   // Left
0165                         QRect(focusRect.right(), focusRect.top(), 1, focusRect.height())   // Right
0166                     };
0167                     painter->drawRects(rects, 4);
0168                 }
0169                 painter->restore();
0170 
0171                 return;
0172             }
0173         }
0174 
0175         QProxyStyle::drawComplexControl(cc, opt, painter, widget);
0176     }
0177 
0178     QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget) const override
0179     {
0180         // Make sure we give combo popup's enough room to display contents;
0181         // Qt doesn't do this by default
0182 
0183         if (cc == QStyle::CC_ComboBox && sc == SC_ComboBoxListBoxPopup) {
0184             const QComboBox *cb = qobject_cast<const QComboBox *>(widget);
0185             const QStyleOptionComboBox *cbOpt = qstyleoption_cast<const QStyleOptionComboBox *>(opt);
0186 
0187             if (cb && cbOpt) {
0188                 QFontMetrics fm = cb->fontMetrics();
0189                 // Compute content width; Qt uses the usual +4 magic number for icon/text margin
0190                 int maxW = 0;
0191                 for (int c = 0; c < cb->count(); ++c) {
0192                     int iw = fm.width(cb->itemText(c));
0193                     if (!cb->itemIcon(c).isNull()) {
0194                         iw += 4 + cb->iconSize().width();
0195                     }
0196                     maxW = qMax(maxW, iw);
0197                 }
0198 
0199                 // Now let sizeFromContent add in extra stuff.
0200                 maxW = QProxyStyle::sizeFromContents(QStyle::CT_ComboBox, opt, QSize(maxW, 1), widget).width();
0201 
0202                 // How much more room do we need for the text?
0203                 int extraW = maxW > cbOpt->rect.width() ? maxW - cbOpt->rect.width() : 0;
0204 
0205                 QRect r = QProxyStyle::subControlRect(cc, opt, sc, widget);
0206                 r.setWidth(r.width() + extraW);
0207                 return r;
0208             }
0209         }
0210 
0211         return QProxyStyle::subControlRect(cc, opt, sc, widget);
0212     }
0213 
0214     int left, right, top, bottom;
0215     int clearButtonOverlay;
0216     bool noBorder;
0217 };
0218 
0219 // ---------------------------------------------------------------------
0220 
0221 RenderFormElement::RenderFormElement(HTMLGenericFormElementImpl *element)
0222     : RenderWidget(element)
0223 //     , m_state(0)
0224     , m_proxyStyle(nullptr)
0225     , m_exposeInternalPadding(false)
0226     , m_isOxygenStyle(false)
0227 {
0228     // init RenderObject attributes
0229     setInline(true);   // our object is Inline
0230 }
0231 
0232 RenderFormElement::~RenderFormElement()
0233 {}
0234 
0235 void RenderFormElement::setStyle(RenderStyle *_style)
0236 {
0237     RenderWidget::setStyle(_style);
0238     setPadding();
0239     if (!shouldDisableNativeBorders()) {
0240         // When the widget shows native border, clipping background to border
0241         // results in a nasty rendering effects
0242         if (style()->backgroundLayers()->backgroundClip() == BGBORDER) {
0243             style()->accessBackgroundLayers()->setBackgroundClip(BGPADDING);
0244         }
0245         m_isOxygenStyle = QApplication::style()->objectName().contains("oxygen");
0246     }
0247 }
0248 
0249 int RenderFormElement::paddingTop() const
0250 {
0251     return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingTop() : 0;
0252 }
0253 int RenderFormElement::paddingBottom() const
0254 {
0255     return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingBottom() : 0;
0256 }
0257 int RenderFormElement::paddingLeft() const
0258 {
0259     return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingLeft() : 0;
0260 }
0261 int RenderFormElement::paddingRight() const
0262 {
0263     return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingRight() : 0;
0264 }
0265 
0266 bool RenderFormElement::includesPadding() const
0267 {
0268     return true;
0269 }
0270 
0271 void RenderFormElement::setPadding()
0272 {
0273     if (!includesPadding()) {
0274         return;
0275     }
0276 
0277     KHTMLProxyStyle *style = static_cast<KHTMLProxyStyle *>(getProxyStyle());
0278     style->left = RenderWidget::paddingLeft();
0279     style->right = RenderWidget::paddingRight();
0280     style->top = RenderWidget::paddingTop();
0281     style->bottom = RenderWidget::paddingBottom();
0282 }
0283 
0284 QProxyStyle *RenderFormElement::getProxyStyle()
0285 {
0286     assert(widget());
0287     if (m_proxyStyle) {
0288         return m_proxyStyle;
0289     }
0290     m_proxyStyle = new KHTMLProxyStyle(widget()->style());
0291     widget()->setStyle(m_proxyStyle);
0292     return m_proxyStyle;
0293 }
0294 
0295 short RenderFormElement::baselinePosition(bool f) const
0296 {
0297     return RenderWidget::baselinePosition(f) - 2 - style()->fontMetrics().descent();
0298 }
0299 
0300 void RenderFormElement::setQWidget(QWidget *w)
0301 {
0302     // Avoid dangling proxy pointer when we switch widgets.
0303     // the widget will cleanup the proxy, as it is its kid.
0304     m_proxyStyle = nullptr;
0305 
0306     // sets the Qt Object Name for the purposes
0307     // of setPadding() -- this is because QStyleSheet
0308     // will propagate children of 'w' even if they are toplevel, like
0309     // the "find" dialog or the popup menu
0310     w->setObjectName("RenderFormElementWidget");
0311     RenderWidget::setQWidget(w);
0312 }
0313 
0314 void RenderFormElement::updateFromElement()
0315 {
0316     m_widget->setEnabled(!element()->disabled());
0317 
0318     // If we've disabled a focused element, clear its focus,
0319     // so Qt doesn't do funny stuff like let one type into a disabled
0320     // line edit.
0321     if (element()->disabled() && element()->focused()) {
0322         document()->quietResetFocus();
0323     }
0324 
0325     RenderWidget::updateFromElement();
0326 }
0327 
0328 // Some form widgets apply the padding internally (i.e. as if they were
0329 // some kind of inline-block). Thus we only want to expose that padding
0330 // while layouting (so that width/height calculations are correct), and
0331 // then pretend it does not exist, as it is beyond the replaced edge and
0332 // thus should not affect other calculations.
0333 
0334 void RenderFormElement::calcMinMaxWidth()
0335 {
0336     m_exposeInternalPadding = true;
0337     RenderWidget::calcMinMaxWidth();
0338     m_exposeInternalPadding = false;
0339 }
0340 
0341 void RenderFormElement::calcWidth()
0342 {
0343     m_exposeInternalPadding = true;
0344     RenderWidget::calcWidth();
0345     m_exposeInternalPadding = false;
0346 }
0347 
0348 void RenderFormElement::calcHeight()
0349 {
0350     m_exposeInternalPadding = true;
0351     RenderWidget::calcHeight();
0352     m_exposeInternalPadding = false;
0353 }
0354 
0355 void RenderFormElement::layout()
0356 {
0357     KHTMLAssert(needsLayout());
0358     KHTMLAssert(minMaxKnown());
0359 
0360     // minimum height
0361     m_height = 0;
0362     calcWidth();
0363     calcHeight();
0364 
0365     if (m_widget)
0366         resizeWidget(m_width - borderLeft() - borderRight() - paddingLeft() - paddingRight(),
0367                      m_height - borderTop() - borderBottom() - paddingTop() - paddingBottom());
0368 
0369     setNeedsLayout(false);
0370 }
0371 
0372 int RenderFormElement::calcContentWidth(int w) const
0373 {
0374     if (!shouldDisableNativeBorders()) {
0375         if (style()->boxSizing() == CONTENT_BOX) {
0376             int nativeBorderWidth = m_widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, m_widget);
0377             return RenderBox::calcContentWidth(w) + 2 * nativeBorderWidth;
0378         }
0379     }
0380 
0381     return RenderBox::calcContentWidth(w);
0382 }
0383 
0384 int RenderFormElement::calcContentHeight(int h) const
0385 {
0386     if (!shouldDisableNativeBorders()) {
0387         if (style()->boxSizing() == CONTENT_BOX) {
0388             int nativeBorderWidth = m_widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, m_widget);
0389             return RenderBox::calcContentHeight(h) + 2 * nativeBorderWidth;
0390         }
0391     }
0392 
0393     return RenderBox::calcContentHeight(h);
0394 }
0395 
0396 void RenderFormElement::paintOneBackground(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer, QRect clipr, int _tx, int _ty, int w, int height)
0397 {
0398     int fudge = 0;
0399     if (!shouldDisableNativeBorders()) {
0400         fudge = m_isOxygenStyle ? 3 : 1;
0401     }
0402 
0403     paintBackgroundExtended(p, c, bgLayer, clipr, _tx, _ty, w, height,
0404                             fudge ? fudge : borderLeft(), fudge ? fudge : borderRight(), RenderWidget::paddingLeft(), RenderWidget::paddingRight(),
0405                             fudge ? fudge : borderTop(), fudge ? fudge : borderBottom(), RenderWidget::paddingTop(), RenderWidget::paddingBottom());
0406 }
0407 
0408 Qt::Alignment RenderFormElement::textAlignment() const
0409 {
0410     switch (style()->textAlign()) {
0411     case LEFT:
0412     case KHTML_LEFT:
0413         return Qt::AlignLeft;
0414     case RIGHT:
0415     case KHTML_RIGHT:
0416         return Qt::AlignRight;
0417     case CENTER:
0418     case KHTML_CENTER:
0419         return Qt::AlignHCenter;
0420     case JUSTIFY:
0421     // Just fall into the auto code for justify.
0422     case TAAUTO:
0423         return style()->direction() == RTL ? Qt::AlignRight : Qt::AlignLeft;
0424     }
0425     assert(false); // Should never be reached.
0426     return Qt::AlignLeft;
0427 }
0428 
0429 // -------------------------------------------------------------------------
0430 
0431 RenderButton::RenderButton(HTMLGenericFormElementImpl *element)
0432     : RenderFormElement(element)
0433 {
0434     m_hasTextIndentHack = false;
0435 }
0436 
0437 short RenderButton::baselinePosition(bool f) const
0438 {
0439     int ret = (height() - RenderWidget::paddingTop() - RenderWidget::paddingBottom() + 1) / 2;
0440     ret += marginTop() + RenderWidget::paddingTop();
0441     ret += ((fontMetrics(f).ascent()) / 2) - 1;
0442     return ret;
0443 }
0444 
0445 void RenderButton::layout()
0446 {
0447     RenderFormElement::layout();
0448     bool needsTextIndentHack = false;
0449     if (!style()->width().isAuto()) {
0450         // check if we need to simulate the effect of a popular
0451         // button text hiding 'trick' that makes use of negative text-indent,
0452         // which we do not support on form widgets.
0453         int ti = style()->textIndent().minWidth(containingBlockWidth());
0454         if (m_widget->width() <= qAbs(ti)) {
0455             needsTextIndentHack = true;
0456         }
0457     }
0458     if (m_hasTextIndentHack != needsTextIndentHack) {
0459         m_hasTextIndentHack = needsTextIndentHack;
0460         updateFromElement();
0461     }
0462 }
0463 
0464 void RenderButton::setStyle(RenderStyle *style)
0465 {
0466     RenderFormElement::setStyle(style);
0467     if (shouldDisableNativeBorders()) {
0468         // we paint the borders ourselves on this button,
0469         // remove the widget's native ones.
0470         KHTMLProxyStyle *style = static_cast<KHTMLProxyStyle *>(getProxyStyle());
0471         style->noBorder = true;
0472     }
0473 }
0474 
0475 // -------------------------------------------------------------------------------
0476 
0477 RenderCheckBox::RenderCheckBox(HTMLInputElementImpl *element)
0478     : RenderButton(element)
0479 {
0480     CheckBoxWidget *b = new CheckBoxWidget(view()->widget());
0481     //b->setAutoMask(true);
0482     b->setMouseTracking(true);
0483     setQWidget(b);
0484 
0485     // prevent firing toggled() signals on initialization
0486     b->setChecked(element->checked());
0487 
0488     connect(b, SIGNAL(stateChanged(int)), this, SLOT(slotStateChanged(int)));
0489     m_ignoreStateChanged = false;
0490 }
0491 
0492 void RenderCheckBox::calcMinMaxWidth()
0493 {
0494     KHTMLAssert(!minMaxKnown());
0495 
0496     QCheckBox *cb = static_cast<QCheckBox *>(m_widget);
0497     QSize s(qMin(22, qMax(14, cb->style()->pixelMetric(QStyle::PM_IndicatorWidth))),
0498             qMin(22, qMax(12, cb->style()->pixelMetric(QStyle::PM_IndicatorHeight))));
0499     setIntrinsicWidth(s.width());
0500     setIntrinsicHeight(s.height());
0501 
0502     RenderButton::calcMinMaxWidth();
0503 }
0504 
0505 void RenderCheckBox::updateFromElement()
0506 {
0507     if (widget()->isChecked() != element()->checked()) {
0508         m_ignoreStateChanged = true;
0509         widget()->setChecked(element()->checked());
0510         m_ignoreStateChanged = false;
0511     }
0512 
0513     RenderButton::updateFromElement();
0514 }
0515 
0516 void RenderCheckBox::slotStateChanged(int state)
0517 {
0518     if (m_ignoreStateChanged) {
0519         return;
0520     }
0521     element()->setChecked(state == Qt::Checked);
0522 }
0523 
0524 bool RenderCheckBox::handleEvent(const DOM::EventImpl &ev)
0525 {
0526     switch (ev.id()) {
0527     case EventImpl::DOMFOCUSIN_EVENT:
0528     case EventImpl::DOMFOCUSOUT_EVENT:
0529     case EventImpl::MOUSEMOVE_EVENT:
0530     case EventImpl::MOUSEOUT_EVENT:
0531     case EventImpl::MOUSEOVER_EVENT:
0532         return RenderButton::handleEvent(ev);
0533     default:
0534         break;
0535     }
0536     return false;
0537 }
0538 
0539 // -------------------------------------------------------------------------------
0540 
0541 RenderRadioButton::RenderRadioButton(HTMLInputElementImpl *element)
0542     : RenderButton(element)
0543 {
0544     RadioButtonWidget *b = new RadioButtonWidget(view()->widget());
0545     b->setMouseTracking(true);
0546     b->setAutoExclusive(false);
0547     setQWidget(b);
0548 
0549     // prevent firing toggled() signals on initialization
0550     b->setChecked(element->checked());
0551 
0552     connect(b, SIGNAL(toggled(bool)), this, SLOT(slotToggled(bool)));
0553     m_ignoreToggled = false;
0554 }
0555 
0556 void RenderRadioButton::updateFromElement()
0557 {
0558     m_ignoreToggled = true;
0559     widget()->setChecked(element()->checked());
0560     m_ignoreToggled = false;
0561 
0562     RenderButton::updateFromElement();
0563 }
0564 
0565 void RenderRadioButton::calcMinMaxWidth()
0566 {
0567     KHTMLAssert(!minMaxKnown());
0568 
0569     QRadioButton *rb = static_cast<QRadioButton *>(m_widget);
0570     QSize s(qMin(22, qMax(14, rb->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth))),
0571             qMin(20, qMax(12, rb->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight))));
0572     setIntrinsicWidth(s.width());
0573     setIntrinsicHeight(s.height());
0574 
0575     RenderButton::calcMinMaxWidth();
0576 }
0577 
0578 void RenderRadioButton::slotToggled(bool /*activated*/)
0579 {
0580     if (m_ignoreToggled) {
0581         return;
0582     }
0583 }
0584 
0585 bool RenderRadioButton::handleEvent(const DOM::EventImpl &ev)
0586 {
0587     switch (ev.id()) {
0588     case EventImpl::DOMFOCUSIN_EVENT:
0589     case EventImpl::DOMFOCUSOUT_EVENT:
0590     case EventImpl::MOUSEMOVE_EVENT:
0591     case EventImpl::MOUSEOUT_EVENT:
0592     case EventImpl::MOUSEOVER_EVENT:
0593         return RenderButton::handleEvent(ev);
0594     default:
0595         break;
0596     }
0597     return false;
0598 }
0599 
0600 // -------------------------------------------------------------------------------
0601 
0602 const QLatin1String sBorderNoneSheet("QPushButton{border:none}");
0603 
0604 RenderSubmitButton::RenderSubmitButton(HTMLInputElementImpl *element)
0605     : RenderButton(element)
0606 {
0607     PushButtonWidget *p = new PushButtonWidget(view()->widget());
0608     setQWidget(p);
0609     //p->setAutoMask(true);
0610     p->setMouseTracking(true);
0611     p->setDefault(false);
0612     p->setAutoDefault(false);
0613 }
0614 
0615 static inline void setStyleSheet_helper(const QString &s, QWidget *w)
0616 {
0617     // ### buggy Qt stylesheets mess with the widget palette.
0618     // force it again after any stylesheet update.
0619     QPalette pal = w->palette();
0620     w->setStyleSheet(s);
0621     w->setPalette(pal);
0622 }
0623 
0624 void RenderSubmitButton::setPadding()
0625 {
0626     // Proxy styling doesn't work well enough for buttons.
0627     // Use stylesheets instead. tests/css/button-padding-top.html
0628     assert(!m_proxyStyle);
0629 
0630     if (!includesPadding()) {
0631         return;
0632     }
0633 
0634     if (!RenderWidget::paddingLeft() && !RenderWidget::paddingRight() &&
0635             !RenderWidget::paddingTop() && !RenderWidget::paddingBottom()) {
0636         setStyleSheet_helper((shouldDisableNativeBorders() ? sBorderNoneSheet : QString()), widget());
0637         return;
0638     }
0639 
0640     setStyleSheet_helper(
0641         QString("QPushButton{padding-left:%1px; padding-right:%2px; padding-top:%3px; padding-bottom:%4px}")
0642         .arg(RenderWidget::paddingLeft())
0643         .arg(RenderWidget::paddingRight())
0644         .arg(RenderWidget::paddingTop())
0645         .arg(RenderWidget::paddingBottom()) + (shouldDisableNativeBorders() ? sBorderNoneSheet : QString())
0646         , widget());
0647 }
0648 
0649 void RenderSubmitButton::setStyle(RenderStyle *style)
0650 {
0651     // Proxy styling doesn't work well enough for buttons.
0652     // Use stylesheets instead. tests/css/button-padding-top.html
0653     assert(!m_proxyStyle);
0654     RenderFormElement::setStyle(style);
0655 
0656     QString s = widget()->styleSheet();
0657     if (shouldDisableNativeBorders()) {
0658         // we paint the borders ourselves on this button,
0659         // remove the widget's native ones.
0660         if (!s.contains(sBorderNoneSheet)) {
0661             s.append(sBorderNoneSheet);
0662             setStyleSheet_helper(s, widget());
0663         }
0664     } else {
0665         setStyleSheet_helper(s.remove(sBorderNoneSheet), widget());
0666     }
0667 }
0668 
0669 QString RenderSubmitButton::rawText()
0670 {
0671     QString value = element()->valueWithDefault().string();
0672     value = value.trimmed();
0673     QString raw;
0674     for (int i = 0; i < value.length(); i++) {
0675         raw += value[i];
0676         if (value[i] == '&') {
0677             raw += '&';
0678         }
0679     }
0680     return raw;
0681 }
0682 
0683 bool RenderSubmitButton::canHaveBorder() const
0684 {
0685     // ### TODO would be nice to be able to
0686     // return style()->hasBackgroundImage() here,
0687     // depending on a config option (e.g. 'favour usability/integration over aspect')
0688     // so that only buttons with both a custom border
0689     // and a background image are drawn without native styling.
0690     //
0691     // This would go in the same place, gui wise, as a choice of b/w default color scheme,
0692     // versus native color scheme.
0693 
0694     return true;
0695 }
0696 
0697 void RenderSubmitButton::calcMinMaxWidth()
0698 {
0699     KHTMLAssert(!minMaxKnown());
0700 
0701     QString raw = rawText();
0702     QPushButton *pb = static_cast<QPushButton *>(m_widget);
0703     pb->setText(raw);
0704     pb->setFont(style()->font());
0705 
0706     bool empty = raw.isEmpty();
0707     if (empty) {
0708         raw = QLatin1Char('X');
0709     }
0710     QFontMetrics fm = pb->fontMetrics();
0711     QSize ts = fm.size(Qt::TextShowMnemonic, raw);
0712     //Oh boy.
0713     QStyleOptionButton butOpt;
0714     butOpt.init(pb);
0715     butOpt.text = raw;
0716     QSize s = pb->style()->sizeFromContents(QStyle::CT_PushButton, &butOpt, ts, pb);
0717 
0718     s = s.expandedTo(QApplication::globalStrut());
0719     int margin = pb->style()->pixelMetric(QStyle::PM_ButtonMargin) +
0720                  pb->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2;
0721 
0722     int w = ts.width() + margin;
0723     int h = s.height();
0724 
0725     assert(includesPadding());
0726     int hpadding = RenderWidget::paddingLeft() + RenderWidget::paddingRight();
0727     int vpadding = RenderWidget::paddingTop() + RenderWidget::paddingBottom();
0728 
0729     // add 30% margins to the width (heuristics to make it look similar to IE)
0730     // ### FIXME BASELINE: we could drop this emulation and adopt Mozilla style buttons
0731     // (+/- padding: 0px 8px 0px 8px) - IE is most often in a separate css
0732     // code path nowadays, so we have wider buttons than other engines.
0733     int toAdd = (w * 13 / 10) - w - hpadding;
0734     toAdd = qMax(0, toAdd);
0735     w += toAdd;
0736 
0737     if (shouldDisableNativeBorders()) {
0738         // we paint the borders ourselves, so let's override our height to something saner
0739         h = ts.height();
0740     } else {
0741         h -= vpadding;
0742     }
0743     s = QSize(w, h).expandedTo(QApplication::globalStrut());
0744 
0745     setIntrinsicWidth(s.width());
0746     setIntrinsicHeight(s.height());
0747 
0748     RenderButton::calcMinMaxWidth();
0749 }
0750 
0751 void RenderSubmitButton::updateFromElement()
0752 {
0753     QString oldText = static_cast<QPushButton *>(m_widget)->text();
0754     QString newText = rawText();
0755     static_cast<QPushButton *>(m_widget)->setText(newText);
0756     if (oldText != newText) {
0757         setNeedsLayoutAndMinMaxRecalc();
0758     }
0759     RenderFormElement::updateFromElement();
0760 }
0761 
0762 short RenderSubmitButton::baselinePosition(bool f) const
0763 {
0764     int ret = (height() - RenderWidget::paddingTop() - RenderWidget::paddingBottom() + 1) / 2;
0765     ret += marginTop() + RenderWidget::paddingTop();
0766     ret += ((fontMetrics(f).ascent()) / 2) - 2;
0767     return ret;
0768 }
0769 
0770 // -------------------------------------------------------------------------------
0771 
0772 RenderResetButton::RenderResetButton(HTMLInputElementImpl *element)
0773     : RenderSubmitButton(element)
0774 {
0775 }
0776 
0777 // -------------------------------------------------------------------------------
0778 
0779 namespace khtml
0780 {
0781 
0782 class CompletionWidget: public KCompletionBox
0783 {
0784 public:
0785     CompletionWidget(QWidget *parent = nullptr) : KCompletionBox(parent) {}
0786     QPoint globalPositionHint() const override
0787     {
0788         QWidget *pw = parentWidget();
0789         KHTMLWidget *kwp = dynamic_cast<KHTMLWidget *>(pw);
0790         if (!kwp) {
0791             qCDebug(KHTML_LOG) << "CompletionWidget has no KHTMLWidget parent";
0792             return KCompletionBox::globalPositionHint();
0793         }
0794         QPoint dest;
0795         KHTMLView *v = kwp->m_kwp->rootViewPos(dest);
0796         QPoint ret;
0797         if (v) {
0798             ret = v->mapToGlobal(dest + QPoint(0, pw->height()));
0799             int zoomLevel = v->zoomLevel();
0800             if (zoomLevel != 100) {
0801                 ret.setX(ret.x()*zoomLevel / 100);
0802                 ret.setY(ret.y()*zoomLevel / 100);
0803             }
0804         }
0805         return ret;
0806     }
0807 };
0808 
0809 }
0810 
0811 LineEditWidget::LineEditWidget(DOM::HTMLInputElementImpl *input, KHTMLView *view, QWidget *parent)
0812     : KLineEdit(parent), m_input(input), m_view(view)
0813 {
0814     m_kwp->setIsRedirected(true);
0815     setMouseTracking(true);
0816     KActionCollection *ac = new KActionCollection(this);
0817     m_spellAction = KStandardAction::spelling(this, SLOT(slotCheckSpelling()), ac);
0818 
0819     setCompletionBox(new CompletionWidget(this));
0820     completionBox()->setObjectName("completion box");
0821     completionBox()->setFont(font());
0822 }
0823 
0824 LineEditWidget::~LineEditWidget()
0825 {
0826 }
0827 
0828 void LineEditWidget::slotCheckSpelling()
0829 {
0830     if (text().isEmpty()) {
0831         return;
0832     }
0833     Sonnet::Dialog *spellDialog = new Sonnet::Dialog(new Sonnet::BackgroundChecker(this), nullptr);
0834     connect(spellDialog, SIGNAL(replace(QString,int,QString)), this, SLOT(spellCheckerCorrected(QString,int,QString)));
0835     connect(spellDialog, SIGNAL(misspelling(QString,int)), this, SLOT(spellCheckerMisspelling(QString,int)));
0836     connect(spellDialog, SIGNAL(done(QString)), this, SLOT(slotSpellCheckDone(QString)));
0837     connect(spellDialog, SIGNAL(cancel()), this, SLOT(spellCheckerFinished()));
0838     connect(spellDialog, SIGNAL(stop()), this, SLOT(spellCheckerFinished()));
0839     spellDialog->setBuffer(text());
0840     spellDialog->show();
0841 }
0842 
0843 void LineEditWidget::spellCheckerMisspelling(const QString &_text, int pos)
0844 {
0845     highLightWord(_text.length(), pos);
0846 }
0847 
0848 void LineEditWidget::setFocus()
0849 {
0850     KLineEdit::setFocus();
0851     end(false);
0852 }
0853 
0854 void LineEditWidget::highLightWord(unsigned int length, unsigned int pos)
0855 {
0856     setSelection(pos, length);
0857 }
0858 
0859 void LineEditWidget::spellCheckerCorrected(const QString &old, int pos, const QString &corr)
0860 {
0861     if (old != corr) {
0862         setSelection(pos, old.length());
0863         insert(corr);
0864         setSelection(pos, corr.length());
0865     }
0866 }
0867 
0868 void LineEditWidget::spellCheckerFinished()
0869 {
0870 }
0871 
0872 void LineEditWidget::slotSpellCheckDone(const QString &s)
0873 {
0874     if (s != text()) {
0875         setText(s);
0876     }
0877 }
0878 
0879 namespace khtml
0880 {
0881 
0882 /**
0883   * @internal
0884   */
0885 class WebShortcutCreator
0886 {
0887 public:
0888     /**
0889       * @short Creates a Web Shourtcut without using kdebase SearchProvider class.
0890       *        It is used by LineEditWidget.
0891       */
0892     static bool createWebShortcut(QString query);
0893 
0894 private:
0895     static bool askData(QString &name, QString &keys);
0896     static void createFile(QString query, QString name, QString keys);
0897 };
0898 
0899 bool WebShortcutCreator::createWebShortcut(QString query)
0900 {
0901     QString name = i18n("New Web Shortcut");
0902     QString keys;
0903     if (askData(name, keys)) {
0904         bool isOk;
0905         do { //It's going to be checked if the keys have already been assigned
0906             isOk = true;
0907             QStringList keyList(keys.split(','));
0908             KService::List providers = KServiceTypeTrader::self()->query("SearchProvider");
0909             foreach (const KService::Ptr &provider, providers) {
0910                 if (!isOk) {
0911                     break;
0912                 }
0913                 foreach (const QString &s, provider->property("Keys").toStringList()) {
0914                     if (!isOk) {
0915                         break;
0916                     }
0917                     foreach (const QString &t, keys) {
0918                         if (!isOk) {
0919                             break;
0920                         }
0921                         if (s == t) {
0922                             KMessageBox::error(nullptr, i18n("%1 is already assigned to %2", s, provider->name()));
0923                             isOk = false;
0924                         }
0925                     }
0926                 }
0927             }
0928             if (!isOk && !askData(name, keys)) {
0929                 return false;
0930             }
0931         } while (!isOk);
0932         createFile(query, name, keys);
0933         return true;
0934     } else {
0935         return false;
0936     }
0937 }
0938 
0939 void WebShortcutCreator::createFile(QString query, QString name, QString keys)
0940 {
0941     // SearchProvider class is part of kdebase, so the file is written as
0942     // an standard desktop file.
0943     QString fileName(keys);
0944     QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kservices5/searchproviders";
0945     QDir().mkpath(dir);
0946     while (QFile::exists(dir + fileName + ".desktop")) {
0947         fileName += '_';
0948     }
0949     KDesktopFile f(dir + fileName + ".desktop");
0950     f.desktopGroup().writeEntry("Keys", keys);
0951     f.desktopGroup().writeEntry("Type", "Service");
0952     f.desktopGroup().writeEntry("ServiceTypes", "SearchProvider");
0953     f.desktopGroup().writeEntry("Name", name);
0954     f.desktopGroup().writeEntry("Query", query);
0955     f.sync();
0956     KBuildSycocaProgressDialog::rebuildKSycoca(nullptr);
0957 }
0958 
0959 bool WebShortcutCreator::askData(QString &name, QString &keys)
0960 {
0961     QDialog *dialog = new QDialog();
0962     dialog->setWindowTitle(name);
0963     QVBoxLayout *mainLayout = new QVBoxLayout();
0964     dialog->setLayout(mainLayout);
0965 
0966     QHBoxLayout *layout = new QHBoxLayout();
0967     mainLayout->addLayout(layout);
0968     QLabel *label = new QLabel(i18n("Search &provider name:"), dialog);
0969     layout->addWidget(label);
0970     QLineEdit *nameEdit = new QLineEdit(i18n("New search provider"), dialog);
0971     label->setBuddy(nameEdit);
0972     layout->addWidget(nameEdit);
0973     layout = new QHBoxLayout();
0974     mainLayout->addLayout(layout);
0975     label = new QLabel(i18n("UR&I shortcuts:"), dialog);
0976     layout->addWidget(label);
0977     QLineEdit *keysEdit = new QLineEdit(dialog);
0978     label->setBuddy(keysEdit);
0979     layout->addWidget(keysEdit);
0980 
0981     QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
0982     buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0983     QObject::connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
0984     QObject::connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
0985     mainLayout->addWidget(buttonBox);
0986 
0987     bool res = dialog->exec();
0988     if (res) {
0989         name = nameEdit->text();
0990         keys = keysEdit->text();
0991     }
0992     delete dialog;
0993     return res;
0994 }
0995 
0996 }
0997 
0998 void LineEditWidget::slotCreateWebShortcut()
0999 {
1000     QString queryName(m_input->name().string());
1001     HTMLFormElementImpl *form = m_input->form();
1002     QUrl url(form->action().string());
1003     QUrl baseUrl(m_view->part()->baseURL().url() + '?');
1004     if (url.path().isEmpty()) {
1005         url.setPath(baseUrl.path());
1006     }
1007     if (url.host().isEmpty()) {
1008         url.setScheme(baseUrl.scheme());
1009         url.setHost(baseUrl.host());
1010     }
1011     NodeImpl *node;
1012     HTMLInputElementImpl *inputNode;
1013     for (unsigned long i = 0; (node = form->elements()->item(i)); i++) {
1014         inputNode = dynamic_cast<HTMLInputElementImpl *>(node);
1015         if (inputNode) {
1016             if ((!inputNode->name().string().size()) ||
1017                     (inputNode->name().string() == queryName)) {
1018                 continue;
1019             } else {
1020                 switch (inputNode->inputType()) {
1021                 case HTMLInputElementImpl::CHECKBOX:
1022                 case HTMLInputElementImpl::RADIO:
1023                     if (!inputNode->checked()) {
1024                         break;
1025                     }
1026                 case HTMLInputElementImpl::TEXT:
1027                 case HTMLInputElementImpl::PASSWORD:
1028                 case HTMLInputElementImpl::HIDDEN:
1029                     url.addQueryItem(inputNode->name().string(), inputNode->value().string());
1030                 default:
1031                     break;
1032                 }
1033             }
1034         }
1035     }
1036     QString query(url.url());
1037     if (!query.contains("?")) {
1038         query += '?'; //This input is the only one of the form
1039     }
1040     query += '&' + queryName + "=\\{@}";
1041     WebShortcutCreator::createWebShortcut(query);
1042 }
1043 
1044 void LineEditWidget::contextMenuEvent(QContextMenuEvent *e)
1045 {
1046     QMenu *popup = createStandardContextMenu();
1047 
1048     if (!popup) {
1049         return;
1050     }
1051 
1052     if (m_input->autoComplete()) {
1053         popup->addSeparator();
1054         QAction *act = popup->addAction(QIcon::fromTheme("edit-clear-history"), i18n("Clear &History"));
1055         act->setEnabled(compObj() && !compObj()->isEmpty());
1056         connect(act, SIGNAL(triggered()),
1057                 this, SLOT(clearHistoryActivated()));
1058     }
1059 
1060     if (echoMode() == QLineEdit::Normal &&
1061             !isReadOnly()) {
1062         popup->addSeparator();
1063 
1064         popup->addAction(m_spellAction);
1065         m_spellAction->setEnabled(!text().isEmpty());
1066     }
1067     if (!m_view->part()->onlyLocalReferences()) {
1068         popup->addSeparator();
1069         QAction *act = popup->addAction(i18n("Create Web Shortcut"));
1070         connect(act, SIGNAL(triggered()),
1071                 this, SLOT(slotCreateWebShortcut()));
1072     }
1073 
1074     emit aboutToShowContextMenu(popup);
1075 
1076     popup->exec(e->globalPos());
1077     delete popup;
1078 }
1079 
1080 void LineEditWidget::clearHistoryActivated()
1081 {
1082     m_view->clearCompletionHistory(m_input->name().string());
1083     if (compObj()) {
1084         compObj()->clear();
1085     }
1086 }
1087 
1088 bool LineEditWidget::event(QEvent *e)
1089 {
1090     if (KLineEdit::event(e)) {
1091         return true;
1092     }
1093 #if 0
1094     if (e->type() == QEvent::AccelAvailable && isReadOnly()) {
1095         QKeyEvent *ke = (QKeyEvent *) e;
1096         if (ke->modifiers() & Qt::ControlModifier) {
1097             switch (ke->key()) {
1098             case Qt::Key_Left:
1099             case Qt::Key_Right:
1100             case Qt::Key_Up:
1101             case Qt::Key_Down:
1102             case Qt::Key_Home:
1103             case Qt::Key_End:
1104                 ke->accept();
1105             default:
1106                 break;
1107             }
1108         }
1109     }
1110 #endif
1111     return false;
1112 }
1113 
1114 void LineEditWidget::mouseMoveEvent(QMouseEvent *e)
1115 {
1116     // hack to prevent Qt from calling setCursor on the widget
1117     setDragEnabled(false);
1118     KLineEdit::mouseMoveEvent(e);
1119     setDragEnabled(true);
1120 }
1121 
1122 // -----------------------------------------------------------------------------
1123 
1124 RenderLineEdit::RenderLineEdit(HTMLInputElementImpl *element)
1125     : RenderFormElement(element), m_blockElementUpdates(false)
1126 {
1127     LineEditWidget *edit = new LineEditWidget(element, view(), view()->widget());
1128     connect(edit, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
1129     connect(edit, SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString)));
1130 
1131     if (element->inputType() == HTMLInputElementImpl::PASSWORD) {
1132         edit->setEchoMode(QLineEdit::Password);
1133     }
1134 
1135     if (element->autoComplete()) {
1136         QStringList completions = view()->formCompletionItems(element->name().string());
1137         if (completions.count()) {
1138             edit->completionObject()->setItems(completions);
1139             edit->setContextMenuPolicy(Qt::NoContextMenu);
1140             edit->completionBox()->setTabHandling(false);
1141         }
1142     }
1143 
1144     setQWidget(edit);
1145 }
1146 
1147 short RenderLineEdit::baselinePosition(bool f) const
1148 {
1149     bool hasFrame = static_cast<LineEditWidget *>(widget())->hasFrame();
1150     int bTop = hasFrame ? 0 : borderTop();
1151     int bBottom = hasFrame ? 0 : borderBottom();
1152     int ret = (height() - RenderWidget::paddingTop() - RenderWidget::paddingBottom() - bTop - bBottom + 1) / 2;
1153     ret += marginTop() + RenderWidget::paddingTop() + bTop;
1154     ret += ((fontMetrics(f).ascent()) / 2) - 2;
1155     return ret;
1156 }
1157 
1158 void RenderLineEdit::setStyle(RenderStyle *_style)
1159 {
1160     RenderFormElement::setStyle(_style);
1161 
1162     if (widget()->alignment() != textAlignment()) {
1163         widget()->setAlignment(textAlignment());
1164     }
1165 
1166     bool showClearButton = (!shouldDisableNativeBorders() && !_style->hasBackgroundImage());
1167 
1168     if (!showClearButton && widget()->isClearButtonEnabled()) {
1169         widget()->setClearButtonEnabled(false);
1170     } else if (showClearButton && !widget()->isClearButtonEnabled()) {
1171         widget()->setClearButtonEnabled(true);
1172         QObjectList children = widget()->children();
1173         foreach (QObject *object, children) {
1174             QWidget *w = qobject_cast<QWidget *>(object);
1175             if (w && !w->isWindow() && (w->objectName() == "KLineEditButton")) {
1176                 // this duplicates KHTMLView's handleWidget but this widget
1177                 // is created on demand, so it might not be here at ChildPolished time
1178                 w->installEventFilter(view());
1179             }
1180         }
1181     }
1182 
1183     if (m_proxyStyle) {
1184         static_cast<KHTMLProxyStyle *>(m_proxyStyle)->clearButtonOverlay = qMax(0, widget()->clearButtonUsedSize().width());
1185     }
1186 }
1187 
1188 void RenderLineEdit::highLightWord(unsigned int length, unsigned int pos)
1189 {
1190     LineEditWidget *w = static_cast<LineEditWidget *>(m_widget);
1191     if (w) {
1192         w->highLightWord(length, pos);
1193     }
1194 }
1195 
1196 void RenderLineEdit::slotReturnPressed()
1197 {
1198     // don't submit the form when return was pressed in a completion-popup
1199     KCompletionBox *box = widget()->completionBox(false);
1200 
1201     if (box && box->isVisible() && box->currentRow() != -1) {
1202         box->hide();
1203         return;
1204     }
1205 
1206     // Emit onChange if necessary
1207     // Works but might not be enough, dirk said he had another solution at
1208     // hand (can't remember which) - David
1209     handleFocusOut();
1210 
1211     HTMLFormElementImpl *fe = element()->form();
1212     if (fe) {
1213         fe->submitFromKeyboard();
1214     }
1215 }
1216 
1217 void RenderLineEdit::handleFocusOut()
1218 {
1219     if (widget() && widget()->isModified()) {
1220         element()->onChange();
1221         widget()->setModified(false);
1222     }
1223 }
1224 
1225 void RenderLineEdit::calcMinMaxWidth()
1226 {
1227     KHTMLAssert(!minMaxKnown());
1228 
1229     const QFontMetrics &fm = style()->fontMetrics();
1230     QSize s;
1231 
1232     int size = (element()->size() > 0) ? (element()->size() + 1) : 17; // "some"
1233 
1234     int h = fm.lineSpacing();
1235     int w = (fm.height() * size) / 2; // on average a character cell is twice as tall as it is wide
1236 
1237     QStyleOptionFrame opt;
1238     opt.initFrom(widget());
1239     if (widget()->hasFrame()) {
1240         opt.lineWidth = widget()->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, widget());
1241     }
1242 
1243     s = QSize(w, qMax(h, 14));
1244     s = widget()->style()->sizeFromContents(QStyle::CT_LineEdit, &opt, s, widget());
1245     s = s.expandedTo(QApplication::globalStrut());
1246 
1247     setIntrinsicWidth(s.width());
1248     setIntrinsicHeight(s.height());
1249 
1250     RenderFormElement::calcMinMaxWidth();
1251 }
1252 
1253 void RenderLineEdit::updateFromElement()
1254 {
1255     int ml = element()->maxLength();
1256     if (ml < 0) {
1257         ml = 32767;
1258     }
1259 
1260     if (widget()->maxLength() != ml)  {
1261         widget()->setMaxLength(ml);
1262     }
1263 
1264     if (element()->value().string() != widget()->text()) {
1265         m_blockElementUpdates = true;  // Do not block signals here (#188374)
1266         int pos = widget()->cursorPosition();
1267         widget()->setText(element()->value().string());
1268         widget()->setCursorPosition(pos);
1269         m_blockElementUpdates = false;
1270     }
1271     widget()->setReadOnly(element()->readOnly());
1272 
1273     widget()->setPlaceholderText(element()->placeholder().string().remove(QLatin1Char('\n')).remove(QLatin1Char('\r')));
1274 
1275     RenderFormElement::updateFromElement();
1276 }
1277 
1278 void RenderLineEdit::slotTextChanged(const QString &string)
1279 {
1280     if (m_blockElementUpdates) {
1281         return;
1282     }
1283 
1284     // don't use setValue here!
1285     element()->m_value = string.isNull() ? DOMString("") : string;
1286     element()->m_unsubmittedFormChange = true;
1287 }
1288 
1289 void RenderLineEdit::select()
1290 {
1291     static_cast<LineEditWidget *>(m_widget)->selectAll();
1292 }
1293 
1294 long RenderLineEdit::selectionStart()
1295 {
1296     LineEditWidget *w = static_cast<LineEditWidget *>(m_widget);
1297     if (w->hasSelectedText()) {
1298         return w->selectionStart();
1299     } else {
1300         return w->cursorPosition();
1301     }
1302 }
1303 
1304 long RenderLineEdit::selectionEnd()
1305 {
1306     LineEditWidget *w = static_cast<LineEditWidget *>(m_widget);
1307     if (w->hasSelectedText()) {
1308         return w->selectionStart() + w->selectedText().length();
1309     } else {
1310         return w->cursorPosition();
1311     }
1312 }
1313 
1314 void RenderLineEdit::setSelectionStart(long pos)
1315 {
1316     LineEditWidget *w = static_cast<LineEditWidget *>(m_widget);
1317     //See whether we have a non-empty selection now.
1318     long end = selectionEnd();
1319     if (end > pos) {
1320         w->setSelection(pos, end - pos);
1321     }
1322     w->setCursorPosition(pos);
1323 }
1324 
1325 void RenderLineEdit::setSelectionEnd(long pos)
1326 {
1327     LineEditWidget *w = static_cast<LineEditWidget *>(m_widget);
1328     //See whether we have a non-empty selection now.
1329     long start = selectionStart();
1330     if (start < pos) {
1331         w->setSelection(start, pos - start);
1332     }
1333 
1334     w->setCursorPosition(pos);
1335 }
1336 
1337 void RenderLineEdit::setSelectionRange(long start, long end)
1338 {
1339     LineEditWidget *w = static_cast<LineEditWidget *>(m_widget);
1340     w->setCursorPosition(end);
1341     w->setSelection(start, end - start);
1342 }
1343 
1344 // ---------------------------------------------------------------------------
1345 
1346 RenderFieldset::RenderFieldset(HTMLGenericFormElementImpl *element)
1347     : RenderBlock(element)
1348 {
1349     m_intrinsicWidth = 0;
1350 }
1351 
1352 void RenderFieldset::calcMinMaxWidth()
1353 {
1354     RenderBlock::calcMinMaxWidth();
1355     if (style()->htmlHacks()) {
1356         if (RenderObject *legend = findLegend()) {
1357             int legendMinWidth = legend->minWidth();
1358 
1359             Length legendMarginLeft = legend->style()->marginLeft();
1360             Length legendMarginRight = legend->style()->marginLeft();
1361 
1362             if (legendMarginLeft.isFixed()) {
1363                 legendMinWidth += legendMarginLeft.value();
1364             }
1365 
1366             if (legendMarginRight.isFixed()) {
1367                 legendMinWidth += legendMarginRight.value();
1368             }
1369 
1370             m_intrinsicWidth = qMax((int)m_minWidth, legendMinWidth + paddingLeft() + paddingRight() + borderLeft() + borderRight());
1371         }
1372     }
1373 }
1374 
1375 RenderObject *RenderFieldset::layoutLegend(bool relayoutChildren)
1376 {
1377     RenderObject *legend = findLegend();
1378     if (legend) {
1379         if (relayoutChildren) {
1380             legend->setNeedsLayout(true);
1381         }
1382         legend->layoutIfNeeded();
1383 
1384         int xPos = borderLeft() + paddingLeft() + legend->marginLeft();
1385         if (style()->direction() == RTL) {
1386             xPos = m_width - paddingRight() - borderRight() - legend->width() - legend->marginRight();
1387         }
1388         int b = borderTop();
1389         int h = legend->height();
1390         legend->setPos(xPos, qMax((b - h) / 2, 0));
1391         m_height = qMax(b, h) + paddingTop();
1392     }
1393     return legend;
1394 }
1395 
1396 RenderObject *RenderFieldset::findLegend() const
1397 {
1398     for (RenderObject *legend = firstChild(); legend; legend = legend->nextSibling()) {
1399         if (!legend->isFloatingOrPositioned() && legend->element() &&
1400                 legend->element()->id() == ID_LEGEND) {
1401             return legend;
1402         }
1403     }
1404     return nullptr;
1405 }
1406 
1407 void RenderFieldset::paintBoxDecorations(PaintInfo &pI, int _tx, int _ty)
1408 {
1409     //qCDebug(KHTML_LOG) << renderName() << "::paintDecorations()";
1410 
1411     RenderObject *legend = findLegend();
1412     if (!legend) {
1413         return RenderBlock::paintBoxDecorations(pI, _tx, _ty);
1414     }
1415 
1416     int w = width();
1417     int h = height() + borderTopExtra() + borderBottomExtra();
1418     int yOff = (legend->yPos() > 0) ? 0 : (legend->height() - borderTop()) / 2;
1419     int legendBottom = _ty + legend->yPos() + legend->height();
1420     h -= yOff;
1421     _ty += yOff - borderTopExtra();
1422 
1423     QRect cr = QRect(_tx, _ty, w, h).intersected(pI.r);
1424     paintOneBackground(pI.p, style()->backgroundColor(), style()->backgroundLayers(), cr, _tx, _ty, w, h);
1425 
1426     if (style()->hasBorder()) {
1427         paintBorderMinusLegend(pI.p, _tx, _ty, w, h, style(), legend->xPos(), legend->width(), legendBottom);
1428     }
1429 }
1430 
1431 void RenderFieldset::paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w, int h,
1432         const RenderStyle *style, int lx, int lw, int lb)
1433 {
1434 
1435     const QColor &tc = style->borderTopColor();
1436     const QColor &bc = style->borderBottomColor();
1437 
1438     EBorderStyle ts = style->borderTopStyle();
1439     EBorderStyle bs = style->borderBottomStyle();
1440     EBorderStyle ls = style->borderLeftStyle();
1441     EBorderStyle rs = style->borderRightStyle();
1442 
1443     bool render_t = ts > BHIDDEN;
1444     bool render_l = ls > BHIDDEN;
1445     bool render_r = rs > BHIDDEN;
1446     bool render_b = bs > BHIDDEN;
1447 
1448     int borderLeftWidth = style->borderLeftWidth();
1449     int borderRightWidth = style->borderRightWidth();
1450 
1451     if (render_t) {
1452         if (lx >= borderLeftWidth)
1453             drawBorder(p, _tx, _ty, _tx + lx, _ty +  style->borderTopWidth(), BSTop, tc, style->color(), ts,
1454                        (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? style->borderLeftWidth() : 0), 0);
1455         if (lx + lw <=  w - borderRightWidth)
1456             drawBorder(p, _tx + lx + lw, _ty, _tx + w, _ty +  style->borderTopWidth(), BSTop, tc, style->color(), ts,
1457                        0, (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? style->borderRightWidth() : 0));
1458     }
1459 
1460     if (render_b)
1461         drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
1462                    (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? style->borderLeftWidth() : 0),
1463                    (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? style->borderRightWidth() : 0));
1464 
1465     if (render_l) {
1466         const QColor &lc = style->borderLeftColor();
1467 
1468         bool ignore_top =
1469             (tc == lc) &&
1470             (ls >= OUTSET) &&
1471             (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
1472 
1473         bool ignore_bottom =
1474             (bc == lc) &&
1475             (ls >= OUTSET) &&
1476             (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
1477 
1478         int startY = _ty;
1479         if (lx < borderLeftWidth && lx + lw > 0) {
1480             // The legend intersects the border.
1481             ignore_top = true;
1482             startY = lb;
1483         }
1484 
1485         drawBorder(p, _tx, startY, _tx + borderLeftWidth, _ty + h, BSLeft, lc, style->color(), ls,
1486                    ignore_top ? 0 : style->borderTopWidth(),
1487                    ignore_bottom ? 0 : style->borderBottomWidth());
1488     }
1489 
1490     if (render_r) {
1491         const QColor &rc = style->borderRightColor();
1492 
1493         bool ignore_top =
1494             (tc == rc) &&
1495             (rs >= DOTTED || rs == INSET) &&
1496             (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
1497 
1498         bool ignore_bottom =
1499             (bc == rc) &&
1500             (rs >= DOTTED || rs == INSET) &&
1501             (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
1502 
1503         int startY = _ty;
1504         if (lx < w && lx + lw > w - borderRightWidth) {
1505             // The legend intersects the border.
1506             ignore_top = true;
1507             startY = lb;
1508         }
1509 
1510         drawBorder(p, _tx + w - borderRightWidth, startY, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
1511                    ignore_top ? 0 : style->borderTopWidth(),
1512                    ignore_bottom ? 0 : style->borderBottomWidth());
1513     }
1514 }
1515 
1516 void RenderFieldset::setStyle(RenderStyle *_style)
1517 {
1518     RenderBlock::setStyle(_style);
1519 
1520     // WinIE renders fieldsets with display:inline like they're inline-blocks.  For us,
1521     // an inline-block is just a block element with replaced set to true and inline set
1522     // to true.  Ensure that if we ended up being inline that we set our replaced flag
1523     // so that we're treated like an inline-block.
1524     if (isInline()) {
1525         setReplaced(true);
1526     }
1527 }
1528 
1529 // -------------------------------------------------------------------------
1530 
1531 RenderFileButton::RenderFileButton(HTMLInputElementImpl *element)
1532     : RenderFormElement(element)
1533 {
1534     FileButtonWidget *w = new FileButtonWidget(view()->widget());
1535 
1536     w->setMode(KFile::File | KFile::ExistingOnly);
1537     w->lineEdit()->setCompletionBox(new CompletionWidget(w));
1538     w->completionObject()->setDir(QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)));
1539 
1540     connect(w->lineEdit(), SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
1541     connect(w->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString)));
1542     connect(w, SIGNAL(urlSelected(QUrl)), this, SLOT(slotUrlSelected()));
1543 
1544     setQWidget(w);
1545     m_haveFocus = false;
1546 }
1547 
1548 short RenderFileButton::baselinePosition(bool f) const
1549 {
1550     int bTop = borderTop();
1551     int bBottom = borderBottom();
1552     int ret = (height() - paddingTop() - paddingBottom() - bTop - bBottom + 1) / 2;
1553     ret += marginTop() + paddingTop() + bTop;
1554     ret += ((fontMetrics(f).ascent()) / 2) - 2;
1555     return ret;
1556 }
1557 
1558 void RenderFileButton::calcMinMaxWidth()
1559 {
1560     KHTMLAssert(!minMaxKnown());
1561 
1562     const QFontMetrics &fm = style()->fontMetrics();
1563     int size = (element()->size() > 0) ? (element()->size() + 1) : 17; // "some"
1564 
1565     int h = fm.lineSpacing();
1566     int w = (fm.height() * size) / 2; // on average a character cell is twice as tall as it is wide
1567     KLineEdit *edit = widget()->lineEdit();
1568 
1569     QStyleOptionFrame opt;
1570     opt.initFrom(edit);
1571     if (edit->hasFrame()) {
1572         opt.lineWidth = edit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, edit);
1573     }
1574 
1575     QSize s(w, qMax(h, 14));
1576     s = edit->style()->sizeFromContents(QStyle::CT_LineEdit, &opt, s, edit);
1577     s = s.expandedTo(QApplication::globalStrut());
1578 
1579     QSize bs = widget()->minimumSizeHint() - edit->minimumSizeHint();
1580 
1581     setIntrinsicWidth(s.width() + bs.width());
1582     setIntrinsicHeight(qMax(s.height(), bs.height()));
1583 
1584     RenderFormElement::calcMinMaxWidth();
1585 }
1586 
1587 void RenderFileButton::handleFocusOut()
1588 {
1589     if (widget()->lineEdit() && widget()->lineEdit()->isModified()) {
1590         element()->onChange();
1591         widget()->lineEdit()->setModified(false);
1592     }
1593 }
1594 
1595 void RenderFileButton::updateFromElement()
1596 {
1597     KLineEdit *edit = widget()->lineEdit();
1598     bool blocked = edit->blockSignals(true);
1599     edit->setText(element()->value().string());
1600     edit->blockSignals(blocked);
1601     edit->setModified(false);
1602 
1603     RenderFormElement::updateFromElement();
1604 }
1605 
1606 void RenderFileButton::slotReturnPressed()
1607 {
1608     // don't submit the form when return was pressed in a completion-popup
1609     KCompletionBox *box = widget()->lineEdit()->completionBox(false);
1610     if (box && box->isVisible() && box->currentRow() != -1) {
1611         box->hide();
1612         return;
1613     }
1614 
1615     handleFocusOut();
1616 
1617     if (element()->form()) {
1618         element()->form()->submitFromKeyboard();
1619     }
1620 }
1621 
1622 void RenderFileButton::slotTextChanged(const QString &/*string*/)
1623 {
1624     element()->m_value = QUrl(widget()->url()).toDisplayString(QUrl::PreferLocalFile);
1625 }
1626 
1627 void RenderFileButton::slotUrlSelected()
1628 {
1629     element()->onChange();
1630 }
1631 
1632 void RenderFileButton::select()
1633 {
1634     widget()->lineEdit()->selectAll();
1635 }
1636 
1637 // -------------------------------------------------------------------------
1638 
1639 RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element)
1640     : RenderFormElement(element)
1641 {
1642 
1643 }
1644 
1645 // -------------------------------------------------------------------------
1646 
1647 RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element)
1648     : RenderBlock(element)
1649 {
1650 }
1651 
1652 // -------------------------------------------------------------------------------
1653 
1654 bool ListBoxWidget::event(QEvent *event)
1655 {
1656     // accept all wheel events so that they are not propagated to the view
1657     // once either end of the list is reached.
1658     bool ret = QListWidget::event(event);
1659     if (event->type() == QEvent::Wheel) {
1660         event->accept();
1661         ret = true;
1662     }
1663     return ret;
1664 }
1665 
1666 ComboBoxWidget::ComboBoxWidget(QWidget *parent)
1667     : KComboBox(false, parent)
1668 {
1669     m_kwp->setIsRedirected(true);
1670     //setAutoMask(true);
1671     if (view()) {
1672         view()->installEventFilter(this);
1673     }
1674     setMouseTracking(true);
1675 }
1676 
1677 void ComboBoxWidget::showPopup()
1678 {
1679     QPoint p = pos();
1680     QPoint dest(p);
1681     QWidget *parent = parentWidget();
1682     KHTMLView *v = m_kwp->rootViewPos(dest);
1683     int zoomLevel = v ? v->zoomLevel() : 100;
1684     if (zoomLevel != 100) {
1685         if (v) {
1686             // we need to place the popup even lower on the screen, take in count the widget is bigger
1687             // now, so we add also the difference between the original height, and the zoomed height
1688             dest.setY(dest.y() + (sizeHint().height() * zoomLevel / 100 - sizeHint().height()));
1689         }
1690     }
1691     bool blocked = blockSignals(true);
1692     if (v != parent) {
1693         setParent(v);
1694     }
1695     move(dest);
1696     blockSignals(blocked);
1697 
1698     KComboBox::showPopup();
1699 
1700     blocked = blockSignals(true);
1701     if (v != parent) {
1702         setParent(parent);
1703         // undo side effect of setParent()
1704         show();
1705     }
1706     move(p);
1707     blockSignals(blocked);
1708 }
1709 
1710 void ComboBoxWidget::hidePopup()
1711 {
1712     KComboBox::hidePopup();
1713 }
1714 
1715 bool ComboBoxWidget::event(QEvent *e)
1716 {
1717     if (KComboBox::event(e)) {
1718         return true;
1719     }
1720     if (e->type() == QEvent::KeyPress) {
1721         QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1722         switch (ke->key()) {
1723         case Qt::Key_Return:
1724         case Qt::Key_Enter:
1725             showPopup();
1726             ke->accept();
1727             return true;
1728         default:
1729             return false;
1730         }
1731     }
1732     return false;
1733 }
1734 
1735 bool ComboBoxWidget::eventFilter(QObject *dest, QEvent *e)
1736 {
1737     if (dest == view() &&  e->type() == QEvent::KeyPress) {
1738         QKeyEvent *ke = static_cast<QKeyEvent *>(e);
1739         bool forward = false;
1740         switch (ke->key()) {
1741         case Qt::Key_Tab:
1742             forward = true;
1743         // fall through
1744         case Qt::Key_Backtab:
1745             // ugly hack. emulate popdownlistbox() (private in QComboBox)
1746             // we re-use ke here to store the reference to the generated event.
1747             ke = new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier);
1748             QApplication::sendEvent(dest, ke);
1749             focusNextPrevChild(forward);
1750             delete ke;
1751             return true;
1752         default:
1753             return KComboBox::eventFilter(dest, e);
1754         }
1755     }
1756     return KComboBox::eventFilter(dest, e);
1757 }
1758 
1759 void ComboBoxWidget::keyPressEvent(QKeyEvent *e)
1760 {
1761     // Normally, widgets are not sent Tab keys this way in the first
1762     // place as they are handled by QWidget::event() for focus handling
1763     // already. But we get our events via EventPropagator::sendEvent()
1764     // directly. Ignore them so that HTMLGenericFormElementImpl::
1765     // defaultEventHandler() can call focusNextPrev().
1766     if (e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab) {
1767         e->ignore();
1768         return;
1769     }
1770     KComboBox::keyPressEvent(e);
1771 }
1772 
1773 // -------------------------------------------------------------------------
1774 
1775 RenderSelect::RenderSelect(HTMLSelectElementImpl *element)
1776     : RenderFormElement(element)
1777 {
1778     m_ignoreSelectEvents = false;
1779     m_multiple = element->multiple();
1780     m_size = element->size();
1781     m_useListBox = (m_multiple || m_size > 1);
1782     m_selectionChanged = true;
1783     m_optionsChanged = true;
1784 
1785     if (m_useListBox) {
1786         setQWidget(createListBox());
1787     } else {
1788         setQWidget(createComboBox());
1789         getProxyStyle(); // We always need it to make sure popups are big enough
1790     }
1791 }
1792 
1793 void RenderSelect::clearItemFlags(int index, Qt::ItemFlags flags)
1794 {
1795     if (m_useListBox) {
1796         QListWidgetItem *item = static_cast<QListWidget *>(m_widget)->item(index);
1797         item->setFlags(item->flags() & ~flags);
1798     } else {
1799         KComboBox *combo = static_cast<KComboBox *>(m_widget);
1800         if (QStandardItemModel *model = qobject_cast<QStandardItemModel *>(combo->model())) {
1801             QStandardItem *item = model->item(index);
1802             item->setFlags(item->flags() & ~flags);
1803         }
1804     }
1805 }
1806 
1807 void RenderSelect::setStyle(RenderStyle *_style)
1808 {
1809     RenderFormElement::setStyle(_style);
1810     if (!m_useListBox) {
1811         KHTMLProxyStyle *proxyStyle = static_cast<KHTMLProxyStyle *>(getProxyStyle());
1812         proxyStyle->noBorder = shouldDisableNativeBorders();
1813     }
1814 }
1815 
1816 void RenderSelect::updateFromElement()
1817 {
1818     m_ignoreSelectEvents = true;
1819 
1820     // change widget type
1821     bool oldMultiple = m_multiple;
1822     unsigned oldSize = m_size;
1823     bool oldListbox = m_useListBox;
1824 
1825     m_multiple = element()->multiple();
1826     m_size = element()->size();
1827     m_useListBox = (m_multiple || m_size > 1);
1828 
1829     if (oldMultiple != m_multiple || oldSize != m_size) {
1830         if (m_useListBox != oldListbox) {
1831             // type of select has changed
1832             if (m_useListBox) {
1833                 setQWidget(createListBox());
1834             } else {
1835                 setQWidget(createComboBox());
1836             }
1837 
1838             // Call setStyle() to fix unwanted font size change (#142722)
1839             // and to update our proxy style properties
1840             setStyle(style());
1841         }
1842 
1843         if (m_useListBox && oldMultiple != m_multiple) {
1844             static_cast<QListWidget *>(m_widget)->setSelectionMode(m_multiple ?
1845                     QListWidget::ExtendedSelection
1846                     : QListWidget::SingleSelection);
1847         }
1848         m_selectionChanged = true;
1849         m_optionsChanged = true;
1850     }
1851 
1852     // update contents listbox/combobox based on options in m_element
1853     if (m_optionsChanged) {
1854         if (element()->m_recalcListItems) {
1855             element()->recalcListItems();
1856         }
1857         const QVector<HTMLGenericFormElementImpl *> listItems = element()->listItems();
1858         int listIndex;
1859 
1860         if (m_useListBox) {
1861             static_cast<QListWidget *>(m_widget)->clear();
1862         } else {
1863             static_cast<KComboBox *>(m_widget)->clear();
1864         }
1865 
1866         for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) {
1867             if (listItems[listIndex]->id() == ID_OPTGROUP) {
1868                 DOMString text = listItems[listIndex]->getAttribute(ATTR_LABEL);
1869                 if (text.isNull()) {
1870                     text = "";
1871                 }
1872 
1873                 text = text.implementation()->collapseWhiteSpace(false, false);
1874 
1875                 if (m_useListBox) {
1876                     QListWidgetItem *item = new QListWidgetItem(QString(text.implementation()->s, text.implementation()->l));
1877                     static_cast<QListWidget *>(m_widget)->insertItem(listIndex, item);
1878                 } else {
1879                     static_cast<KComboBox *>(m_widget)->insertItem(listIndex, QString(text.implementation()->s, text.implementation()->l));
1880                 }
1881 
1882                 bool disabled = !listItems[listIndex]->getAttribute(ATTR_DISABLED).isNull();
1883                 if (disabled) {
1884                     clearItemFlags(listIndex, Qt::ItemIsSelectable | Qt::ItemIsEnabled);
1885                 } else {
1886                     clearItemFlags(listIndex, Qt::ItemIsSelectable);
1887                 }
1888             } else if (listItems[listIndex]->id() == ID_OPTION) {
1889                 HTMLOptionElementImpl *optElem = static_cast<HTMLOptionElementImpl *>(listItems[listIndex]);
1890 
1891                 DOMString domText = optElem->text();
1892                 // Prefer label if set
1893                 DOMString label = optElem->getAttribute(ATTR_LABEL);
1894                 if (!label.isEmpty()) {
1895                     domText = label;
1896                 }
1897                 domText = domText.implementation()->collapseWhiteSpace(false, false);
1898 
1899                 QString text;
1900 
1901                 ElementImpl *parentOptGroup = optElem->parentNode()->id() == ID_OPTGROUP ?
1902                                               static_cast<ElementImpl *>(optElem->parentNode()) : nullptr;
1903 
1904                 if (parentOptGroup) {
1905                     text = QLatin1String("    ") + domText.string();
1906                 } else {
1907                     text = domText.string();
1908                 }
1909 
1910                 if (m_useListBox) {
1911                     static_cast<QListWidget *>(m_widget)->insertItem(listIndex, text);
1912                 } else {
1913                     static_cast<KComboBox *>(m_widget)->insertItem(listIndex, text);
1914                 }
1915 
1916                 bool disabled = !optElem->getAttribute(ATTR_DISABLED).isNull();
1917                 if (parentOptGroup) {
1918                     disabled = disabled || !parentOptGroup->getAttribute(ATTR_DISABLED).isNull();
1919                 }
1920 
1921                 if (disabled) {
1922                     clearItemFlags(listIndex, Qt::ItemIsSelectable | Qt::ItemIsEnabled);
1923                 }
1924             } else {
1925                 KHTMLAssert(false);
1926             }
1927 
1928             m_selectionChanged = true;
1929         }
1930 
1931         // QComboBox caches the size hint unless you call setFont (ref: TT docu)
1932         if (!m_useListBox) {
1933             KComboBox *that = static_cast<KComboBox *>(m_widget);
1934             that->setFont(that->font());
1935         }
1936         setNeedsLayoutAndMinMaxRecalc();
1937         m_optionsChanged = false;
1938     }
1939 
1940     // update selection
1941     if (m_selectionChanged) {
1942         updateSelection();
1943     }
1944 
1945     m_ignoreSelectEvents = false;
1946 
1947     RenderFormElement::updateFromElement();
1948 }
1949 
1950 short RenderSelect::baselinePosition(bool f) const
1951 {
1952     if (m_useListBox) {
1953         return RenderFormElement::baselinePosition(f);
1954     }
1955 
1956     int bTop = shouldDisableNativeBorders() ? borderTop() : 0;
1957     int bBottom = shouldDisableNativeBorders() ? borderBottom() : 0;
1958     int ret = (height() - RenderWidget::paddingTop() - RenderWidget::paddingBottom() - bTop - bBottom + 1) / 2;
1959     ret += marginTop() + RenderWidget::paddingTop() + bTop;
1960     ret += ((fontMetrics(f).ascent()) / 2) - 2;
1961     return ret;
1962 }
1963 
1964 void RenderSelect::calcMinMaxWidth()
1965 {
1966     KHTMLAssert(!minMaxKnown());
1967 
1968     if (m_optionsChanged) {
1969         updateFromElement();
1970     }
1971 
1972     // ### ugly HACK FIXME!!!
1973     setMinMaxKnown();
1974     layoutIfNeeded();
1975     setNeedsLayoutAndMinMaxRecalc();
1976     // ### end FIXME
1977 
1978     RenderFormElement::calcMinMaxWidth();
1979 }
1980 
1981 void RenderSelect::layout()
1982 {
1983     KHTMLAssert(needsLayout());
1984     KHTMLAssert(minMaxKnown());
1985 
1986     // ### maintain selection properly between type/size changes, and work
1987     // out how to handle multiselect->singleselect (probably just select
1988     // first selected one)
1989 
1990     // calculate size
1991     if (m_useListBox) {
1992         QListWidget *w = static_cast<QListWidget *>(m_widget);
1993 
1994         int width = 0;
1995         int height = 0;
1996 
1997         QAbstractItemModel *m = w->model();
1998         QAbstractItemDelegate *d = w->itemDelegate();
1999         QStyleOptionViewItem so;
2000         so.font = w->font();
2001 
2002         for (int rowIndex = 0; rowIndex < w->count(); rowIndex++) {
2003             QModelIndex mi = m->index(rowIndex, 0);
2004             QSize s = d->sizeHint(so, mi);
2005             width = qMax(width, s.width());
2006             height = qMax(height, s.height());
2007         }
2008 
2009         if (!height) {
2010             height = w->fontMetrics().height();
2011         }
2012         if (!width) {
2013             width = w->fontMetrics().width('x');
2014         }
2015 
2016         int size = m_size;
2017         // check if multiple and size was not given or invalid
2018         // Internet Exploder sets size to qMin(number of elements, 4)
2019         // Netscape seems to simply set it to "number of elements"
2020         // the average of that is IMHO qMin(number of elements, 10)
2021         // so I did that ;-)
2022         if (size < 1) {
2023             size = qMin(w->count(), 10);
2024         }
2025 
2026         QStyleOptionFrame opt;
2027         opt.initFrom(w);
2028         opt.lineWidth = w->lineWidth();
2029         opt.midLineWidth = w->midLineWidth();
2030         opt.frameShape = w->frameShape();
2031         QRect r = w->style()->subElementRect(QStyle::SE_ShapedFrameContents, &opt, w);
2032         QRect o = opt.rect;
2033         int hfw = (r.left() - o.left()) + (o.right() - r.right());
2034         int vfw = (r.top() - o.top()) + (o.bottom() - r.bottom());
2035 
2036         width += hfw + w->verticalScrollBar()->sizeHint().width();
2037         // FIXME BASELINE: the 3 lines below could be removed.
2038         int lhs = m_widget->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
2039         if (lhs > 0) {
2040             width += lhs;
2041         }
2042         height = size * height + vfw;
2043 
2044         assert(includesPadding());
2045         width -= RenderWidget::paddingLeft() + RenderWidget::paddingRight();
2046         height -= RenderWidget::paddingTop() + RenderWidget::paddingBottom();
2047 
2048         setIntrinsicWidth(width);
2049         setIntrinsicHeight(height);
2050     } else {
2051         QSize s(m_widget->sizeHint());
2052         int w = s.width();
2053         int h = s.height();
2054 
2055         if (shouldDisableNativeBorders()) {
2056             const int dfw = 2 * m_widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, m_widget);
2057             w -= dfw;
2058             h -= dfw;
2059         }
2060 
2061         setIntrinsicWidth(w);
2062         setIntrinsicHeight(h);
2063     }
2064 
2065     /// uuh, ignore the following line..
2066     setNeedsLayout(true);
2067     RenderFormElement::layout();
2068 
2069     // and now disable the widget in case there is no <option> given
2070     const QVector<HTMLGenericFormElementImpl *> listItems = element()->listItems();
2071 
2072     bool foundOption = false;
2073     for (int i = 0; i < listItems.size() && !foundOption; i++) {
2074         foundOption = (listItems[i]->id() == ID_OPTION);
2075     }
2076 
2077     m_widget->setEnabled(foundOption && ! element()->disabled());
2078 }
2079 
2080 void RenderSelect::slotSelected(int index) // emitted by the combobox only
2081 {
2082     if (m_ignoreSelectEvents) {
2083         return;
2084     }
2085 
2086     KHTMLAssert(!m_useListBox);
2087 
2088     const QVector<HTMLGenericFormElementImpl *> listItems = element()->listItems();
2089     if (index >= 0 && index < int(listItems.size())) {
2090         bool found = (listItems[index]->id() == ID_OPTION);
2091 
2092         if (!found) {
2093             // this one is not selectable,  we need to find an option element
2094             while (index < listItems.size()) {
2095                 if (listItems[index]->id() == ID_OPTION) {
2096                     found = true;
2097                     break;
2098                 }
2099                 ++index;
2100             }
2101 
2102             if (!found) {
2103                 while (index >= 0) {
2104                     if (listItems[index]->id() == ID_OPTION) {
2105                         found = true;
2106                         break;
2107                     }
2108                     --index;
2109                 }
2110             }
2111         }
2112 
2113         if (found) {
2114             bool changed = false;
2115 
2116             for (int i = 0; i < listItems.size(); ++i)
2117                 if (listItems[i]->id() == ID_OPTION && i !=  index) {
2118                     HTMLOptionElementImpl *opt = static_cast<HTMLOptionElementImpl *>(listItems[i]);
2119                     changed |= (opt->m_selected == true);
2120                     opt->m_selected = false;
2121                 }
2122 
2123             HTMLOptionElementImpl *opt = static_cast<HTMLOptionElementImpl *>(listItems[index]);
2124             changed |= (opt->m_selected == false);
2125             opt->m_selected = true;
2126 
2127             if (index != static_cast<ComboBoxWidget *>(m_widget)->currentIndex()) {
2128                 static_cast<ComboBoxWidget *>(m_widget)->setCurrentIndex(index);
2129             }
2130 
2131             // When selecting an optgroup item, and we move forward to we
2132             // shouldn't emit onChange. Hence this bool, the if above doesn't do it.
2133             if (changed) {
2134                 ref();
2135                 element()->onChange();
2136                 deref();
2137             }
2138         }
2139     }
2140 }
2141 
2142 void RenderSelect::slotSelectionChanged() // emitted by the listbox only
2143 {
2144     if (m_ignoreSelectEvents) {
2145         return;
2146     }
2147 
2148     // don't use listItems() here as we have to avoid recalculations - changing the
2149     // option list will make use update options not in the way the user expects them
2150     const QVector<HTMLGenericFormElementImpl *> listItems = element()->m_listItems;
2151     for (int i = 0; i < listItems.count(); i++)
2152         // don't use setSelected() here because it will cause us to be called
2153         // again with updateSelection.
2154         if (listItems[i]->id() == ID_OPTION)
2155             static_cast<HTMLOptionElementImpl *>(listItems[i])
2156             ->m_selected = static_cast<QListWidget *>(m_widget)->item(i)->isSelected();
2157 
2158     ref();
2159     element()->onChange();
2160     deref();
2161 }
2162 
2163 void RenderSelect::setOptionsChanged(bool _optionsChanged)
2164 {
2165     m_optionsChanged = _optionsChanged;
2166 }
2167 
2168 void RenderSelect::setPadding()
2169 {
2170     RenderFormElement::setPadding();
2171 }
2172 
2173 ListBoxWidget *RenderSelect::createListBox()
2174 {
2175     ListBoxWidget *lb = new ListBoxWidget(view()->widget());
2176     lb->setSelectionMode(m_multiple ? QListWidget::ExtendedSelection : QListWidget::SingleSelection);
2177     connect(lb, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged()));
2178     m_ignoreSelectEvents = false;
2179     lb->setMouseTracking(true);
2180 
2181     return lb;
2182 }
2183 
2184 ComboBoxWidget *RenderSelect::createComboBox()
2185 {
2186     ComboBoxWidget *cb = new ComboBoxWidget(view()->widget());
2187     connect(cb, SIGNAL(activated(int)), this, SLOT(slotSelected(int)));
2188     return cb;
2189 }
2190 
2191 void RenderSelect::updateSelection()
2192 {
2193     const QVector<HTMLGenericFormElementImpl *> listItems = element()->listItems();
2194     int i;
2195     if (m_useListBox) {
2196         // if multi-select, we select only the new selected index
2197         QListWidget *listBox = static_cast<QListWidget *>(m_widget);
2198         for (i = 0; i < int(listItems.size()); i++)
2199             listBox->item(i)->setSelected(listItems[i]->id() == ID_OPTION &&
2200                                           static_cast<HTMLOptionElementImpl *>(listItems[i])->selectedBit());
2201     } else {
2202         bool found = false;
2203         int firstOption = i = listItems.size();
2204         while (i--)
2205             if (listItems[i]->id() == ID_OPTION) {
2206                 if (found) {
2207                     static_cast<HTMLOptionElementImpl *>(listItems[i])->m_selected = false;
2208                 } else if (static_cast<HTMLOptionElementImpl *>(listItems[i])->selectedBit()) {
2209                     static_cast<KComboBox *>(m_widget)->setCurrentIndex(i);
2210                     found = true;
2211                 }
2212                 firstOption = i;
2213             }
2214 
2215         if (!found && firstOption != listItems.size()) {
2216             // select first option (IE7/Gecko behaviour)
2217             static_cast<HTMLOptionElementImpl *>(listItems[firstOption])->m_selected = true;
2218             static_cast<KComboBox *>(m_widget)->setCurrentIndex(firstOption);
2219         }
2220     }
2221 
2222     m_selectionChanged = false;
2223 }
2224 
2225 // -------------------------------------------------------------------------
2226 
2227 TextAreaWidget::TextAreaWidget(int wrap, QWidget *parent)
2228     : KTextEdit(parent)
2229 {
2230     m_kwp->setIsRedirected(true);
2231 
2232     if (wrap != DOM::HTMLTextAreaElementImpl::ta_NoWrap) {
2233         setLineWrapMode(QTextEdit::WidgetWidth);
2234     } else {
2235         setLineWrapMode(QTextEdit::NoWrap);
2236     }
2237 
2238     setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2239     setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2240 
2241     KCursor::setAutoHideCursor(viewport(), true);
2242     setAcceptRichText(false);
2243     setMouseTracking(true);
2244 }
2245 
2246 TextAreaWidget::~TextAreaWidget()
2247 {
2248 }
2249 
2250 void TextAreaWidget::scrollContentsBy(int dx, int dy)
2251 {
2252     KTextEdit::scrollContentsBy(dx, dy);
2253     update();
2254 }
2255 
2256 bool TextAreaWidget::event(QEvent *e)
2257 {
2258 #if 0
2259     if (e->type() == QEvent::AccelAvailable && isReadOnly()) {
2260         QKeyEvent *ke = (QKeyEvent *) e;
2261         if (ke->modifiers() & Qt::ControlModifier) {
2262             switch (ke->key()) {
2263             case Qt::Key_Left:
2264             case Qt::Key_Right:
2265             case Qt::Key_Up:
2266             case Qt::Key_Down:
2267             case Qt::Key_Home:
2268             case Qt::Key_End:
2269                 ke->accept();
2270             default:
2271                 break;
2272             }
2273         }
2274     }
2275 #endif
2276     // accept all wheel events so that they are not propagated to the view
2277     // once either end of the widget is reached.
2278     bool ret = KTextEdit::event(e);
2279     if (e->type() == QEvent::Wheel) {
2280         e->accept();
2281         ret = true;
2282     }
2283     return ret;
2284 }
2285 
2286 void TextAreaWidget::keyPressEvent(QKeyEvent *e)
2287 {
2288     // The ComboBoxWidget::keyPressEvent() comment about having to
2289     // deal with events coming from EventPropagator::sendEvent()
2290     // directly applies here, too.
2291     if ((e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab) &&
2292             tabChangesFocus()) {
2293         e->ignore();
2294         return;
2295     }
2296     KTextEdit::keyPressEvent(e);
2297 }
2298 
2299 // -------------------------------------------------------------------------
2300 
2301 RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
2302     : RenderFormElement(element)
2303 {
2304     TextAreaWidget *edit = new TextAreaWidget(element->wrap(), view());
2305     setQWidget(edit);
2306     const KHTMLSettings *settings = view()->part()->settings();
2307     edit->setCheckSpellingEnabled(settings->autoSpellCheck());
2308     edit->setTabChangesFocus(! settings->allowTabulation());
2309 
2310     connect(edit, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
2311 
2312     setText(element->value().string());
2313     m_textAlignment = edit->alignment();
2314 }
2315 
2316 RenderTextArea::~RenderTextArea()
2317 {
2318     element()->m_value = text();
2319 }
2320 
2321 void RenderTextArea::handleFocusOut()
2322 {
2323     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2324 
2325     if (w && element()->m_changed) {
2326         element()->m_changed = false;
2327         element()->onChange();
2328     }
2329 }
2330 
2331 void RenderTextArea::calcMinMaxWidth()
2332 {
2333     KHTMLAssert(!minMaxKnown());
2334 
2335     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2336     const QFontMetrics &m = style()->fontMetrics();
2337     w->setTabStopWidth(8 * m.width(" "));
2338     int lvs = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing));
2339     int lhs = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing));
2340     int llm = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutLeftMargin));
2341     int lrm = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutRightMargin));
2342     int lbm = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
2343     int ltm = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutTopMargin));
2344 
2345     QStyleOptionFrame opt;
2346     opt.initFrom(w);
2347     opt.lineWidth = w->lineWidth();
2348     opt.midLineWidth = w->midLineWidth();
2349     opt.frameShape = w->frameShape();
2350     QRect r = w->style()->subElementRect(QStyle::SE_ShapedFrameContents, &opt, w);
2351     QRect o = opt.rect;
2352     int hfw = (r.left() - o.left()) + (o.right() - r.right());
2353     int vfw = (r.top() - o.top()) + (o.bottom() - r.bottom());
2354 
2355     QSize size(qMax(element()->cols(), 1L)*m.width('x') + hfw + llm + lrm +
2356                w->verticalScrollBar()->sizeHint().width() + lhs,
2357                qMax(element()->rows(), 1L)*m.lineSpacing() + vfw + lbm + ltm +
2358                (w->lineWrapMode() == QTextEdit::NoWrap ?
2359                 w->horizontalScrollBar()->sizeHint().height() + lvs : 0)
2360               );
2361 
2362     assert(includesPadding());
2363     size.rwidth() -= RenderWidget::paddingLeft() + RenderWidget::paddingRight();
2364     size.rheight() -= RenderWidget::paddingTop() + RenderWidget::paddingBottom();
2365 
2366     setIntrinsicWidth(size.width());
2367     setIntrinsicHeight(size.height());
2368 
2369     RenderFormElement::calcMinMaxWidth();
2370 }
2371 
2372 void RenderTextArea::setStyle(RenderStyle *_style)
2373 {
2374     RenderFormElement::setStyle(_style);
2375 
2376     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2377 
2378     if (m_textAlignment != textAlignment()) {
2379         m_textAlignment = textAlignment();
2380         bool unsubmittedFormChange = element()->m_unsubmittedFormChange;
2381         bool blocked = w->blockSignals(true);
2382         int cx = w->horizontalScrollBar()->value();
2383         int cy = w->verticalScrollBar()->value();
2384         QTextCursor tc = w->textCursor();
2385         // Set alignment on all textarea's paragraphs
2386         w->selectAll();
2387         w->setAlignment(m_textAlignment);
2388         w->setTextCursor(tc);
2389         w->horizontalScrollBar()->setValue(cx);
2390         w->verticalScrollBar()->setValue(cy);
2391         w->blockSignals(blocked);
2392         element()->m_unsubmittedFormChange = unsubmittedFormChange;
2393     }
2394 
2395     if (style()->overflowX() == OSCROLL) {
2396         w->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
2397     } else if (style()->overflowX() == OHIDDEN) {
2398         w->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2399     } else {
2400         w->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2401     }
2402     if (style()->overflowY() == OSCROLL) {
2403         w->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
2404     } else if (style()->overflowY() == OHIDDEN) {
2405         w->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2406     } else {
2407         w->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
2408     }
2409 }
2410 
2411 short RenderTextArea::scrollWidth() const
2412 {
2413     return RenderObject::scrollWidth();
2414 }
2415 
2416 int RenderTextArea::scrollHeight() const
2417 {
2418     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2419     int contentHeight = qRound(w->document()->size().height());
2420     return qMax(contentHeight, RenderObject::clientHeight());
2421 }
2422 
2423 void RenderTextArea::setText(const QString &newText)
2424 {
2425     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2426 
2427     // When this is called, m_value in the element must have just
2428     // been set to new value --- see if we have any work to do
2429 
2430     QString oldText = text();
2431     int oldTextLen = oldText.length();
2432     int newTextLen = newText.length();
2433     if (newTextLen != oldTextLen || newText != oldText) {
2434         bool blocked = w->blockSignals(true);
2435         int cx = w->horizontalScrollBar()->value();
2436         int cy = w->verticalScrollBar()->value();
2437         // Not using setPlaintext as it resets text alignment property
2438         int minLen = qMin(newTextLen, oldTextLen);
2439         int ex = 0;
2440         while (ex < minLen && (newText.at(ex) == oldText.at(ex))) {
2441             ++ex;
2442         }
2443         QTextCursor tc = w->textCursor();
2444         tc.setPosition(ex, QTextCursor::MoveAnchor);
2445         tc.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
2446         tc.insertText(newText.right(newTextLen - ex));
2447 
2448         if (oldTextLen == 0) {
2449             tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
2450         } else {
2451             tc.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
2452         }
2453         w->setTextCursor(tc);
2454         w->horizontalScrollBar()->setValue(cx);
2455         w->verticalScrollBar()->setValue(cy);
2456         w->blockSignals(blocked);
2457     }
2458 }
2459 
2460 void RenderTextArea::updateFromElement()
2461 {
2462     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2463     w->setReadOnly(element()->readOnly());
2464     w->setPlaceholderText(element()->placeholder().string());
2465     RenderFormElement::updateFromElement();
2466 }
2467 
2468 QString RenderTextArea::text()
2469 {
2470     // ### We may want to cache this when physical, since the DOM no longer caches,
2471     // but seeing how text() has always been called on textChanged(), it's probably not needed
2472 
2473     QString txt;
2474     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2475 #ifdef __GNUC__
2476 #warning "Physical wrap mode needs testing (also in ::selection*)"
2477 #endif
2478     if (element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical) {
2479         QTextCursor tc(w->document());
2480         while (!tc.atEnd()) {
2481             tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
2482             txt += tc.selectedText();
2483             if (tc.movePosition(QTextCursor::Right)) {
2484                 txt += QLatin1String("\n");
2485                 tc.movePosition(QTextCursor::StartOfLine);
2486             } else {
2487                 break;
2488             }
2489         }
2490     } else {
2491         txt = w->toPlainText();
2492     }
2493     return txt;
2494 }
2495 
2496 void RenderTextArea::highLightWord(unsigned int length, unsigned int pos)
2497 {
2498     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2499     if (w) {
2500         w->highlightWord(length, pos);
2501     }
2502 }
2503 
2504 void RenderTextArea::slotTextChanged()
2505 {
2506     element()->m_changed    = true;
2507     if (element()->m_value != text()) {
2508         element()->m_unsubmittedFormChange = true;
2509     }
2510 }
2511 
2512 void RenderTextArea::select()
2513 {
2514     static_cast<TextAreaWidget *>(m_widget)->selectAll();
2515 }
2516 
2517 long RenderTextArea::selectionStart()
2518 {
2519     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2520     return w->textCursor().selectionStart();
2521 }
2522 
2523 long RenderTextArea::selectionEnd()
2524 {
2525     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2526     return w->textCursor().selectionEnd();
2527 }
2528 
2529 static void setPhysWrapPos(QTextCursor &otc, bool selStart, int idx)
2530 {
2531     QTextCursor tc = otc;
2532     tc.setPosition(0);
2533     tc.movePosition(QTextCursor::EndOfLine);
2534     while (!tc.atEnd()) {
2535         if (tc.movePosition(QTextCursor::Down) && tc.position() < idx) {
2536             --idx;
2537         }
2538         if (tc.position() >= idx) {
2539             break;
2540         }
2541     }
2542     otc.setPosition(idx, selStart ? QTextCursor::MoveAnchor : QTextCursor::KeepAnchor);
2543 }
2544 
2545 void RenderTextArea::setSelectionStart(long offset)
2546 {
2547     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2548     QTextCursor tc = w->textCursor();
2549     if (element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical) {
2550         setPhysWrapPos(tc, true /*selStart*/, offset);
2551     } else {
2552         tc.setPosition(offset);
2553     }
2554     w->setTextCursor(tc);
2555 }
2556 
2557 void RenderTextArea::setSelectionEnd(long offset)
2558 {
2559     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2560     QTextCursor tc = w->textCursor();
2561     if (element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical) {
2562         setPhysWrapPos(tc, false /*selStart*/, offset);
2563     } else {
2564         tc.setPosition(offset, QTextCursor::KeepAnchor);
2565     }
2566     w->setTextCursor(tc);
2567 }
2568 
2569 void RenderTextArea::setSelectionRange(long start, long end)
2570 {
2571     setSelectionStart(start);
2572     setSelectionEnd(end);
2573 }
2574 // ---------------------------------------------------------------------------
2575 
2576 #include "moc_render_form.cpp"