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