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