1 /*
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2000 Dirk Mueller (mueller@kde.org)
7  *           (C) 2006 Maksim Orlovich (maksim@kde.org)
8  *           (C) 2007-2009 Germain Garand (germain@ebooksfrance.org)
9  *           (C) 2007 Mitz Pettel (mitz@webkit.org)
10  *           (C) 2007 Charles Samuels (charles@kde.org)
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB.  If not, write to
24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA.
26  *
27  */
28 
29 #include "render_form.h"
30 
31 #include <kcompletionbox.h>
32 #include <kcursor.h>
33 #include "khtml_debug.h"
34 #include <kfind.h>
35 #include <kfinddialog.h>
36 #include <klocalizedstring.h>
37 #include <kmessagebox.h>
38 #include <kreplace.h>
39 #include <kreplacedialog.h>
40 #include <sonnet/dialog.h>
41 #include <kurlcompletion.h>
42 #include <kwindowsystem.h>
43 #include <kstandardaction.h>
44 #include <kactioncollection.h>
45 #include <kdesktopfile.h>
46 #include <kconfiggroup.h>
47 #include <kbuildsycocaprogressdialog.h>
48 #include <kservicetypetrader.h>
49 #include <kservice.h>
50 #include <sonnet/backgroundchecker.h>
51 #include <sonnet/dialog.h>
52 
53 #include <QAbstractItemView>
54 #include <QAbstractTextDocumentLayout>
55 #include <QDialog>
56 #include <QDialogButtonBox>
57 #include <QDir>
58 #include <QStyle>
59 #include <QStyleOptionButton>
60 #include <QLabel>
61 #include <QStyleOptionFrame>
62 #include <QStandardItemModel>
63 
64 #include <misc/helper.h>
65 #include <xml/dom2_eventsimpl.h>
66 #include <html/html_formimpl.h>
67 #include <html/html_miscimpl.h>
68 
69 #include <assert.h>
70 
71 #include <khtmlview.h>
72 #include <khtml_ext.h>
73 #include <xml/dom_docimpl.h>
74 
75 #include <QMenu>
76 #include <QBitmap>
77 #include <QHBoxLayout>
78 #include <QVBoxLayout>
79 
80 using namespace khtml;
81 using namespace DOM;
82 
83 // ----------------- proxy style used to apply some CSS properties to native Qt widgets -----------------
84 
85 struct KHTMLProxyStyle : public QProxyStyle {
KHTMLProxyStyleKHTMLProxyStyle86     KHTMLProxyStyle(QStyle *parent)
87         : QProxyStyle(parent)
88     {
89         noBorder = false;
90         left = right = top = bottom = 0;
91         clearButtonOverlay = 0;
92     }
93 
subElementRectKHTMLProxyStyle94     QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const override
95     {
96         QRect r = QProxyStyle::subElementRect(element, option, widget);
97         switch (element) {
98         case QStyle::SE_PushButtonContents:
99         case QStyle::SE_LineEditContents:
100         case QStyle::SE_ShapedFrameContents:
101             r.adjust(left, top, -qMax(0, right - clearButtonOverlay), -bottom);
102         default:
103             break;
104         }
105         return r;
106     }
107 
drawControlKHTMLProxyStyle108     void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const override
109     {
110         if (element == QStyle::CE_ComboBoxLabel) {
111             const QStyleOptionComboBox *o = qstyleoption_cast<const QStyleOptionComboBox *>(option);
112             if (o) {
113                 QStyleOptionComboBox comboOpt = *o;
114                 comboOpt.currentText = comboOpt.currentText.trimmed();
115                 // by default combobox label is drawn left justified, vertical centered
116                 // translate it to reflect padding values
117                 comboOpt.rect.translate(left, (top - bottom) / 2);
118                 if (noBorder) {
119                     // Need to expand a bit for some styles
120                     comboOpt.rect.adjust(-1, -2, 1, 2);
121                     comboOpt.state &= ~State_On;
122                 }
123                 return QProxyStyle::drawControl(element, &comboOpt, painter, widget);
124             }
125         }
126 
127         QProxyStyle::drawControl(element, option, painter, widget);
128     }
129 
drawComplexControlKHTMLProxyStyle130     void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *painter, const QWidget *widget) const override
131     {
132         if ((cc == QStyle::CC_ComboBox) && noBorder) {
133             if (const QStyleOptionComboBox *cbOpt = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
134                 bool enabled = (cbOpt->state & State_Enabled);
135                 QColor color = cbOpt->palette.color(QPalette::ButtonText);
136                 painter->save();
137                 painter->setBackgroundMode(Qt::TransparentMode);
138                 painter->setPen(color);
139                 painter->setRenderHint(QPainter::Antialiasing);
140                 // Drop down indicator
141                 QRect arrowRect = QProxyStyle::subControlRect(cc, opt, SC_ComboBoxArrow, widget);
142                 arrowRect.setTop(cbOpt->rect.top());
143                 arrowRect.setBottom(cbOpt->rect.bottom());
144                 arrowRect.setRight(cbOpt->rect.right() - 1);
145                 if (enabled && (cbOpt->state & State_On)) {
146                     arrowRect.translate(1, 1);    // push effect
147                 }
148                 //if (!enabled) color = color.lighter();
149                 painter->setBrush(enabled ? QBrush(color, Qt::SolidPattern) : Qt::NoBrush);
150                 QPolygon cbArrowDown;
151                 cbArrowDown.setPoints(6,  3, -2, 4, -2, 0, 2, -4, -2, -3, -2, 0, 1);
152                 cbArrowDown.translate((arrowRect.x() + (arrowRect.width() >> 1)), (arrowRect.y() + (arrowRect.height() >> 1)));
153                 painter->drawPolygon(cbArrowDown);
154                 // Focus rect (from qcleanlooksstyle)
155                 if (enabled && (cbOpt->state & State_HasFocus)) {
156                     QRect focusRect = QProxyStyle::subElementRect(SE_ComboBoxFocusRect, cbOpt, widget);
157                     focusRect.adjust(0, -2, 0, 2);
158                     painter->setBrush(QBrush(color, Qt::Dense4Pattern));
159                     painter->setBrushOrigin(focusRect.topLeft());
160                     painter->setPen(Qt::NoPen);
161                     const QRect rects[4] = {
162                         QRect(focusRect.left(), focusRect.top(), focusRect.width(), 1),    // Top
163                         QRect(focusRect.left(), focusRect.bottom(), focusRect.width(), 1), // Bottom
164                         QRect(focusRect.left(), focusRect.top(), 1, focusRect.height()),   // Left
165                         QRect(focusRect.right(), focusRect.top(), 1, focusRect.height())   // Right
166                     };
167                     painter->drawRects(rects, 4);
168                 }
169                 painter->restore();
170 
171                 return;
172             }
173         }
174 
175         QProxyStyle::drawComplexControl(cc, opt, painter, widget);
176     }
177 
subControlRectKHTMLProxyStyle178     QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget) const override
179     {
180         // Make sure we give combo popup's enough room to display contents;
181         // Qt doesn't do this by default
182 
183         if (cc == QStyle::CC_ComboBox && sc == SC_ComboBoxListBoxPopup) {
184             const QComboBox *cb = qobject_cast<const QComboBox *>(widget);
185             const QStyleOptionComboBox *cbOpt = qstyleoption_cast<const QStyleOptionComboBox *>(opt);
186 
187             if (cb && cbOpt) {
188                 QFontMetrics fm = cb->fontMetrics();
189                 // Compute content width; Qt uses the usual +4 magic number for icon/text margin
190                 int maxW = 0;
191                 for (int c = 0; c < cb->count(); ++c) {
192                     int iw = fm.width(cb->itemText(c));
193                     if (!cb->itemIcon(c).isNull()) {
194                         iw += 4 + cb->iconSize().width();
195                     }
196                     maxW = qMax(maxW, iw);
197                 }
198 
199                 // Now let sizeFromContent add in extra stuff.
200                 maxW = QProxyStyle::sizeFromContents(QStyle::CT_ComboBox, opt, QSize(maxW, 1), widget).width();
201 
202                 // How much more room do we need for the text?
203                 int extraW = maxW > cbOpt->rect.width() ? maxW - cbOpt->rect.width() : 0;
204 
205                 QRect r = QProxyStyle::subControlRect(cc, opt, sc, widget);
206                 r.setWidth(r.width() + extraW);
207                 return r;
208             }
209         }
210 
211         return QProxyStyle::subControlRect(cc, opt, sc, widget);
212     }
213 
214     int left, right, top, bottom;
215     int clearButtonOverlay;
216     bool noBorder;
217 };
218 
219 // ---------------------------------------------------------------------
220 
RenderFormElement(HTMLGenericFormElementImpl * element)221 RenderFormElement::RenderFormElement(HTMLGenericFormElementImpl *element)
222     : RenderWidget(element)
223 //     , m_state(0)
224     , m_proxyStyle(nullptr)
225     , m_exposeInternalPadding(false)
226     , m_isOxygenStyle(false)
227 {
228     // init RenderObject attributes
229     setInline(true);   // our object is Inline
230 }
231 
~RenderFormElement()232 RenderFormElement::~RenderFormElement()
233 {}
234 
setStyle(RenderStyle * _style)235 void RenderFormElement::setStyle(RenderStyle *_style)
236 {
237     RenderWidget::setStyle(_style);
238     setPadding();
239     if (!shouldDisableNativeBorders()) {
240         // When the widget shows native border, clipping background to border
241         // results in a nasty rendering effects
242         if (style()->backgroundLayers()->backgroundClip() == BGBORDER) {
243             style()->accessBackgroundLayers()->setBackgroundClip(BGPADDING);
244         }
245         m_isOxygenStyle = QApplication::style()->objectName().contains("oxygen");
246     }
247 }
248 
paddingTop() const249 int RenderFormElement::paddingTop() const
250 {
251     return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingTop() : 0;
252 }
paddingBottom() const253 int RenderFormElement::paddingBottom() const
254 {
255     return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingBottom() : 0;
256 }
paddingLeft() const257 int RenderFormElement::paddingLeft() const
258 {
259     return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingLeft() : 0;
260 }
paddingRight() const261 int RenderFormElement::paddingRight() const
262 {
263     return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingRight() : 0;
264 }
265 
includesPadding() const266 bool RenderFormElement::includesPadding() const
267 {
268     return true;
269 }
270 
setPadding()271 void RenderFormElement::setPadding()
272 {
273     if (!includesPadding()) {
274         return;
275     }
276 
277     KHTMLProxyStyle *style = static_cast<KHTMLProxyStyle *>(getProxyStyle());
278     style->left = RenderWidget::paddingLeft();
279     style->right = RenderWidget::paddingRight();
280     style->top = RenderWidget::paddingTop();
281     style->bottom = RenderWidget::paddingBottom();
282 }
283 
getProxyStyle()284 QProxyStyle *RenderFormElement::getProxyStyle()
285 {
286     assert(widget());
287     if (m_proxyStyle) {
288         return m_proxyStyle;
289     }
290     m_proxyStyle = new KHTMLProxyStyle(widget()->style());
291     widget()->setStyle(m_proxyStyle);
292     return m_proxyStyle;
293 }
294 
baselinePosition(bool f) const295 short RenderFormElement::baselinePosition(bool f) const
296 {
297     return RenderWidget::baselinePosition(f) - 2 - style()->fontMetrics().descent();
298 }
299 
setQWidget(QWidget * w)300 void RenderFormElement::setQWidget(QWidget *w)
301 {
302     // Avoid dangling proxy pointer when we switch widgets.
303     // the widget will cleanup the proxy, as it is its kid.
304     m_proxyStyle = nullptr;
305 
306     // sets the Qt Object Name for the purposes
307     // of setPadding() -- this is because QStyleSheet
308     // will propagate children of 'w' even if they are toplevel, like
309     // the "find" dialog or the popup menu
310     w->setObjectName("RenderFormElementWidget");
311     RenderWidget::setQWidget(w);
312 }
313 
updateFromElement()314 void RenderFormElement::updateFromElement()
315 {
316     m_widget->setEnabled(!element()->disabled());
317 
318     // If we've disabled a focused element, clear its focus,
319     // so Qt doesn't do funny stuff like let one type into a disabled
320     // line edit.
321     if (element()->disabled() && element()->focused()) {
322         document()->quietResetFocus();
323     }
324 
325     RenderWidget::updateFromElement();
326 }
327 
328 // Some form widgets apply the padding internally (i.e. as if they were
329 // some kind of inline-block). Thus we only want to expose that padding
330 // while layouting (so that width/height calculations are correct), and
331 // then pretend it does not exist, as it is beyond the replaced edge and
332 // thus should not affect other calculations.
333 
calcMinMaxWidth()334 void RenderFormElement::calcMinMaxWidth()
335 {
336     m_exposeInternalPadding = true;
337     RenderWidget::calcMinMaxWidth();
338     m_exposeInternalPadding = false;
339 }
340 
calcWidth()341 void RenderFormElement::calcWidth()
342 {
343     m_exposeInternalPadding = true;
344     RenderWidget::calcWidth();
345     m_exposeInternalPadding = false;
346 }
347 
calcHeight()348 void RenderFormElement::calcHeight()
349 {
350     m_exposeInternalPadding = true;
351     RenderWidget::calcHeight();
352     m_exposeInternalPadding = false;
353 }
354 
layout()355 void RenderFormElement::layout()
356 {
357     KHTMLAssert(needsLayout());
358     KHTMLAssert(minMaxKnown());
359 
360     // minimum height
361     m_height = 0;
362     calcWidth();
363     calcHeight();
364 
365     if (m_widget)
366         resizeWidget(m_width - borderLeft() - borderRight() - paddingLeft() - paddingRight(),
367                      m_height - borderTop() - borderBottom() - paddingTop() - paddingBottom());
368 
369     setNeedsLayout(false);
370 }
371 
calcContentWidth(int w) const372 int RenderFormElement::calcContentWidth(int w) const
373 {
374     if (!shouldDisableNativeBorders()) {
375         if (style()->boxSizing() == CONTENT_BOX) {
376             int nativeBorderWidth = m_widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, m_widget);
377             return RenderBox::calcContentWidth(w) + 2 * nativeBorderWidth;
378         }
379     }
380 
381     return RenderBox::calcContentWidth(w);
382 }
383 
calcContentHeight(int h) const384 int RenderFormElement::calcContentHeight(int h) const
385 {
386     if (!shouldDisableNativeBorders()) {
387         if (style()->boxSizing() == CONTENT_BOX) {
388             int nativeBorderWidth = m_widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, m_widget);
389             return RenderBox::calcContentHeight(h) + 2 * nativeBorderWidth;
390         }
391     }
392 
393     return RenderBox::calcContentHeight(h);
394 }
395 
paintOneBackground(QPainter * p,const QColor & c,const BackgroundLayer * bgLayer,QRect clipr,int _tx,int _ty,int w,int height)396 void RenderFormElement::paintOneBackground(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer, QRect clipr, int _tx, int _ty, int w, int height)
397 {
398     int fudge = 0;
399     if (!shouldDisableNativeBorders()) {
400         fudge = m_isOxygenStyle ? 3 : 1;
401     }
402 
403     paintBackgroundExtended(p, c, bgLayer, clipr, _tx, _ty, w, height,
404                             fudge ? fudge : borderLeft(), fudge ? fudge : borderRight(), RenderWidget::paddingLeft(), RenderWidget::paddingRight(),
405                             fudge ? fudge : borderTop(), fudge ? fudge : borderBottom(), RenderWidget::paddingTop(), RenderWidget::paddingBottom());
406 }
407 
textAlignment() const408 Qt::Alignment RenderFormElement::textAlignment() const
409 {
410     switch (style()->textAlign()) {
411     case LEFT:
412     case KHTML_LEFT:
413         return Qt::AlignLeft;
414     case RIGHT:
415     case KHTML_RIGHT:
416         return Qt::AlignRight;
417     case CENTER:
418     case KHTML_CENTER:
419         return Qt::AlignHCenter;
420     case JUSTIFY:
421     // Just fall into the auto code for justify.
422     case TAAUTO:
423         return style()->direction() == RTL ? Qt::AlignRight : Qt::AlignLeft;
424     }
425     assert(false); // Should never be reached.
426     return Qt::AlignLeft;
427 }
428 
429 // -------------------------------------------------------------------------
430 
RenderButton(HTMLGenericFormElementImpl * element)431 RenderButton::RenderButton(HTMLGenericFormElementImpl *element)
432     : RenderFormElement(element)
433 {
434     m_hasTextIndentHack = false;
435 }
436 
baselinePosition(bool f) const437 short RenderButton::baselinePosition(bool f) const
438 {
439     int ret = (height() - RenderWidget::paddingTop() - RenderWidget::paddingBottom() + 1) / 2;
440     ret += marginTop() + RenderWidget::paddingTop();
441     ret += ((fontMetrics(f).ascent()) / 2) - 1;
442     return ret;
443 }
444 
layout()445 void RenderButton::layout()
446 {
447     RenderFormElement::layout();
448     bool needsTextIndentHack = false;
449     if (!style()->width().isAuto()) {
450         // check if we need to simulate the effect of a popular
451         // button text hiding 'trick' that makes use of negative text-indent,
452         // which we do not support on form widgets.
453         int ti = style()->textIndent().minWidth(containingBlockWidth());
454         if (m_widget->width() <= qAbs(ti)) {
455             needsTextIndentHack = true;
456         }
457     }
458     if (m_hasTextIndentHack != needsTextIndentHack) {
459         m_hasTextIndentHack = needsTextIndentHack;
460         updateFromElement();
461     }
462 }
463 
setStyle(RenderStyle * style)464 void RenderButton::setStyle(RenderStyle *style)
465 {
466     RenderFormElement::setStyle(style);
467     if (shouldDisableNativeBorders()) {
468         // we paint the borders ourselves on this button,
469         // remove the widget's native ones.
470         KHTMLProxyStyle *style = static_cast<KHTMLProxyStyle *>(getProxyStyle());
471         style->noBorder = true;
472     }
473 }
474 
475 // -------------------------------------------------------------------------------
476 
RenderCheckBox(HTMLInputElementImpl * element)477 RenderCheckBox::RenderCheckBox(HTMLInputElementImpl *element)
478     : RenderButton(element)
479 {
480     CheckBoxWidget *b = new CheckBoxWidget(view()->widget());
481     //b->setAutoMask(true);
482     b->setMouseTracking(true);
483     setQWidget(b);
484 
485     // prevent firing toggled() signals on initialization
486     b->setChecked(element->checked());
487 
488     connect(b, SIGNAL(stateChanged(int)), this, SLOT(slotStateChanged(int)));
489     m_ignoreStateChanged = false;
490 }
491 
calcMinMaxWidth()492 void RenderCheckBox::calcMinMaxWidth()
493 {
494     KHTMLAssert(!minMaxKnown());
495 
496     QCheckBox *cb = static_cast<QCheckBox *>(m_widget);
497     QSize s(qMin(22, qMax(14, cb->style()->pixelMetric(QStyle::PM_IndicatorWidth))),
498             qMin(22, qMax(12, cb->style()->pixelMetric(QStyle::PM_IndicatorHeight))));
499     setIntrinsicWidth(s.width());
500     setIntrinsicHeight(s.height());
501 
502     RenderButton::calcMinMaxWidth();
503 }
504 
updateFromElement()505 void RenderCheckBox::updateFromElement()
506 {
507     if (widget()->isChecked() != element()->checked()) {
508         m_ignoreStateChanged = true;
509         widget()->setChecked(element()->checked());
510         m_ignoreStateChanged = false;
511     }
512 
513     RenderButton::updateFromElement();
514 }
515 
slotStateChanged(int state)516 void RenderCheckBox::slotStateChanged(int state)
517 {
518     if (m_ignoreStateChanged) {
519         return;
520     }
521     element()->setChecked(state == Qt::Checked);
522 }
523 
handleEvent(const DOM::EventImpl & ev)524 bool RenderCheckBox::handleEvent(const DOM::EventImpl &ev)
525 {
526     switch (ev.id()) {
527     case EventImpl::DOMFOCUSIN_EVENT:
528     case EventImpl::DOMFOCUSOUT_EVENT:
529     case EventImpl::MOUSEMOVE_EVENT:
530     case EventImpl::MOUSEOUT_EVENT:
531     case EventImpl::MOUSEOVER_EVENT:
532         return RenderButton::handleEvent(ev);
533     default:
534         break;
535     }
536     return false;
537 }
538 
539 // -------------------------------------------------------------------------------
540 
RenderRadioButton(HTMLInputElementImpl * element)541 RenderRadioButton::RenderRadioButton(HTMLInputElementImpl *element)
542     : RenderButton(element)
543 {
544     RadioButtonWidget *b = new RadioButtonWidget(view()->widget());
545     b->setMouseTracking(true);
546     b->setAutoExclusive(false);
547     setQWidget(b);
548 
549     // prevent firing toggled() signals on initialization
550     b->setChecked(element->checked());
551 
552     connect(b, SIGNAL(toggled(bool)), this, SLOT(slotToggled(bool)));
553     m_ignoreToggled = false;
554 }
555 
updateFromElement()556 void RenderRadioButton::updateFromElement()
557 {
558     m_ignoreToggled = true;
559     widget()->setChecked(element()->checked());
560     m_ignoreToggled = false;
561 
562     RenderButton::updateFromElement();
563 }
564 
calcMinMaxWidth()565 void RenderRadioButton::calcMinMaxWidth()
566 {
567     KHTMLAssert(!minMaxKnown());
568 
569     QRadioButton *rb = static_cast<QRadioButton *>(m_widget);
570     QSize s(qMin(22, qMax(14, rb->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth))),
571             qMin(20, qMax(12, rb->style()->pixelMetric(QStyle::PM_ExclusiveIndicatorHeight))));
572     setIntrinsicWidth(s.width());
573     setIntrinsicHeight(s.height());
574 
575     RenderButton::calcMinMaxWidth();
576 }
577 
slotToggled(bool)578 void RenderRadioButton::slotToggled(bool /*activated*/)
579 {
580     if (m_ignoreToggled) {
581         return;
582     }
583 }
584 
handleEvent(const DOM::EventImpl & ev)585 bool RenderRadioButton::handleEvent(const DOM::EventImpl &ev)
586 {
587     switch (ev.id()) {
588     case EventImpl::DOMFOCUSIN_EVENT:
589     case EventImpl::DOMFOCUSOUT_EVENT:
590     case EventImpl::MOUSEMOVE_EVENT:
591     case EventImpl::MOUSEOUT_EVENT:
592     case EventImpl::MOUSEOVER_EVENT:
593         return RenderButton::handleEvent(ev);
594     default:
595         break;
596     }
597     return false;
598 }
599 
600 // -------------------------------------------------------------------------------
601 
602 const QLatin1String sBorderNoneSheet("QPushButton{border:none}");
603 
RenderSubmitButton(HTMLInputElementImpl * element)604 RenderSubmitButton::RenderSubmitButton(HTMLInputElementImpl *element)
605     : RenderButton(element)
606 {
607     PushButtonWidget *p = new PushButtonWidget(view()->widget());
608     setQWidget(p);
609     //p->setAutoMask(true);
610     p->setMouseTracking(true);
611     p->setDefault(false);
612     p->setAutoDefault(false);
613 }
614 
setStyleSheet_helper(const QString & s,QWidget * w)615 static inline void setStyleSheet_helper(const QString &s, QWidget *w)
616 {
617     // ### buggy Qt stylesheets mess with the widget palette.
618     // force it again after any stylesheet update.
619     QPalette pal = w->palette();
620     w->setStyleSheet(s);
621     w->setPalette(pal);
622 }
623 
setPadding()624 void RenderSubmitButton::setPadding()
625 {
626     // Proxy styling doesn't work well enough for buttons.
627     // Use stylesheets instead. tests/css/button-padding-top.html
628     assert(!m_proxyStyle);
629 
630     if (!includesPadding()) {
631         return;
632     }
633 
634     if (!RenderWidget::paddingLeft() && !RenderWidget::paddingRight() &&
635             !RenderWidget::paddingTop() && !RenderWidget::paddingBottom()) {
636         setStyleSheet_helper((shouldDisableNativeBorders() ? sBorderNoneSheet : QString()), widget());
637         return;
638     }
639 
640     setStyleSheet_helper(
641         QString("QPushButton{padding-left:%1px; padding-right:%2px; padding-top:%3px; padding-bottom:%4px}")
642         .arg(RenderWidget::paddingLeft())
643         .arg(RenderWidget::paddingRight())
644         .arg(RenderWidget::paddingTop())
645         .arg(RenderWidget::paddingBottom()) + (shouldDisableNativeBorders() ? sBorderNoneSheet : QString())
646         , widget());
647 }
648 
setStyle(RenderStyle * style)649 void RenderSubmitButton::setStyle(RenderStyle *style)
650 {
651     // Proxy styling doesn't work well enough for buttons.
652     // Use stylesheets instead. tests/css/button-padding-top.html
653     assert(!m_proxyStyle);
654     RenderFormElement::setStyle(style);
655 
656     QString s = widget()->styleSheet();
657     if (shouldDisableNativeBorders()) {
658         // we paint the borders ourselves on this button,
659         // remove the widget's native ones.
660         if (!s.contains(sBorderNoneSheet)) {
661             s.append(sBorderNoneSheet);
662             setStyleSheet_helper(s, widget());
663         }
664     } else {
665         setStyleSheet_helper(s.remove(sBorderNoneSheet), widget());
666     }
667 }
668 
rawText()669 QString RenderSubmitButton::rawText()
670 {
671     QString value = element()->valueWithDefault().string();
672     value = value.trimmed();
673     QString raw;
674     for (int i = 0; i < value.length(); i++) {
675         raw += value[i];
676         if (value[i] == '&') {
677             raw += '&';
678         }
679     }
680     return raw;
681 }
682 
canHaveBorder() const683 bool RenderSubmitButton::canHaveBorder() const
684 {
685     // ### TODO would be nice to be able to
686     // return style()->hasBackgroundImage() here,
687     // depending on a config option (e.g. 'favour usability/integration over aspect')
688     // so that only buttons with both a custom border
689     // and a background image are drawn without native styling.
690     //
691     // This would go in the same place, gui wise, as a choice of b/w default color scheme,
692     // versus native color scheme.
693 
694     return true;
695 }
696 
calcMinMaxWidth()697 void RenderSubmitButton::calcMinMaxWidth()
698 {
699     KHTMLAssert(!minMaxKnown());
700 
701     QString raw = rawText();
702     QPushButton *pb = static_cast<QPushButton *>(m_widget);
703     pb->setText(raw);
704     pb->setFont(style()->font());
705 
706     bool empty = raw.isEmpty();
707     if (empty) {
708         raw = QLatin1Char('X');
709     }
710     QFontMetrics fm = pb->fontMetrics();
711     QSize ts = fm.size(Qt::TextShowMnemonic, raw);
712     //Oh boy.
713     QStyleOptionButton butOpt;
714     butOpt.init(pb);
715     butOpt.text = raw;
716     QSize s = pb->style()->sizeFromContents(QStyle::CT_PushButton, &butOpt, ts, pb);
717 
718     s = s.expandedTo(QApplication::globalStrut());
719     int margin = pb->style()->pixelMetric(QStyle::PM_ButtonMargin) +
720                  pb->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2;
721 
722     int w = ts.width() + margin;
723     int h = s.height();
724 
725     assert(includesPadding());
726     int hpadding = RenderWidget::paddingLeft() + RenderWidget::paddingRight();
727     int vpadding = RenderWidget::paddingTop() + RenderWidget::paddingBottom();
728 
729     // add 30% margins to the width (heuristics to make it look similar to IE)
730     // ### FIXME BASELINE: we could drop this emulation and adopt Mozilla style buttons
731     // (+/- padding: 0px 8px 0px 8px) - IE is most often in a separate css
732     // code path nowadays, so we have wider buttons than other engines.
733     int toAdd = (w * 13 / 10) - w - hpadding;
734     toAdd = qMax(0, toAdd);
735     w += toAdd;
736 
737     if (shouldDisableNativeBorders()) {
738         // we paint the borders ourselves, so let's override our height to something saner
739         h = ts.height();
740     } else {
741         h -= vpadding;
742     }
743     s = QSize(w, h).expandedTo(QApplication::globalStrut());
744 
745     setIntrinsicWidth(s.width());
746     setIntrinsicHeight(s.height());
747 
748     RenderButton::calcMinMaxWidth();
749 }
750 
updateFromElement()751 void RenderSubmitButton::updateFromElement()
752 {
753     QString oldText = static_cast<QPushButton *>(m_widget)->text();
754     QString newText = rawText();
755     static_cast<QPushButton *>(m_widget)->setText(newText);
756     if (oldText != newText) {
757         setNeedsLayoutAndMinMaxRecalc();
758     }
759     RenderFormElement::updateFromElement();
760 }
761 
baselinePosition(bool f) const762 short RenderSubmitButton::baselinePosition(bool f) const
763 {
764     int ret = (height() - RenderWidget::paddingTop() - RenderWidget::paddingBottom() + 1) / 2;
765     ret += marginTop() + RenderWidget::paddingTop();
766     ret += ((fontMetrics(f).ascent()) / 2) - 2;
767     return ret;
768 }
769 
770 // -------------------------------------------------------------------------------
771 
RenderResetButton(HTMLInputElementImpl * element)772 RenderResetButton::RenderResetButton(HTMLInputElementImpl *element)
773     : RenderSubmitButton(element)
774 {
775 }
776 
777 // -------------------------------------------------------------------------------
778 
779 namespace khtml
780 {
781 
782 class CompletionWidget: public KCompletionBox
783 {
784 public:
CompletionWidget(QWidget * parent=nullptr)785     CompletionWidget(QWidget *parent = nullptr) : KCompletionBox(parent) {}
globalPositionHint() const786     QPoint globalPositionHint() const override
787     {
788         QWidget *pw = parentWidget();
789         KHTMLWidget *kwp = dynamic_cast<KHTMLWidget *>(pw);
790         if (!kwp) {
791             qCDebug(KHTML_LOG) << "CompletionWidget has no KHTMLWidget parent";
792             return KCompletionBox::globalPositionHint();
793         }
794         QPoint dest;
795         KHTMLView *v = kwp->m_kwp->rootViewPos(dest);
796         QPoint ret;
797         if (v) {
798             ret = v->mapToGlobal(dest + QPoint(0, pw->height()));
799             int zoomLevel = v->zoomLevel();
800             if (zoomLevel != 100) {
801                 ret.setX(ret.x()*zoomLevel / 100);
802                 ret.setY(ret.y()*zoomLevel / 100);
803             }
804         }
805         return ret;
806     }
807 };
808 
809 }
810 
LineEditWidget(DOM::HTMLInputElementImpl * input,KHTMLView * view,QWidget * parent)811 LineEditWidget::LineEditWidget(DOM::HTMLInputElementImpl *input, KHTMLView *view, QWidget *parent)
812     : KLineEdit(parent), m_input(input), m_view(view)
813 {
814     m_kwp->setIsRedirected(true);
815     setMouseTracking(true);
816     KActionCollection *ac = new KActionCollection(this);
817     m_spellAction = KStandardAction::spelling(this, SLOT(slotCheckSpelling()), ac);
818 
819     setCompletionBox(new CompletionWidget(this));
820     completionBox()->setObjectName("completion box");
821     completionBox()->setFont(font());
822 }
823 
~LineEditWidget()824 LineEditWidget::~LineEditWidget()
825 {
826 }
827 
slotCheckSpelling()828 void LineEditWidget::slotCheckSpelling()
829 {
830     if (text().isEmpty()) {
831         return;
832     }
833     Sonnet::Dialog *spellDialog = new Sonnet::Dialog(new Sonnet::BackgroundChecker(this), nullptr);
834     connect(spellDialog, SIGNAL(replace(QString,int,QString)), this, SLOT(spellCheckerCorrected(QString,int,QString)));
835     connect(spellDialog, SIGNAL(misspelling(QString,int)), this, SLOT(spellCheckerMisspelling(QString,int)));
836     connect(spellDialog, SIGNAL(done(QString)), this, SLOT(slotSpellCheckDone(QString)));
837     connect(spellDialog, SIGNAL(cancel()), this, SLOT(spellCheckerFinished()));
838     connect(spellDialog, SIGNAL(stop()), this, SLOT(spellCheckerFinished()));
839     spellDialog->setBuffer(text());
840     spellDialog->show();
841 }
842 
spellCheckerMisspelling(const QString & _text,int pos)843 void LineEditWidget::spellCheckerMisspelling(const QString &_text, int pos)
844 {
845     highLightWord(_text.length(), pos);
846 }
847 
setFocus()848 void LineEditWidget::setFocus()
849 {
850     KLineEdit::setFocus();
851     end(false);
852 }
853 
highLightWord(unsigned int length,unsigned int pos)854 void LineEditWidget::highLightWord(unsigned int length, unsigned int pos)
855 {
856     setSelection(pos, length);
857 }
858 
spellCheckerCorrected(const QString & old,int pos,const QString & corr)859 void LineEditWidget::spellCheckerCorrected(const QString &old, int pos, const QString &corr)
860 {
861     if (old != corr) {
862         setSelection(pos, old.length());
863         insert(corr);
864         setSelection(pos, corr.length());
865     }
866 }
867 
spellCheckerFinished()868 void LineEditWidget::spellCheckerFinished()
869 {
870 }
871 
slotSpellCheckDone(const QString & s)872 void LineEditWidget::slotSpellCheckDone(const QString &s)
873 {
874     if (s != text()) {
875         setText(s);
876     }
877 }
878 
879 namespace khtml
880 {
881 
882 /**
883   * @internal
884   */
885 class WebShortcutCreator
886 {
887 public:
888     /**
889       * @short Creates a Web Shourtcut without using kdebase SearchProvider class.
890       *        It is used by LineEditWidget.
891       */
892     static bool createWebShortcut(QString query);
893 
894 private:
895     static bool askData(QString &name, QString &keys);
896     static void createFile(QString query, QString name, QString keys);
897 };
898 
createWebShortcut(QString query)899 bool WebShortcutCreator::createWebShortcut(QString query)
900 {
901     QString name = i18n("New Web Shortcut");
902     QString keys;
903     if (askData(name, keys)) {
904         bool isOk;
905         do { //It's going to be checked if the keys have already been assigned
906             isOk = true;
907             QStringList keyList(keys.split(','));
908             KService::List providers = KServiceTypeTrader::self()->query("SearchProvider");
909             foreach (const KService::Ptr &provider, providers) {
910                 if (!isOk) {
911                     break;
912                 }
913                 foreach (const QString &s, provider->property("Keys").toStringList()) {
914                     if (!isOk) {
915                         break;
916                     }
917                     foreach (const QString &t, keys) {
918                         if (!isOk) {
919                             break;
920                         }
921                         if (s == t) {
922                             KMessageBox::sorry(nullptr, i18n("%1 is already assigned to %2", s, provider->name()), i18n("Error"));
923                             isOk = false;
924                         }
925                     }
926                 }
927             }
928             if (!isOk && !askData(name, keys)) {
929                 return false;
930             }
931         } while (!isOk);
932         createFile(query, name, keys);
933         return true;
934     } else {
935         return false;
936     }
937 }
938 
createFile(QString query,QString name,QString keys)939 void WebShortcutCreator::createFile(QString query, QString name, QString keys)
940 {
941     // SearchProvider class is part of kdebase, so the file is written as
942     // an standard desktop file.
943     QString fileName(keys);
944     QString dir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/kservices5/searchproviders";
945     QDir().mkpath(dir);
946     while (QFile::exists(dir + fileName + ".desktop")) {
947         fileName += '_';
948     }
949     KDesktopFile f(dir + fileName + ".desktop");
950     f.desktopGroup().writeEntry("Keys", keys);
951     f.desktopGroup().writeEntry("Type", "Service");
952     f.desktopGroup().writeEntry("ServiceTypes", "SearchProvider");
953     f.desktopGroup().writeEntry("Name", name);
954     f.desktopGroup().writeEntry("Query", query);
955     f.sync();
956     KBuildSycocaProgressDialog::rebuildKSycoca(nullptr);
957 }
958 
askData(QString & name,QString & keys)959 bool WebShortcutCreator::askData(QString &name, QString &keys)
960 {
961     QDialog *dialog = new QDialog();
962     dialog->setWindowTitle(name);
963     QVBoxLayout *mainLayout = new QVBoxLayout();
964     dialog->setLayout(mainLayout);
965 
966     QHBoxLayout *layout = new QHBoxLayout();
967     mainLayout->addLayout(layout);
968     QLabel *label = new QLabel(i18n("Search &provider name:"), dialog);
969     layout->addWidget(label);
970     QLineEdit *nameEdit = new QLineEdit(i18n("New search provider"), dialog);
971     label->setBuddy(nameEdit);
972     layout->addWidget(nameEdit);
973     layout = new QHBoxLayout();
974     mainLayout->addLayout(layout);
975     label = new QLabel(i18n("UR&I shortcuts:"), dialog);
976     layout->addWidget(label);
977     QLineEdit *keysEdit = new QLineEdit(dialog);
978     label->setBuddy(keysEdit);
979     layout->addWidget(keysEdit);
980 
981     QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
982     buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
983     QObject::connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
984     QObject::connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
985     mainLayout->addWidget(buttonBox);
986 
987     bool res = dialog->exec();
988     if (res) {
989         name = nameEdit->text();
990         keys = keysEdit->text();
991     }
992     delete dialog;
993     return res;
994 }
995 
996 }
997 
slotCreateWebShortcut()998 void LineEditWidget::slotCreateWebShortcut()
999 {
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 
contextMenuEvent(QContextMenuEvent * e)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 
clearHistoryActivated()1080 void LineEditWidget::clearHistoryActivated()
1081 {
1082     m_view->clearCompletionHistory(m_input->name().string());
1083     if (compObj()) {
1084         compObj()->clear();
1085     }
1086 }
1087 
event(QEvent * e)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 
mouseMoveEvent(QMouseEvent * e)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 
RenderLineEdit(HTMLInputElementImpl * element)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 
baselinePosition(bool f) const1147 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 
setStyle(RenderStyle * _style)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 
highLightWord(unsigned int length,unsigned int pos)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 
slotReturnPressed()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 
handleFocusOut()1217 void RenderLineEdit::handleFocusOut()
1218 {
1219     if (widget() && widget()->isModified()) {
1220         element()->onChange();
1221         widget()->setModified(false);
1222     }
1223 }
1224 
calcMinMaxWidth()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 
updateFromElement()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 
slotTextChanged(const QString & string)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 
select()1289 void RenderLineEdit::select()
1290 {
1291     static_cast<LineEditWidget *>(m_widget)->selectAll();
1292 }
1293 
selectionStart()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 
selectionEnd()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 
setSelectionStart(long pos)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 
setSelectionEnd(long pos)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 
setSelectionRange(long start,long end)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 
RenderFieldset(HTMLGenericFormElementImpl * element)1346 RenderFieldset::RenderFieldset(HTMLGenericFormElementImpl *element)
1347     : RenderBlock(element)
1348 {
1349     m_intrinsicWidth = 0;
1350 }
1351 
calcMinMaxWidth()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 
layoutLegend(bool relayoutChildren)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 
findLegend() const1396 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 
paintBoxDecorations(PaintInfo & pI,int _tx,int _ty)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 
paintBorderMinusLegend(QPainter * p,int _tx,int _ty,int w,int h,const RenderStyle * style,int lx,int lw,int lb)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 
setStyle(RenderStyle * _style)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 
RenderFileButton(HTMLInputElementImpl * element)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 
baselinePosition(bool f) const1548 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 
calcMinMaxWidth()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 
handleFocusOut()1587 void RenderFileButton::handleFocusOut()
1588 {
1589     if (widget()->lineEdit() && widget()->lineEdit()->isModified()) {
1590         element()->onChange();
1591         widget()->lineEdit()->setModified(false);
1592     }
1593 }
1594 
updateFromElement()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 
slotReturnPressed()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 
slotTextChanged(const QString &)1622 void RenderFileButton::slotTextChanged(const QString &/*string*/)
1623 {
1624     element()->m_value = QUrl(widget()->url()).toDisplayString(QUrl::PreferLocalFile);
1625 }
1626 
slotUrlSelected()1627 void RenderFileButton::slotUrlSelected()
1628 {
1629     element()->onChange();
1630 }
1631 
select()1632 void RenderFileButton::select()
1633 {
1634     widget()->lineEdit()->selectAll();
1635 }
1636 
1637 // -------------------------------------------------------------------------
1638 
RenderLabel(HTMLGenericFormElementImpl * element)1639 RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element)
1640     : RenderFormElement(element)
1641 {
1642 
1643 }
1644 
1645 // -------------------------------------------------------------------------
1646 
RenderLegend(HTMLGenericFormElementImpl * element)1647 RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element)
1648     : RenderBlock(element)
1649 {
1650 }
1651 
1652 // -------------------------------------------------------------------------------
1653 
event(QEvent * event)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 
ComboBoxWidget(QWidget * parent)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 
showPopup()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 
hidePopup()1710 void ComboBoxWidget::hidePopup()
1711 {
1712     KComboBox::hidePopup();
1713 }
1714 
event(QEvent * e)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 
eventFilter(QObject * dest,QEvent * e)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 
keyPressEvent(QKeyEvent * e)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 
RenderSelect(HTMLSelectElementImpl * element)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 
clearItemFlags(int index,Qt::ItemFlags flags)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 
setStyle(RenderStyle * _style)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 
updateFromElement()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 
baselinePosition(bool f) const1950 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 
calcMinMaxWidth()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 
layout()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 
slotSelected(int index)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 
slotSelectionChanged()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 
setOptionsChanged(bool _optionsChanged)2163 void RenderSelect::setOptionsChanged(bool _optionsChanged)
2164 {
2165     m_optionsChanged = _optionsChanged;
2166 }
2167 
setPadding()2168 void RenderSelect::setPadding()
2169 {
2170     RenderFormElement::setPadding();
2171 }
2172 
createListBox()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 
createComboBox()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 
updateSelection()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 
TextAreaWidget(int wrap,QWidget * parent)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 
~TextAreaWidget()2246 TextAreaWidget::~TextAreaWidget()
2247 {
2248 }
2249 
scrollContentsBy(int dx,int dy)2250 void TextAreaWidget::scrollContentsBy(int dx, int dy)
2251 {
2252     KTextEdit::scrollContentsBy(dx, dy);
2253     update();
2254 }
2255 
event(QEvent * e)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 
keyPressEvent(QKeyEvent * e)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 
RenderTextArea(HTMLTextAreaElementImpl * element)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 
~RenderTextArea()2316 RenderTextArea::~RenderTextArea()
2317 {
2318     element()->m_value = text();
2319 }
2320 
handleFocusOut()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 
calcMinMaxWidth()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 
setStyle(RenderStyle * _style)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 
scrollWidth() const2411 short RenderTextArea::scrollWidth() const
2412 {
2413     return RenderObject::scrollWidth();
2414 }
2415 
scrollHeight() const2416 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 
setText(const QString & newText)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 
updateFromElement()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 
text()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 
highLightWord(unsigned int length,unsigned int pos)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 
slotTextChanged()2504 void RenderTextArea::slotTextChanged()
2505 {
2506     element()->m_changed    = true;
2507     if (element()->m_value != text()) {
2508         element()->m_unsubmittedFormChange = true;
2509     }
2510 }
2511 
select()2512 void RenderTextArea::select()
2513 {
2514     static_cast<TextAreaWidget *>(m_widget)->selectAll();
2515 }
2516 
selectionStart()2517 long RenderTextArea::selectionStart()
2518 {
2519     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2520     return w->textCursor().selectionStart();
2521 }
2522 
selectionEnd()2523 long RenderTextArea::selectionEnd()
2524 {
2525     TextAreaWidget *w = static_cast<TextAreaWidget *>(m_widget);
2526     return w->textCursor().selectionEnd();
2527 }
2528 
setPhysWrapPos(QTextCursor & otc,bool selStart,int idx)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 
setSelectionStart(long offset)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 
setSelectionEnd(long offset)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 
setSelectionRange(long start,long end)2569 void RenderTextArea::setSelectionRange(long start, long end)
2570 {
2571     setSelectionStart(start);
2572     setSelectionEnd(end);
2573 }
2574 // ---------------------------------------------------------------------------
2575 
2576