mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-25 03:12:49 +00:00
2519 lines
81 KiB
C++
2519 lines
81 KiB
C++
/*
|
|
* This file is part of the DOM implementation for KDE.
|
|
*
|
|
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
|
|
* (C) 1999 Antti Koivisto (koivisto@kde.org)
|
|
* (C) 2000 Dirk Mueller (mueller@kde.org)
|
|
* (C) 2006 Maksim Orlovich (maksim@kde.org)
|
|
* (C) 2007-2009 Germain Garand (germain@ebooksfrance.org)
|
|
* (C) 2007 Mitz Pettel (mitz@webkit.org)
|
|
* (C) 2007 Charles Samuels (charles@kde.org)
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "render_form.h"
|
|
|
|
#include <kcompletionbox.h>
|
|
#include <kcursor.h>
|
|
#include <kdebug.h>
|
|
#include <kfiledialog.h>
|
|
#include <kfind.h>
|
|
#include <kfinddialog.h>
|
|
#include <kiconloader.h>
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
#include <kreplace.h>
|
|
#include <kreplacedialog.h>
|
|
#include <dialog.h>
|
|
#include <backgroundchecker.h>
|
|
#include <kurlcompletion.h>
|
|
#include <kwindowsystem.h>
|
|
#include <kstandardaction.h>
|
|
#include <kactioncollection.h>
|
|
#include <kdeuiwidgetsproxystyle_p.h>
|
|
#include <kurl.h>
|
|
#include <kdesktopfile.h>
|
|
#include <kconfiggroup.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kdialog.h>
|
|
#include <kbuildsycocaprogressdialog.h>
|
|
#include <kservicetypetrader.h>
|
|
#include <kservice.h>
|
|
|
|
#include <QAbstractItemView>
|
|
#include <QAbstractTextDocumentLayout>
|
|
#include <QtGui/QStyle>
|
|
#include <QtGui/qstyleoption.h>
|
|
#include <QtGui/QLabel>
|
|
#include <QtGui/qstyleoption.h>
|
|
#include <QtGui/QStandardItemModel>
|
|
|
|
#include <misc/helper.h>
|
|
#include <xml/dom2_eventsimpl.h>
|
|
#include <html/html_formimpl.h>
|
|
#include <html/html_miscimpl.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <khtmlview.h>
|
|
#include <khtml_ext.h>
|
|
#include <xml/dom_docimpl.h>
|
|
|
|
#include <QtGui/QMenu>
|
|
#include <QtGui/QBitmap>
|
|
#include <QtGui/QHBoxLayout>
|
|
#include <QtGui/QVBoxLayout>
|
|
|
|
using namespace khtml;
|
|
using namespace DOM;
|
|
|
|
// ----------------- proxy style used to apply some CSS properties to native Qt widgets -----------------
|
|
|
|
struct KHTMLProxyStyle : public KdeUiProxyStyle
|
|
{
|
|
KHTMLProxyStyle(QWidget *parent)
|
|
: KdeUiProxyStyle(parent)
|
|
{
|
|
noBorder = false;
|
|
left = right = top = bottom = 0;
|
|
clearButtonOverlay = 0;
|
|
setParent(parent);
|
|
}
|
|
|
|
QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
|
|
{
|
|
QRect r = style()->subElementRect(element, option, widget);
|
|
switch (element) {
|
|
case QStyle::SE_PushButtonContents:
|
|
case QStyle::SE_LineEditContents:
|
|
case QStyle::SE_ShapedFrameContents:
|
|
r.adjust(left, top, -qMax(0, right - clearButtonOverlay), -bottom);
|
|
default:
|
|
break;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
|
|
{
|
|
if (element == QStyle::CE_ComboBoxLabel) {
|
|
const QStyleOptionComboBox *o = qstyleoption_cast<const QStyleOptionComboBox*>(option);
|
|
if (o) {
|
|
QStyleOptionComboBox comboOpt = *o;
|
|
comboOpt.currentText = comboOpt.currentText.trimmed();
|
|
// by default combobox label is drawn left justified, vertical centered
|
|
// translate it to reflect padding values
|
|
comboOpt.rect.translate(left, (top - bottom) / 2);
|
|
if (noBorder) {
|
|
// Need to expand a bit for some styles
|
|
comboOpt.rect.adjust(-1, -2, 1, 2);
|
|
comboOpt.state &= ~State_On;
|
|
}
|
|
return style()->drawControl(element, &comboOpt, painter, widget);
|
|
}
|
|
}
|
|
|
|
style()->drawControl(element, option, painter, widget);
|
|
}
|
|
|
|
void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *painter, const QWidget *widget) const
|
|
{
|
|
if ((cc == QStyle::CC_ComboBox) && noBorder) {
|
|
if (const QStyleOptionComboBox *cbOpt = qstyleoption_cast<const QStyleOptionComboBox*>(opt)) {
|
|
bool enabled = (cbOpt->state & State_Enabled);
|
|
QColor color = cbOpt->palette.color(QPalette::ButtonText);
|
|
painter->save();
|
|
painter->setBackgroundMode(Qt::TransparentMode);
|
|
painter->setPen(color);
|
|
painter->setRenderHint(QPainter::Antialiasing);
|
|
// Drop down indicator
|
|
QRect arrowRect = style()->subControlRect(cc, opt, SC_ComboBoxArrow, widget);
|
|
arrowRect.setTop(cbOpt->rect.top());
|
|
arrowRect.setBottom(cbOpt->rect.bottom());
|
|
arrowRect.setRight(cbOpt->rect.right() - 1);
|
|
if (enabled && (cbOpt->state & State_On))
|
|
arrowRect.translate(1, 1); // push effect
|
|
//if (!enabled) color = color.lighter();
|
|
painter->setBrush(enabled ? QBrush(color, Qt::SolidPattern) : Qt::NoBrush);
|
|
QPolygon cbArrowDown;
|
|
cbArrowDown.setPoints(6, 3,-2, 4,-2, 0,2, -4,-2, -3,-2, 0,1);
|
|
cbArrowDown.translate((arrowRect.x() + (arrowRect.width() >> 1)), (arrowRect.y() + (arrowRect.height() >> 1)));
|
|
painter->drawPolygon(cbArrowDown);
|
|
// Focus rect (from qcleanlooksstyle)
|
|
if (enabled && (cbOpt->state & State_HasFocus)) {
|
|
QRect focusRect = style()->subElementRect(SE_ComboBoxFocusRect, cbOpt, widget);
|
|
focusRect.adjust(0, -2, 0, 2);
|
|
painter->setBrush(QBrush(color, Qt::Dense4Pattern));
|
|
painter->setBrushOrigin(focusRect.topLeft());
|
|
painter->setPen(Qt::NoPen);
|
|
const QRect rects[4] = {
|
|
QRect(focusRect.left(), focusRect.top(), focusRect.width(), 1), // Top
|
|
QRect(focusRect.left(), focusRect.bottom(), focusRect.width(), 1), // Bottom
|
|
QRect(focusRect.left(), focusRect.top(), 1, focusRect.height()), // Left
|
|
QRect(focusRect.right(), focusRect.top(), 1, focusRect.height()) // Right
|
|
};
|
|
painter->drawRects(rects, 4);
|
|
}
|
|
painter->restore();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
style()->drawComplexControl(cc, opt, painter, widget);
|
|
}
|
|
|
|
QRect subControlRect(ComplexControl cc, const QStyleOptionComplex* opt, SubControl sc, const QWidget* widget) const
|
|
{
|
|
// Make sure we give combo popup's enough room to display contents;
|
|
// Qt doesn't do this by default
|
|
|
|
if (cc == QStyle::CC_ComboBox && sc == SC_ComboBoxListBoxPopup) {
|
|
const QComboBox* cb = qobject_cast<const QComboBox*>(widget);
|
|
const QStyleOptionComboBox* cbOpt = qstyleoption_cast<const QStyleOptionComboBox*>(opt);
|
|
|
|
if (cb && cbOpt) {
|
|
QFontMetrics fm = cb->fontMetrics();
|
|
// Compute content width; Qt uses the usual +4 magic number for icon/text margin
|
|
int maxW = 0;
|
|
for (int c = 0; c < cb->count(); ++c) {
|
|
int iw = fm.width(cb->itemText(c));
|
|
if (!cb->itemIcon(c).isNull())
|
|
iw += 4 + cb->iconSize().width();
|
|
maxW = qMax(maxW, iw);
|
|
}
|
|
|
|
// Now let sizeFromContent add in extra stuff.
|
|
maxW = style()->sizeFromContents(QStyle::CT_ComboBox, opt, QSize(maxW, 1), widget).width();
|
|
|
|
// How much more room do we need for the text?
|
|
int extraW = maxW > cbOpt->rect.width() ? maxW - cbOpt->rect.width() : 0;
|
|
|
|
QRect r = style()->subControlRect(cc, opt, sc, widget);
|
|
r.setWidth(r.width() + extraW);
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return style()->subControlRect(cc, opt, sc, widget);
|
|
}
|
|
|
|
int left, right, top, bottom;
|
|
int clearButtonOverlay;
|
|
bool noBorder;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
RenderFormElement::RenderFormElement(HTMLGenericFormElementImpl *element)
|
|
: RenderWidget(element)
|
|
// , m_state(0)
|
|
, m_proxyStyle(0)
|
|
, m_exposeInternalPadding(false)
|
|
, m_isOxygenStyle(false)
|
|
{
|
|
// init RenderObject attributes
|
|
setInline(true); // our object is Inline
|
|
}
|
|
|
|
RenderFormElement::~RenderFormElement()
|
|
{}
|
|
|
|
void RenderFormElement::setStyle(RenderStyle *_style)
|
|
{
|
|
RenderWidget::setStyle(_style);
|
|
setPadding();
|
|
if (!shouldDisableNativeBorders()) {
|
|
// When the widget shows native border, clipping background to border
|
|
// results in a nasty rendering effects
|
|
if (style()->backgroundLayers()->backgroundClip() == BGBORDER) {
|
|
style()->accessBackgroundLayers()->setBackgroundClip(BGPADDING);
|
|
}
|
|
m_isOxygenStyle = QApplication::style()->objectName().contains("oxygen");
|
|
}
|
|
}
|
|
|
|
int RenderFormElement::paddingTop() const
|
|
{
|
|
return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingTop() : 0;
|
|
}
|
|
int RenderFormElement::paddingBottom() const
|
|
{
|
|
return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingBottom() : 0;
|
|
}
|
|
int RenderFormElement::paddingLeft() const
|
|
{
|
|
return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingLeft() : 0;
|
|
}
|
|
int RenderFormElement::paddingRight() const
|
|
{
|
|
return (!includesPadding() || m_exposeInternalPadding) ? RenderWidget::paddingRight() : 0;
|
|
}
|
|
|
|
bool RenderFormElement::includesPadding() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void RenderFormElement::setPadding()
|
|
{
|
|
if (!includesPadding())
|
|
return;
|
|
|
|
KHTMLProxyStyle *style = static_cast<KHTMLProxyStyle*>(getProxyStyle());
|
|
style->left = RenderWidget::paddingLeft();
|
|
style->right = RenderWidget::paddingRight();
|
|
style->top = RenderWidget::paddingTop();
|
|
style->bottom = RenderWidget::paddingBottom();
|
|
}
|
|
|
|
KdeUiProxyStyle* RenderFormElement::getProxyStyle()
|
|
{
|
|
assert(widget());
|
|
if (m_proxyStyle)
|
|
return m_proxyStyle;
|
|
m_proxyStyle = new KHTMLProxyStyle(widget());
|
|
widget()->setStyle( m_proxyStyle );
|
|
return m_proxyStyle;
|
|
}
|
|
|
|
short RenderFormElement::baselinePosition( bool f ) const
|
|
{
|
|
return RenderWidget::baselinePosition( f ) - 2 - style()->fontMetrics().descent();
|
|
}
|
|
|
|
void RenderFormElement::setQWidget( QWidget *w )
|
|
{
|
|
// Avoid dangling proxy pointer when we switch widgets.
|
|
// the widget will cleanup the proxy, as it is its kid.
|
|
m_proxyStyle = 0;
|
|
|
|
// sets the Qt Object Name for the purposes
|
|
// of setPadding() -- this is because QStyleSheet
|
|
// will propagate children of 'w' even if they are toplevel, like
|
|
// the "find" dialog or the popup menu
|
|
w->setObjectName("RenderFormElementWidget");
|
|
RenderWidget::setQWidget(w);
|
|
}
|
|
|
|
void RenderFormElement::updateFromElement()
|
|
{
|
|
m_widget->setEnabled(!element()->disabled());
|
|
|
|
// If we've disabled a focused element, clear its focus,
|
|
// so Qt doesn't do funny stuff like let one type into a disabled
|
|
// line edit.
|
|
if (element()->disabled() && element()->focused())
|
|
document()->quietResetFocus();
|
|
|
|
RenderWidget::updateFromElement();
|
|
}
|
|
|
|
// Some form widgets apply the padding internally (i.e. as if they were
|
|
// some kind of inline-block). Thus we only want to expose that padding
|
|
// while layouting (so that width/height calculations are correct), and
|
|
// then pretend it does not exist, as it is beyond the replaced edge and
|
|
// thus should not affect other calculations.
|
|
|
|
void RenderFormElement::calcMinMaxWidth()
|
|
{
|
|
m_exposeInternalPadding = true;
|
|
RenderWidget::calcMinMaxWidth();
|
|
m_exposeInternalPadding = false;
|
|
}
|
|
|
|
void RenderFormElement::calcWidth()
|
|
{
|
|
m_exposeInternalPadding = true;
|
|
RenderWidget::calcWidth();
|
|
m_exposeInternalPadding = false;
|
|
}
|
|
|
|
void RenderFormElement::calcHeight()
|
|
{
|
|
m_exposeInternalPadding = true;
|
|
RenderWidget::calcHeight();
|
|
m_exposeInternalPadding = false;
|
|
}
|
|
|
|
void RenderFormElement::layout()
|
|
{
|
|
KHTMLAssert( needsLayout() );
|
|
KHTMLAssert( minMaxKnown() );
|
|
|
|
// minimum height
|
|
m_height = 0;
|
|
calcWidth();
|
|
calcHeight();
|
|
|
|
if ( m_widget )
|
|
resizeWidget(m_width-borderLeft()-borderRight()-paddingLeft()-paddingRight(),
|
|
m_height-borderTop()-borderBottom()-paddingTop()-paddingBottom());
|
|
|
|
setNeedsLayout(false);
|
|
}
|
|
|
|
int RenderFormElement::calcContentWidth(int w) const
|
|
{
|
|
if (!shouldDisableNativeBorders()) {
|
|
if (style()->boxSizing() == CONTENT_BOX) {
|
|
int nativeBorderWidth = m_widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, m_widget);
|
|
return RenderBox::calcContentWidth(w) + 2 * nativeBorderWidth;
|
|
}
|
|
}
|
|
|
|
return RenderBox::calcContentWidth(w);
|
|
}
|
|
|
|
int RenderFormElement::calcContentHeight(int h) const
|
|
{
|
|
if (!shouldDisableNativeBorders()) {
|
|
if (style()->boxSizing() == CONTENT_BOX) {
|
|
int nativeBorderWidth = m_widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, m_widget);
|
|
return RenderBox::calcContentHeight(h) + 2 * nativeBorderWidth;
|
|
}
|
|
}
|
|
|
|
return RenderBox::calcContentHeight(h);
|
|
}
|
|
|
|
void RenderFormElement::paintOneBackground(QPainter *p, const QColor& c, const BackgroundLayer* bgLayer, QRect clipr, int _tx, int _ty, int w, int height)
|
|
{
|
|
int fudge = 0;
|
|
if (!shouldDisableNativeBorders()) {
|
|
fudge = m_isOxygenStyle ? 3 : 1;
|
|
}
|
|
|
|
paintBackgroundExtended(p, c, bgLayer, clipr, _tx, _ty, w, height,
|
|
fudge ? fudge : borderLeft() , fudge ? fudge : borderRight(), RenderWidget::paddingLeft(), RenderWidget::paddingRight(),
|
|
fudge ? fudge : borderTop(), fudge ? fudge : borderBottom(), RenderWidget::paddingTop(), RenderWidget::paddingBottom());
|
|
}
|
|
|
|
Qt::Alignment RenderFormElement::textAlignment() const
|
|
{
|
|
switch (style()->textAlign()) {
|
|
case LEFT:
|
|
case KHTML_LEFT:
|
|
return Qt::AlignLeft;
|
|
case RIGHT:
|
|
case KHTML_RIGHT:
|
|
return Qt::AlignRight;
|
|
case CENTER:
|
|
case KHTML_CENTER:
|
|
return Qt::AlignHCenter;
|
|
case JUSTIFY:
|
|
// Just fall into the auto code for justify.
|
|
case TAAUTO:
|
|
return style()->direction() == RTL ? Qt::AlignRight : Qt::AlignLeft;
|
|
}
|
|
assert(false); // Should never be reached.
|
|
return Qt::AlignLeft;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
RenderButton::RenderButton(HTMLGenericFormElementImpl *element)
|
|
: RenderFormElement(element)
|
|
{
|
|
m_hasTextIndentHack = false;
|
|
}
|
|
|
|
short RenderButton::baselinePosition( bool f ) const
|
|
{
|
|
int ret = (height()-RenderWidget::paddingTop()-RenderWidget::paddingBottom()+1)/2;
|
|
ret += marginTop() + RenderWidget::paddingTop();
|
|
ret += ((fontMetrics( f ).ascent())/2)-1;
|
|
return ret;
|
|
}
|
|
|
|
void RenderButton::layout()
|
|
{
|
|
RenderFormElement::layout();
|
|
bool needsTextIndentHack = false;
|
|
if (!style()->width().isAuto()) {
|
|
// check if we need to simulate the effect of a popular
|
|
// button text hiding 'trick' that makes use of negative text-indent,
|
|
// which we do not support on form widgets.
|
|
int ti = style()->textIndent().minWidth( containingBlockWidth() );
|
|
if (m_widget->width() <= qAbs(ti)) {
|
|
needsTextIndentHack = true;
|
|
}
|
|
}
|
|
if (m_hasTextIndentHack != needsTextIndentHack) {
|
|
m_hasTextIndentHack = needsTextIndentHack;
|
|
updateFromElement();
|
|
}
|
|
}
|
|
|
|
void RenderButton::setStyle(RenderStyle *style)
|
|
{
|
|
RenderFormElement::setStyle(style);
|
|
if (shouldDisableNativeBorders()) {
|
|
// we paint the borders ourselves on this button,
|
|
// remove the widget's native ones.
|
|
KHTMLProxyStyle* style = static_cast<KHTMLProxyStyle*>(getProxyStyle());
|
|
style->noBorder = true;
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------
|
|
|
|
|
|
RenderCheckBox::RenderCheckBox(HTMLInputElementImpl *element)
|
|
: RenderButton(element)
|
|
{
|
|
CheckBoxWidget* b = new CheckBoxWidget(view()->widget());
|
|
//b->setAutoMask(true);
|
|
b->setMouseTracking(true);
|
|
setQWidget(b);
|
|
|
|
// prevent firing toggled() signals on initialization
|
|
b->setChecked(element->checked());
|
|
|
|
connect(b,SIGNAL(stateChanged(int)),this,SLOT(slotStateChanged(int)));
|
|
m_ignoreStateChanged = false;
|
|
}
|
|
|
|
|
|
void RenderCheckBox::calcMinMaxWidth()
|
|
{
|
|
KHTMLAssert( !minMaxKnown() );
|
|
|
|
QCheckBox *cb = static_cast<QCheckBox *>( m_widget );
|
|
QSize s( qMin(22, qMax(14, cb->style()->pixelMetric( QStyle::PM_IndicatorWidth ))),
|
|
qMin(22, qMax(12, cb->style()->pixelMetric( QStyle::PM_IndicatorHeight ))) );
|
|
setIntrinsicWidth( s.width() );
|
|
setIntrinsicHeight( s.height() );
|
|
|
|
RenderButton::calcMinMaxWidth();
|
|
}
|
|
|
|
void RenderCheckBox::updateFromElement()
|
|
{
|
|
if (widget()->isChecked() != element()->checked()) {
|
|
m_ignoreStateChanged = true;
|
|
widget()->setChecked(element()->checked());
|
|
m_ignoreStateChanged = false;
|
|
}
|
|
|
|
RenderButton::updateFromElement();
|
|
}
|
|
|
|
void RenderCheckBox::slotStateChanged(int state)
|
|
{
|
|
if (m_ignoreStateChanged) return;
|
|
element()->setChecked(state == Qt::Checked);
|
|
}
|
|
|
|
bool RenderCheckBox::handleEvent(const DOM::EventImpl& ev)
|
|
{
|
|
switch(ev.id()) {
|
|
case EventImpl::DOMFOCUSIN_EVENT:
|
|
case EventImpl::DOMFOCUSOUT_EVENT:
|
|
case EventImpl::MOUSEMOVE_EVENT:
|
|
case EventImpl::MOUSEOUT_EVENT:
|
|
case EventImpl::MOUSEOVER_EVENT:
|
|
return RenderButton::handleEvent(ev);
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------
|
|
|
|
RenderRadioButton::RenderRadioButton(HTMLInputElementImpl *element)
|
|
: RenderButton(element)
|
|
{
|
|
RadioButtonWidget* b = new RadioButtonWidget(view()->widget());
|
|
b->setMouseTracking(true);
|
|
b->setAutoExclusive(false);
|
|
setQWidget(b);
|
|
|
|
// prevent firing toggled() signals on initialization
|
|
b->setChecked(element->checked());
|
|
|
|
connect(b,SIGNAL(toggled(bool)),this,SLOT(slotToggled(bool)));
|
|
m_ignoreToggled = false;
|
|
}
|
|
|
|
void RenderRadioButton::updateFromElement()
|
|
{
|
|
m_ignoreToggled = true;
|
|
widget()->setChecked(element()->checked());
|
|
m_ignoreToggled = false;
|
|
|
|
RenderButton::updateFromElement();
|
|
}
|
|
|
|
void RenderRadioButton::calcMinMaxWidth()
|
|
{
|
|
KHTMLAssert( !minMaxKnown() );
|
|
|
|
QRadioButton *rb = static_cast<QRadioButton *>( m_widget );
|
|
QSize s( qMin(22, qMax(14, rb->style()->pixelMetric( QStyle::PM_ExclusiveIndicatorWidth ))),
|
|
qMin(20, qMax(12, rb->style()->pixelMetric( QStyle::PM_ExclusiveIndicatorHeight ))) );
|
|
setIntrinsicWidth( s.width() );
|
|
setIntrinsicHeight( s.height() );
|
|
|
|
RenderButton::calcMinMaxWidth();
|
|
}
|
|
|
|
void RenderRadioButton::slotToggled(bool /*activated*/)
|
|
{
|
|
if (m_ignoreToggled)
|
|
return;
|
|
}
|
|
|
|
bool RenderRadioButton::handleEvent(const DOM::EventImpl& ev)
|
|
{
|
|
switch(ev.id()) {
|
|
case EventImpl::DOMFOCUSIN_EVENT:
|
|
case EventImpl::DOMFOCUSOUT_EVENT:
|
|
case EventImpl::MOUSEMOVE_EVENT:
|
|
case EventImpl::MOUSEOUT_EVENT:
|
|
case EventImpl::MOUSEOVER_EVENT:
|
|
return RenderButton::handleEvent(ev);
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------
|
|
|
|
static const QString &sBorderNoneSheet = KGlobal::staticQString("QPushButton{border:none}");
|
|
|
|
RenderSubmitButton::RenderSubmitButton(HTMLInputElementImpl *element)
|
|
: RenderButton(element)
|
|
{
|
|
PushButtonWidget* p = new PushButtonWidget(view()->widget());
|
|
setQWidget(p);
|
|
//p->setAutoMask(true);
|
|
p->setMouseTracking(true);
|
|
p->setDefault(false);
|
|
p->setAutoDefault(false);
|
|
}
|
|
|
|
static inline void setStyleSheet_helper(const QString& s, QWidget* w)
|
|
{
|
|
// ### buggy Qt stylesheets mess with the widget palette.
|
|
// force it again after any stylesheet update.
|
|
QPalette pal = w->palette();
|
|
w->setStyleSheet(s);
|
|
w->setPalette(pal);
|
|
}
|
|
|
|
void RenderSubmitButton::setPadding()
|
|
{
|
|
// Proxy styling doesn't work well enough for buttons.
|
|
// Use stylesheets instead. tests/css/button-padding-top.html
|
|
assert(!m_proxyStyle);
|
|
|
|
if (!includesPadding())
|
|
return;
|
|
|
|
if (!RenderWidget::paddingLeft() && !RenderWidget::paddingRight() &&
|
|
!RenderWidget::paddingTop() && !RenderWidget::paddingBottom()) {
|
|
setStyleSheet_helper( (shouldDisableNativeBorders() ? sBorderNoneSheet : QString()), widget() );
|
|
return;
|
|
}
|
|
|
|
setStyleSheet_helper(
|
|
QString("QPushButton{padding-left:%1px; padding-right:%2px; padding-top:%3px; padding-bottom:%4px}")
|
|
.arg( RenderWidget::paddingLeft() )
|
|
.arg( RenderWidget::paddingRight() )
|
|
.arg( RenderWidget::paddingTop() )
|
|
.arg( RenderWidget::paddingBottom()) + (shouldDisableNativeBorders() ? sBorderNoneSheet : QString())
|
|
, widget());
|
|
}
|
|
|
|
void RenderSubmitButton::setStyle(RenderStyle *style)
|
|
{
|
|
// Proxy styling doesn't work well enough for buttons.
|
|
// Use stylesheets instead. tests/css/button-padding-top.html
|
|
assert(!m_proxyStyle);
|
|
RenderFormElement::setStyle(style);
|
|
|
|
QString s = widget()->styleSheet();
|
|
if (shouldDisableNativeBorders()) {
|
|
// we paint the borders ourselves on this button,
|
|
// remove the widget's native ones.
|
|
if (!s.contains(sBorderNoneSheet)) {
|
|
s.append(sBorderNoneSheet);
|
|
setStyleSheet_helper( s, widget() );
|
|
}
|
|
} else {
|
|
setStyleSheet_helper( s.remove(sBorderNoneSheet), widget() );
|
|
}
|
|
}
|
|
|
|
QString RenderSubmitButton::rawText()
|
|
{
|
|
QString value = element()->valueWithDefault().string();
|
|
value = value.trimmed();
|
|
QString raw;
|
|
for(int i = 0; i < value.length(); i++) {
|
|
raw += value[i];
|
|
if(value[i] == '&')
|
|
raw += '&';
|
|
}
|
|
return raw;
|
|
}
|
|
|
|
bool RenderSubmitButton::canHaveBorder() const
|
|
{
|
|
// ### TODO would be nice to be able to
|
|
// return style()->hasBackgroundImage() here,
|
|
// depending on a config option (e.g. 'favour usability/integration over aspect')
|
|
// so that only buttons with both a custom border
|
|
// and a background image are drawn without native styling.
|
|
//
|
|
// This would go in the same place, gui wise, as a choice of b/w default color scheme,
|
|
// versus native color scheme.
|
|
|
|
return true;
|
|
}
|
|
|
|
void RenderSubmitButton::calcMinMaxWidth()
|
|
{
|
|
KHTMLAssert( !minMaxKnown() );
|
|
|
|
QString raw = rawText();
|
|
QPushButton* pb = static_cast<QPushButton*>(m_widget);
|
|
pb->setText(raw);
|
|
pb->setFont(style()->font());
|
|
|
|
bool empty = raw.isEmpty();
|
|
if ( empty )
|
|
raw = QLatin1Char('X');
|
|
QFontMetrics fm = pb->fontMetrics();
|
|
QSize ts = fm.size( Qt::TextShowMnemonic, raw);
|
|
//Oh boy.
|
|
QStyleOptionButton butOpt;
|
|
butOpt.init(pb);
|
|
butOpt.text = raw;
|
|
QSize s = pb->style()->sizeFromContents( QStyle::CT_PushButton, &butOpt, ts, pb );
|
|
|
|
s = s.expandedTo(QApplication::globalStrut());
|
|
int margin = pb->style()->pixelMetric( QStyle::PM_ButtonMargin) +
|
|
pb->style()->pixelMetric( QStyle::PM_DefaultFrameWidth ) * 2;
|
|
|
|
int w = ts.width() + margin;
|
|
int h = s.height();
|
|
|
|
assert(includesPadding());
|
|
int hpadding = RenderWidget::paddingLeft() + RenderWidget::paddingRight();
|
|
int vpadding = RenderWidget::paddingTop() + RenderWidget::paddingBottom();
|
|
|
|
// add 30% margins to the width (heuristics to make it look similar to IE)
|
|
// ### FIXME BASELINE: we could drop this emulation and adopt Mozilla style buttons
|
|
// (+/- padding: 0px 8px 0px 8px) - IE is most often in a separate css
|
|
// code path nowadays, so we have wider buttons than other engines.
|
|
int toAdd = (w*13/10)-w-hpadding;
|
|
toAdd = qMax(0,toAdd);
|
|
w += toAdd;
|
|
|
|
if (shouldDisableNativeBorders()) {
|
|
// we paint the borders ourselves, so let's override our height to something saner
|
|
h = ts.height();
|
|
} else {
|
|
h -= vpadding;
|
|
}
|
|
s = QSize(w,h).expandedTo(QApplication::globalStrut());
|
|
|
|
setIntrinsicWidth( s.width() );
|
|
setIntrinsicHeight( s.height() );
|
|
|
|
RenderButton::calcMinMaxWidth();
|
|
}
|
|
|
|
void RenderSubmitButton::updateFromElement()
|
|
{
|
|
QString oldText = static_cast<QPushButton*>(m_widget)->text();
|
|
QString newText = rawText();
|
|
static_cast<QPushButton*>(m_widget)->setText(newText);
|
|
if ( oldText != newText )
|
|
setNeedsLayoutAndMinMaxRecalc();
|
|
RenderFormElement::updateFromElement();
|
|
}
|
|
|
|
short RenderSubmitButton::baselinePosition( bool f ) const
|
|
{
|
|
int ret = (height()-RenderWidget::paddingTop()-RenderWidget::paddingBottom()+1)/2;
|
|
ret += marginTop() + RenderWidget::paddingTop();
|
|
ret += ((fontMetrics( f ).ascent())/2)-2;
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------
|
|
|
|
RenderResetButton::RenderResetButton(HTMLInputElementImpl *element)
|
|
: RenderSubmitButton(element)
|
|
{
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------
|
|
|
|
namespace khtml {
|
|
|
|
class CompletionWidget: public KCompletionBox
|
|
{
|
|
public:
|
|
CompletionWidget( QWidget *parent = 0 ) : KCompletionBox( parent ) {}
|
|
virtual QPoint globalPositionHint() const
|
|
{
|
|
QWidget* pw = parentWidget();
|
|
KHTMLWidget* kwp = dynamic_cast<KHTMLWidget*>(pw);
|
|
if (!kwp) {
|
|
qDebug() << "CompletionWidget has no KHTMLWidget parent" << endl;
|
|
return KCompletionBox::globalPositionHint();
|
|
}
|
|
QPoint dest;
|
|
KHTMLView* v = kwp->m_kwp->rootViewPos(dest);
|
|
QPoint ret;
|
|
if (v) {
|
|
ret = v->mapToGlobal( dest + QPoint(0, pw->height()) );
|
|
int zoomLevel = v->zoomLevel();
|
|
if (zoomLevel != 100) {
|
|
ret.setX( ret.x()*zoomLevel/100 );
|
|
ret.setY( ret.y()*zoomLevel/100 );
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
LineEditWidget::LineEditWidget(DOM::HTMLInputElementImpl* input, KHTMLView* view, QWidget* parent)
|
|
: KLineEdit(parent), m_input(input), m_view(view)
|
|
{
|
|
m_kwp->setIsRedirected( true );
|
|
setMouseTracking(true);
|
|
KActionCollection *ac = new KActionCollection(this);
|
|
m_spellAction = KStandardAction::spelling( this, SLOT(slotCheckSpelling()), ac );
|
|
|
|
setCompletionBox( new CompletionWidget( this ) );
|
|
completionBox()->setObjectName("completion box");
|
|
completionBox()->setFont(font());
|
|
}
|
|
|
|
LineEditWidget::~LineEditWidget()
|
|
{
|
|
}
|
|
|
|
void LineEditWidget::slotCheckSpelling()
|
|
{
|
|
if ( text().isEmpty() ) {
|
|
return;
|
|
}
|
|
Sonnet::Dialog *spellDialog = new Sonnet::Dialog(new Sonnet::BackgroundChecker(this), 0);
|
|
connect(spellDialog, SIGNAL(replace(QString,int,QString)), this, SLOT(spellCheckerCorrected(QString,int,QString)));
|
|
connect(spellDialog, SIGNAL(misspelling(QString,int)), this, SLOT(spellCheckerMisspelling(QString,int)));
|
|
connect(spellDialog, SIGNAL(done(QString)), this, SLOT(slotSpellCheckDone(QString)));
|
|
connect(spellDialog, SIGNAL(cancel()), this, SLOT(spellCheckerFinished()));
|
|
connect(spellDialog, SIGNAL(stop()), this, SLOT(spellCheckerFinished()));
|
|
spellDialog->setBuffer(text());
|
|
spellDialog->show();
|
|
}
|
|
|
|
void LineEditWidget::spellCheckerMisspelling( const QString &_text, int pos)
|
|
{
|
|
highLightWord( _text.length(),pos );
|
|
}
|
|
|
|
void LineEditWidget::setFocus()
|
|
{
|
|
KLineEdit::setFocus();
|
|
end( false );
|
|
}
|
|
|
|
void LineEditWidget::highLightWord( unsigned int length, unsigned int pos )
|
|
{
|
|
setSelection ( pos, length );
|
|
}
|
|
|
|
void LineEditWidget::spellCheckerCorrected( const QString &old, int pos, const QString &corr )
|
|
{
|
|
if( old!= corr )
|
|
{
|
|
setSelection ( pos, old.length() );
|
|
insert( corr );
|
|
setSelection ( pos, corr.length() );
|
|
}
|
|
}
|
|
|
|
void LineEditWidget::spellCheckerFinished()
|
|
{
|
|
}
|
|
|
|
void LineEditWidget::slotSpellCheckDone( const QString &s )
|
|
{
|
|
if( s != text() )
|
|
setText( s );
|
|
}
|
|
|
|
namespace khtml {
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
class WebShortcutCreator
|
|
{
|
|
public:
|
|
/**
|
|
* @short Creates a Web Shourtcut without using kdebase SearchProvider class.
|
|
* It is used by LineEditWidget.
|
|
*/
|
|
static bool createWebShortcut(QString query);
|
|
|
|
private:
|
|
static bool askData(QString &name, QString &keys);
|
|
static void createFile(QString query, QString name, QString keys);
|
|
};
|
|
|
|
bool WebShortcutCreator::createWebShortcut(QString query)
|
|
{
|
|
QString name = i18n( "New Web Shortcut" );
|
|
QString keys;
|
|
if ( askData( name, keys ) ) {
|
|
bool isOk;
|
|
do { //It's going to be checked if the keys have already been assigned
|
|
isOk = true;
|
|
QStringList keyList( keys.split( ',' ) );
|
|
KService::List providers = KServiceTypeTrader::self()->query( "SearchProvider" );
|
|
foreach ( const KService::Ptr &provider, providers ) {
|
|
if ( !isOk ) {
|
|
break;
|
|
}
|
|
foreach ( const QString &s, provider->property( "Keys" ).toStringList() ) {
|
|
if ( !isOk ) {
|
|
break;
|
|
}
|
|
foreach ( const QString &t, keys ) {
|
|
if ( !isOk ) {
|
|
break;
|
|
}
|
|
if ( s == t ) {
|
|
KMessageBox::sorry( 0, i18n( "%1 is already assigned to %2", s, provider->name() ), i18n( "Error" ) );
|
|
isOk = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( !isOk && !askData( name, keys ) ) {
|
|
return false;
|
|
}
|
|
} while ( !isOk );
|
|
createFile( query, name, keys );
|
|
return true;
|
|
} else return false;
|
|
}
|
|
|
|
void WebShortcutCreator::createFile(QString query, QString name, QString keys)
|
|
{
|
|
// SearchProvider class is part of kdebase, so the file is written as
|
|
// an standard desktop file.
|
|
QString fileName( keys );
|
|
KStandardDirs dirs;
|
|
QString dir = dirs.saveLocation( "services", "searchproviders" );
|
|
while ( KGlobal::dirs()->exists( dir + fileName + ".desktop" ) )
|
|
fileName += '_';
|
|
KDesktopFile f( dir + fileName + ".desktop");
|
|
f.desktopGroup().writeEntry( "Keys", keys );
|
|
f.desktopGroup().writeEntry( "Type", "Service" );
|
|
f.desktopGroup().writeEntry( "ServiceTypes", "SearchProvider" );
|
|
f.desktopGroup().writeEntry( "Name", name );
|
|
f.desktopGroup().writeEntry( "Query", query );
|
|
f.sync();
|
|
KBuildSycocaProgressDialog::rebuildKSycoca( 0 );
|
|
}
|
|
|
|
bool WebShortcutCreator::askData(QString &name, QString &keys)
|
|
{
|
|
KDialog *dialog = new KDialog();
|
|
QWidget *widget = new QWidget();
|
|
dialog->setButtons( KDialog::Ok | KDialog::Cancel );
|
|
dialog->setCaption( name );
|
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
|
widget->setLayout( mainLayout );
|
|
dialog->setMainWidget( widget );
|
|
QHBoxLayout *layout = new QHBoxLayout();
|
|
mainLayout->addLayout( layout );
|
|
QLabel *label = new QLabel( i18n( "Search &provider name:" ) );
|
|
layout->addWidget( label );
|
|
QLineEdit *nameEdit = new QLineEdit( i18n( "New search provider" ) );
|
|
label->setBuddy( nameEdit );
|
|
layout->addWidget( nameEdit );
|
|
layout = new QHBoxLayout();
|
|
mainLayout->addLayout( layout );
|
|
label = new QLabel( i18n( "UR&I shortcuts:" ) );
|
|
layout->addWidget( label );
|
|
QLineEdit *keysEdit = new QLineEdit();
|
|
label->setBuddy( keysEdit );
|
|
layout->addWidget( keysEdit );
|
|
bool res = dialog->exec();
|
|
if (res) {
|
|
name = nameEdit->text();
|
|
keys = keysEdit->text();
|
|
}
|
|
delete dialog;
|
|
return res;
|
|
}
|
|
|
|
}
|
|
|
|
void LineEditWidget::slotCreateWebShortcut()
|
|
{
|
|
QString queryName( m_input->name().string() );
|
|
HTMLFormElementImpl *form = m_input->form();
|
|
KUrl url( form->action().string() );
|
|
KUrl baseUrl( m_view->part()->baseURL().url() + '?' );
|
|
if ( !url.hasPath() ) {
|
|
url.setPath( baseUrl.path() );
|
|
}
|
|
if ( !url.hasHost() ) {
|
|
url.setProtocol( baseUrl.protocol() );
|
|
url.setHost( baseUrl.host() );
|
|
}
|
|
NodeImpl *node;
|
|
HTMLInputElementImpl *inputNode;
|
|
for( unsigned long i = 0; ( node = form->elements()->item( i ) ); i++ ) {
|
|
inputNode = dynamic_cast<HTMLInputElementImpl*>( node );
|
|
if ( inputNode ) {
|
|
if ( ( !inputNode->name().string().size() ) ||
|
|
(inputNode->name().string() == queryName) ) {
|
|
continue;
|
|
} else {
|
|
switch ( inputNode->inputType() ) {
|
|
case HTMLInputElementImpl::CHECKBOX:
|
|
case HTMLInputElementImpl::RADIO:
|
|
if ( !inputNode->checked() ) {
|
|
break;
|
|
}
|
|
case HTMLInputElementImpl::TEXT:
|
|
case HTMLInputElementImpl::PASSWORD:
|
|
case HTMLInputElementImpl::HIDDEN:
|
|
url.addQueryItem( inputNode->name().string(), inputNode->value().string() );
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
QString query( url.url() );
|
|
if ( !query.contains( "?" ) ) {
|
|
query += '?'; //This input is the only one of the form
|
|
}
|
|
query += '&' + queryName + "=\\{@}";
|
|
WebShortcutCreator::createWebShortcut( query );
|
|
}
|
|
|
|
void LineEditWidget::contextMenuEvent(QContextMenuEvent *e)
|
|
{
|
|
QMenu* popup = createStandardContextMenu();
|
|
|
|
if ( !popup )
|
|
return;
|
|
|
|
if (m_input->autoComplete()) {
|
|
popup->addSeparator();
|
|
QAction* act = popup->addAction( KIcon("edit-clear-history"), i18n("Clear &History"));
|
|
act->setEnabled(compObj() && !compObj()->isEmpty());
|
|
connect(act, SIGNAL(triggered()),
|
|
this, SLOT(clearHistoryActivated()));
|
|
}
|
|
|
|
if (echoMode() == QLineEdit::Normal &&
|
|
!isReadOnly()) {
|
|
popup->addSeparator();
|
|
|
|
popup->addAction( m_spellAction );
|
|
m_spellAction->setEnabled( !text().isEmpty() );
|
|
}
|
|
if ( !m_view->part()->onlyLocalReferences() ) {
|
|
popup->addSeparator();
|
|
QAction *act = popup->addAction( i18n("Create Web Shortcut") );
|
|
connect(act, SIGNAL(triggered()),
|
|
this, SLOT(slotCreateWebShortcut()));
|
|
}
|
|
|
|
emit aboutToShowContextMenu(popup);
|
|
|
|
popup->exec(e->globalPos());
|
|
delete popup;
|
|
}
|
|
|
|
|
|
void LineEditWidget::clearHistoryActivated()
|
|
{
|
|
m_view->clearCompletionHistory(m_input->name().string());
|
|
if (compObj())
|
|
compObj()->clear();
|
|
}
|
|
|
|
bool LineEditWidget::event( QEvent *e )
|
|
{
|
|
if (KLineEdit::event(e))
|
|
return true;
|
|
#if 0
|
|
if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
|
|
QKeyEvent* ke = (QKeyEvent*) e;
|
|
if ( ke->modifiers() & Qt::ControlModifier ) {
|
|
switch ( ke->key() ) {
|
|
case Qt::Key_Left:
|
|
case Qt::Key_Right:
|
|
case Qt::Key_Up:
|
|
case Qt::Key_Down:
|
|
case Qt::Key_Home:
|
|
case Qt::Key_End:
|
|
ke->accept();
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
void LineEditWidget::mouseMoveEvent(QMouseEvent *e)
|
|
{
|
|
// hack to prevent Qt from calling setCursor on the widget
|
|
setDragEnabled(false);
|
|
KLineEdit::mouseMoveEvent(e);
|
|
setDragEnabled(true);
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
RenderLineEdit::RenderLineEdit(HTMLInputElementImpl *element)
|
|
: RenderFormElement(element), m_blockElementUpdates(false)
|
|
{
|
|
LineEditWidget *edit = new LineEditWidget(element, view(), view()->widget());
|
|
connect(edit,SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
|
|
connect(edit,SIGNAL(textChanged(QString)),this,SLOT(slotTextChanged(QString)));
|
|
|
|
if(element->inputType() == HTMLInputElementImpl::PASSWORD)
|
|
edit->setEchoMode( QLineEdit::Password );
|
|
|
|
if ( element->autoComplete() ) {
|
|
QStringList completions = view()->formCompletionItems(element->name().string());
|
|
if (completions.count()) {
|
|
edit->completionObject()->setItems(completions);
|
|
edit->setContextMenuPolicy(Qt::NoContextMenu);
|
|
edit->completionBox()->setTabHandling( false );
|
|
}
|
|
}
|
|
|
|
setQWidget(edit);
|
|
}
|
|
|
|
short RenderLineEdit::baselinePosition( bool f ) const
|
|
{
|
|
bool hasFrame = static_cast<LineEditWidget*>(widget())->hasFrame();
|
|
int bTop = hasFrame ? 0 : borderTop();
|
|
int bBottom = hasFrame ? 0 : borderBottom();
|
|
int ret = (height()-RenderWidget::paddingTop()-RenderWidget::paddingBottom()-bTop-bBottom+1)/2;
|
|
ret += marginTop() + RenderWidget::paddingTop() + bTop;
|
|
ret += ((fontMetrics( f ).ascent())/2)-2;
|
|
return ret;
|
|
}
|
|
|
|
void RenderLineEdit::setStyle(RenderStyle* _style)
|
|
{
|
|
RenderFormElement::setStyle( _style );
|
|
|
|
if (widget()->alignment() != textAlignment())
|
|
widget()->setAlignment(textAlignment());
|
|
|
|
bool showClearButton = (!shouldDisableNativeBorders() && !_style->hasBackgroundImage());
|
|
|
|
if (!showClearButton && widget()->isClearButtonShown()) {
|
|
widget()->setClearButtonShown(false);
|
|
}
|
|
else if (showClearButton && !widget()->isClearButtonShown()) {
|
|
widget()->setClearButtonShown(true);
|
|
QObjectList children = widget()->children();
|
|
foreach (QObject* object, children) {
|
|
QWidget *w = qobject_cast<QWidget*>(object);
|
|
if (w && !w->isWindow() && (w->objectName() == "KLineEditButton")) {
|
|
// this duplicates KHTMLView's handleWidget but this widget
|
|
// is created on demand, so it might not be here at ChildPolished time
|
|
w->installEventFilter(view());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_proxyStyle) {
|
|
static_cast<KHTMLProxyStyle*>(m_proxyStyle)->clearButtonOverlay = qMax(0, widget()->clearButtonUsedSize().width());
|
|
}
|
|
}
|
|
|
|
void RenderLineEdit::highLightWord( unsigned int length, unsigned int pos )
|
|
{
|
|
LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
|
|
if ( w )
|
|
w->highLightWord( length, pos );
|
|
}
|
|
|
|
|
|
void RenderLineEdit::slotReturnPressed()
|
|
{
|
|
// don't submit the form when return was pressed in a completion-popup
|
|
KCompletionBox *box = widget()->completionBox(false);
|
|
|
|
if ( box && box->isVisible() && box->currentRow() != -1 ) {
|
|
box->hide();
|
|
return;
|
|
}
|
|
|
|
// Emit onChange if necessary
|
|
// Works but might not be enough, dirk said he had another solution at
|
|
// hand (can't remember which) - David
|
|
handleFocusOut();
|
|
|
|
HTMLFormElementImpl* fe = element()->form();
|
|
if ( fe )
|
|
fe->submitFromKeyboard();
|
|
}
|
|
|
|
void RenderLineEdit::handleFocusOut()
|
|
{
|
|
if ( widget() && widget()->isModified() ) {
|
|
element()->onChange();
|
|
widget()->setModified( false );
|
|
}
|
|
}
|
|
|
|
void RenderLineEdit::calcMinMaxWidth()
|
|
{
|
|
KHTMLAssert( !minMaxKnown() );
|
|
|
|
const QFontMetrics &fm = style()->fontMetrics();
|
|
QSize s;
|
|
|
|
int size = (element()->size() > 0) ? (element()->size() + 1) : 17; // "some"
|
|
|
|
int h = fm.lineSpacing();
|
|
int w = (fm.height() * size) / 2; // on average a character cell is twice as tall as it is wide
|
|
|
|
QStyleOptionFrame opt;
|
|
opt.initFrom(widget());
|
|
if (widget()->hasFrame())
|
|
opt.lineWidth = widget()->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, widget());
|
|
|
|
s = QSize(w, qMax(h, 14));
|
|
s = widget()->style()->sizeFromContents(QStyle::CT_LineEdit, &opt, s, widget());
|
|
s = s.expandedTo(QApplication::globalStrut());
|
|
|
|
setIntrinsicWidth( s.width() );
|
|
setIntrinsicHeight( s.height() );
|
|
|
|
RenderFormElement::calcMinMaxWidth();
|
|
}
|
|
|
|
void RenderLineEdit::updateFromElement()
|
|
{
|
|
int ml = element()->maxLength();
|
|
if ( ml < 0 )
|
|
ml = 32767;
|
|
|
|
if ( widget()->maxLength() != ml ) {
|
|
widget()->setMaxLength( ml );
|
|
}
|
|
|
|
if (element()->value().string() != widget()->text()) {
|
|
m_blockElementUpdates = true; // Do not block signals here (#188374)
|
|
int pos = widget()->cursorPosition();
|
|
widget()->setText(element()->value().string());
|
|
widget()->setCursorPosition(pos);
|
|
m_blockElementUpdates = false;
|
|
}
|
|
widget()->setReadOnly(element()->readOnly());
|
|
|
|
widget()->setClickMessage(element()->placeholder().string().remove(QLatin1Char('\n')).remove(QLatin1Char('\r')));
|
|
|
|
RenderFormElement::updateFromElement();
|
|
}
|
|
|
|
void RenderLineEdit::slotTextChanged(const QString &string)
|
|
{
|
|
if (m_blockElementUpdates) return;
|
|
|
|
// don't use setValue here!
|
|
element()->m_value = string.isNull() ? DOMString("") : string;
|
|
element()->m_unsubmittedFormChange = true;
|
|
}
|
|
|
|
void RenderLineEdit::select()
|
|
{
|
|
static_cast<LineEditWidget*>(m_widget)->selectAll();
|
|
}
|
|
|
|
long RenderLineEdit::selectionStart()
|
|
{
|
|
LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
|
|
if (w->hasSelectedText())
|
|
return w->selectionStart();
|
|
else
|
|
return w->cursorPosition();
|
|
}
|
|
|
|
|
|
long RenderLineEdit::selectionEnd()
|
|
{
|
|
LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
|
|
if (w->hasSelectedText())
|
|
return w->selectionStart() + w->selectedText().length();
|
|
else
|
|
return w->cursorPosition();
|
|
}
|
|
|
|
void RenderLineEdit::setSelectionStart(long pos)
|
|
{
|
|
LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
|
|
//See whether we have a non-empty selection now.
|
|
long end = selectionEnd();
|
|
if (end > pos)
|
|
w->setSelection(pos, end - pos);
|
|
w->setCursorPosition(pos);
|
|
}
|
|
|
|
void RenderLineEdit::setSelectionEnd(long pos)
|
|
{
|
|
LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
|
|
//See whether we have a non-empty selection now.
|
|
long start = selectionStart();
|
|
if (start < pos)
|
|
w->setSelection(start, pos - start);
|
|
|
|
w->setCursorPosition(pos);
|
|
}
|
|
|
|
void RenderLineEdit::setSelectionRange(long start, long end)
|
|
{
|
|
LineEditWidget* w = static_cast<LineEditWidget*>(m_widget);
|
|
w->setCursorPosition(end);
|
|
w->setSelection(start, end - start);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
RenderFieldset::RenderFieldset(HTMLGenericFormElementImpl *element)
|
|
: RenderBlock(element)
|
|
{
|
|
m_intrinsicWidth = 0;
|
|
}
|
|
|
|
void RenderFieldset::calcMinMaxWidth()
|
|
{
|
|
RenderBlock::calcMinMaxWidth();
|
|
if (style()->htmlHacks()){ if (RenderObject* legend = findLegend()) {
|
|
int legendMinWidth = legend->minWidth();
|
|
|
|
Length legendMarginLeft = legend->style()->marginLeft();
|
|
Length legendMarginRight = legend->style()->marginLeft();
|
|
|
|
if (legendMarginLeft.isFixed())
|
|
legendMinWidth += legendMarginLeft.value();
|
|
|
|
if (legendMarginRight.isFixed())
|
|
legendMinWidth += legendMarginRight.value();
|
|
|
|
m_intrinsicWidth = qMax((int)m_minWidth, legendMinWidth + paddingLeft() + paddingRight() + borderLeft() + borderRight());
|
|
}}
|
|
}
|
|
|
|
RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren)
|
|
{
|
|
RenderObject* legend = findLegend();
|
|
if (legend) {
|
|
if (relayoutChildren)
|
|
legend->setNeedsLayout(true);
|
|
legend->layoutIfNeeded();
|
|
|
|
int xPos = borderLeft() + paddingLeft() + legend->marginLeft();
|
|
if (style()->direction() == RTL)
|
|
xPos = m_width - paddingRight() - borderRight() - legend->width() - legend->marginRight();
|
|
int b = borderTop();
|
|
int h = legend->height();
|
|
legend->setPos(xPos, qMax((b-h)/2, 0));
|
|
m_height = qMax(b,h) + paddingTop();
|
|
}
|
|
return legend;
|
|
}
|
|
|
|
RenderObject* RenderFieldset::findLegend() const
|
|
{
|
|
for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) {
|
|
if (!legend->isFloatingOrPositioned() && legend->element() &&
|
|
legend->element()->id() == ID_LEGEND)
|
|
return legend;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void RenderFieldset::paintBoxDecorations(PaintInfo& pI, int _tx, int _ty)
|
|
{
|
|
//kDebug( 6040 ) << renderName() << "::paintDecorations()";
|
|
|
|
RenderObject* legend = findLegend();
|
|
if (!legend)
|
|
return RenderBlock::paintBoxDecorations(pI, _tx, _ty);
|
|
|
|
int w = width();
|
|
int h = height() + borderTopExtra() + borderBottomExtra();
|
|
int yOff = (legend->yPos() > 0) ? 0 : (legend->height()-borderTop())/2;
|
|
int legendBottom = _ty + legend->yPos() + legend->height();
|
|
h -= yOff;
|
|
_ty += yOff - borderTopExtra();
|
|
|
|
QRect cr = QRect(_tx, _ty, w, h).intersected( pI.r );
|
|
paintOneBackground(pI.p, style()->backgroundColor(), style()->backgroundLayers(), cr, _tx, _ty, w, h);
|
|
|
|
if ( style()->hasBorder() )
|
|
paintBorderMinusLegend(pI.p, _tx, _ty, w, h, style(), legend->xPos(), legend->width(), legendBottom);
|
|
}
|
|
|
|
void RenderFieldset::paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w, int h,
|
|
const RenderStyle* style, int lx, int lw, int lb)
|
|
{
|
|
|
|
const QColor& tc = style->borderTopColor();
|
|
const QColor& bc = style->borderBottomColor();
|
|
|
|
EBorderStyle ts = style->borderTopStyle();
|
|
EBorderStyle bs = style->borderBottomStyle();
|
|
EBorderStyle ls = style->borderLeftStyle();
|
|
EBorderStyle rs = style->borderRightStyle();
|
|
|
|
bool render_t = ts > BHIDDEN;
|
|
bool render_l = ls > BHIDDEN;
|
|
bool render_r = rs > BHIDDEN;
|
|
bool render_b = bs > BHIDDEN;
|
|
|
|
int borderLeftWidth = style->borderLeftWidth();
|
|
int borderRightWidth = style->borderRightWidth();
|
|
|
|
if(render_t) {
|
|
if (lx >= borderLeftWidth)
|
|
drawBorder(p, _tx, _ty, _tx + lx, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
|
|
(render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0), 0);
|
|
if (lx + lw <= w - borderRightWidth)
|
|
drawBorder(p, _tx+lx+lw, _ty, _tx + w, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
|
|
0, (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
|
|
}
|
|
|
|
if(render_b)
|
|
drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
|
|
(render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0),
|
|
(render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0));
|
|
|
|
if(render_l)
|
|
{
|
|
const QColor& lc = style->borderLeftColor();
|
|
|
|
bool ignore_top =
|
|
(tc == lc) &&
|
|
(ls >= OUTSET) &&
|
|
(ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
|
|
|
|
bool ignore_bottom =
|
|
(bc == lc) &&
|
|
(ls >= OUTSET) &&
|
|
(bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
|
|
|
|
int startY = _ty;
|
|
if (lx < borderLeftWidth && lx + lw > 0) {
|
|
// The legend intersects the border.
|
|
ignore_top = true;
|
|
startY = lb;
|
|
}
|
|
|
|
drawBorder(p, _tx, startY, _tx + borderLeftWidth, _ty + h, BSLeft, lc, style->color(), ls,
|
|
ignore_top?0:style->borderTopWidth(),
|
|
ignore_bottom?0:style->borderBottomWidth());
|
|
}
|
|
|
|
if(render_r)
|
|
{
|
|
const QColor& rc = style->borderRightColor();
|
|
|
|
bool ignore_top =
|
|
(tc == rc) &&
|
|
(rs >= DOTTED || rs == INSET) &&
|
|
(ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
|
|
|
|
bool ignore_bottom =
|
|
(bc == rc) &&
|
|
(rs >= DOTTED || rs == INSET) &&
|
|
(bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
|
|
|
|
int startY = _ty;
|
|
if (lx < w && lx + lw > w - borderRightWidth) {
|
|
// The legend intersects the border.
|
|
ignore_top = true;
|
|
startY = lb;
|
|
}
|
|
|
|
drawBorder(p, _tx + w - borderRightWidth, startY, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
|
|
ignore_top?0:style->borderTopWidth(),
|
|
ignore_bottom?0:style->borderBottomWidth());
|
|
}
|
|
}
|
|
|
|
void RenderFieldset::setStyle(RenderStyle* _style)
|
|
{
|
|
RenderBlock::setStyle(_style);
|
|
|
|
// WinIE renders fieldsets with display:inline like they're inline-blocks. For us,
|
|
// an inline-block is just a block element with replaced set to true and inline set
|
|
// to true. Ensure that if we ended up being inline that we set our replaced flag
|
|
// so that we're treated like an inline-block.
|
|
if (isInline())
|
|
setReplaced(true);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
RenderFileButton::RenderFileButton(HTMLInputElementImpl *element)
|
|
: RenderFormElement(element)
|
|
{
|
|
FileButtonWidget* w = new FileButtonWidget( view()->widget() );
|
|
|
|
w->setMode(KFile::File | KFile::ExistingOnly);
|
|
w->lineEdit()->setCompletionBox( new CompletionWidget(w) );
|
|
w->completionObject()->setDir(KGlobalSettings::documentPath());
|
|
|
|
connect(w->lineEdit(), SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
|
|
connect(w->lineEdit(), SIGNAL(textChanged(QString)),this,SLOT(slotTextChanged(QString)));
|
|
connect(w, SIGNAL(urlSelected(KUrl)),this,SLOT(slotUrlSelected(KUrl)));
|
|
|
|
setQWidget(w);
|
|
m_haveFocus = false;
|
|
}
|
|
|
|
short RenderFileButton::baselinePosition( bool f ) const
|
|
{
|
|
int bTop = borderTop();
|
|
int bBottom = borderBottom();
|
|
int ret = (height()-paddingTop()-paddingBottom()-bTop-bBottom+1)/2;
|
|
ret += marginTop() + paddingTop() + bTop;
|
|
ret += ((fontMetrics( f ).ascent())/2)-2;
|
|
return ret;
|
|
}
|
|
|
|
void RenderFileButton::calcMinMaxWidth()
|
|
{
|
|
KHTMLAssert( !minMaxKnown() );
|
|
|
|
const QFontMetrics &fm = style()->fontMetrics();
|
|
int size = (element()->size() > 0) ? (element()->size() + 1) : 17; // "some"
|
|
|
|
int h = fm.lineSpacing();
|
|
int w = (fm.height() * size) / 2; // on average a character cell is twice as tall as it is wide
|
|
KLineEdit* edit = widget()->lineEdit();
|
|
|
|
QStyleOptionFrame opt;
|
|
opt.initFrom(edit);
|
|
if (edit->hasFrame())
|
|
opt.lineWidth = edit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, edit);
|
|
|
|
QSize s(w, qMax(h, 14));
|
|
s = edit->style()->sizeFromContents(QStyle::CT_LineEdit, &opt, s, edit);
|
|
s = s.expandedTo(QApplication::globalStrut());
|
|
|
|
QSize bs = widget()->minimumSizeHint() - edit->minimumSizeHint();
|
|
|
|
setIntrinsicWidth( s.width() + bs.width() );
|
|
setIntrinsicHeight( qMax(s.height(), bs.height()) );
|
|
|
|
RenderFormElement::calcMinMaxWidth();
|
|
}
|
|
|
|
void RenderFileButton::handleFocusOut()
|
|
{
|
|
if ( widget()->lineEdit() && widget()->lineEdit()->isModified() ) {
|
|
element()->onChange();
|
|
widget()->lineEdit()->setModified( false );
|
|
}
|
|
}
|
|
|
|
void RenderFileButton::updateFromElement()
|
|
{
|
|
KLineEdit* edit = widget()->lineEdit();
|
|
bool blocked = edit->blockSignals(true);
|
|
edit->setText(element()->value().string());
|
|
edit->blockSignals(blocked);
|
|
edit->setModified(false);
|
|
|
|
RenderFormElement::updateFromElement();
|
|
}
|
|
|
|
void RenderFileButton::slotReturnPressed()
|
|
{
|
|
// don't submit the form when return was pressed in a completion-popup
|
|
KCompletionBox* box = widget()->lineEdit()->completionBox(false);
|
|
if (box && box->isVisible() && box->currentRow() != -1) {
|
|
box->hide();
|
|
return;
|
|
}
|
|
|
|
handleFocusOut();
|
|
|
|
if (element()->form())
|
|
element()->form()->submitFromKeyboard();
|
|
}
|
|
|
|
void RenderFileButton::slotTextChanged(const QString &/*string*/)
|
|
{
|
|
element()->m_value = KUrl( widget()->url() ).pathOrUrl();
|
|
}
|
|
|
|
void RenderFileButton::slotUrlSelected(const KUrl &)
|
|
{
|
|
element()->onChange();
|
|
}
|
|
|
|
void RenderFileButton::select()
|
|
{
|
|
widget()->lineEdit()->selectAll();
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element)
|
|
: RenderFormElement(element)
|
|
{
|
|
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element)
|
|
: RenderBlock(element)
|
|
{
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------
|
|
|
|
bool ListBoxWidget::event( QEvent * event )
|
|
{
|
|
// accept all wheel events so that they are not propagated to the view
|
|
// once either end of the list is reached.
|
|
bool ret = KListWidget::event(event);
|
|
if (event->type() == QEvent::Wheel) {
|
|
event->accept();
|
|
ret = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ComboBoxWidget::ComboBoxWidget(QWidget *parent)
|
|
: KComboBox(false, parent)
|
|
{
|
|
m_kwp->setIsRedirected( true );
|
|
//setAutoMask(true);
|
|
if (view()) view()->installEventFilter(this);
|
|
setMouseTracking(true);
|
|
}
|
|
|
|
void ComboBoxWidget::showPopup()
|
|
{
|
|
QPoint p = pos();
|
|
QPoint dest( p );
|
|
QWidget* parent = parentWidget();
|
|
KHTMLView* v = m_kwp->rootViewPos(dest);
|
|
int zoomLevel = v ? v->zoomLevel() : 100;
|
|
if (zoomLevel != 100) {
|
|
if (v) {
|
|
// we need to place the popup even lower on the screen, take in count the widget is bigger
|
|
// now, so we add also the difference between the original height, and the zoomed height
|
|
dest.setY(dest.y() + (sizeHint().height() * zoomLevel / 100 - sizeHint().height()));
|
|
}
|
|
}
|
|
bool blocked = blockSignals(true);
|
|
if (v != parent) {
|
|
setParent(v);
|
|
}
|
|
move( dest );
|
|
blockSignals(blocked);
|
|
|
|
KComboBox::showPopup();
|
|
|
|
blocked = blockSignals(true);
|
|
if (v != parent) {
|
|
setParent(parent);
|
|
// undo side effect of setParent()
|
|
show();
|
|
}
|
|
move( p );
|
|
blockSignals(blocked);
|
|
}
|
|
|
|
void ComboBoxWidget::hidePopup()
|
|
{
|
|
KComboBox::hidePopup();
|
|
}
|
|
|
|
|
|
bool ComboBoxWidget::event(QEvent *e)
|
|
{
|
|
if (KComboBox::event(e))
|
|
return true;
|
|
if (e->type()==QEvent::KeyPress)
|
|
{
|
|
QKeyEvent *ke = static_cast<QKeyEvent *>(e);
|
|
switch(ke->key())
|
|
{
|
|
case Qt::Key_Return:
|
|
case Qt::Key_Enter:
|
|
showPopup();
|
|
ke->accept();
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ComboBoxWidget::eventFilter(QObject *dest, QEvent *e)
|
|
{
|
|
if (dest==view() && e->type()==QEvent::KeyPress)
|
|
{
|
|
QKeyEvent *ke = static_cast<QKeyEvent *>(e);
|
|
bool forward = false;
|
|
switch(ke->key())
|
|
{
|
|
case Qt::Key_Tab:
|
|
forward=true;
|
|
// fall through
|
|
case Qt::Key_Backtab:
|
|
// ugly hack. emulate popdownlistbox() (private in QComboBox)
|
|
// we re-use ke here to store the reference to the generated event.
|
|
ke = new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier);
|
|
QApplication::sendEvent(dest,ke);
|
|
focusNextPrevChild(forward);
|
|
delete ke;
|
|
return true;
|
|
default:
|
|
return KComboBox::eventFilter(dest, e);
|
|
}
|
|
}
|
|
return KComboBox::eventFilter(dest, e);
|
|
}
|
|
|
|
void ComboBoxWidget::keyPressEvent(QKeyEvent *e)
|
|
{
|
|
// Normally, widgets are not sent Tab keys this way in the first
|
|
// place as they are handled by QWidget::event() for focus handling
|
|
// already. But we get our events via EventPropagator::sendEvent()
|
|
// directly. Ignore them so that HTMLGenericFormElementImpl::
|
|
// defaultEventHandler() can call focusNextPrev().
|
|
if (e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab) {
|
|
e->ignore();
|
|
return;
|
|
}
|
|
KComboBox::keyPressEvent(e);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
RenderSelect::RenderSelect(HTMLSelectElementImpl *element)
|
|
: RenderFormElement(element)
|
|
{
|
|
m_ignoreSelectEvents = false;
|
|
m_multiple = element->multiple();
|
|
m_size = element->size();
|
|
m_useListBox = (m_multiple || m_size > 1);
|
|
m_selectionChanged = true;
|
|
m_optionsChanged = true;
|
|
|
|
if(m_useListBox)
|
|
setQWidget(createListBox());
|
|
else {
|
|
setQWidget(createComboBox());
|
|
getProxyStyle(); // We always need it to make sure popups are big enough
|
|
}
|
|
}
|
|
|
|
void RenderSelect::clearItemFlags(int index, Qt::ItemFlags flags)
|
|
{
|
|
if(m_useListBox) {
|
|
QListWidgetItem* item = static_cast<KListWidget*>(m_widget)->item(index);
|
|
item->setFlags(item->flags() & ~flags);
|
|
} else {
|
|
KComboBox* combo = static_cast<KComboBox*>(m_widget);
|
|
if (QStandardItemModel* model = qobject_cast<QStandardItemModel*>(combo->model())) {
|
|
QStandardItem* item = model->item(index);
|
|
item->setFlags(item->flags() & ~flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RenderSelect::setStyle(RenderStyle *_style)
|
|
{
|
|
RenderFormElement::setStyle(_style);
|
|
if (!m_useListBox) {
|
|
KHTMLProxyStyle* proxyStyle = static_cast<KHTMLProxyStyle*>(getProxyStyle());
|
|
proxyStyle->noBorder = shouldDisableNativeBorders();
|
|
}
|
|
}
|
|
|
|
void RenderSelect::updateFromElement()
|
|
{
|
|
m_ignoreSelectEvents = true;
|
|
|
|
// change widget type
|
|
bool oldMultiple = m_multiple;
|
|
unsigned oldSize = m_size;
|
|
bool oldListbox = m_useListBox;
|
|
|
|
m_multiple = element()->multiple();
|
|
m_size = element()->size();
|
|
m_useListBox = (m_multiple || m_size > 1);
|
|
|
|
if (oldMultiple != m_multiple || oldSize != m_size) {
|
|
if (m_useListBox != oldListbox) {
|
|
// type of select has changed
|
|
if (m_useListBox)
|
|
setQWidget(createListBox());
|
|
else
|
|
setQWidget(createComboBox());
|
|
|
|
// Call setStyle() to fix unwanted font size change (#142722)
|
|
// and to update our proxy style properties
|
|
setStyle(style());
|
|
}
|
|
|
|
if (m_useListBox && oldMultiple != m_multiple) {
|
|
static_cast<KListWidget*>(m_widget)->setSelectionMode(m_multiple ?
|
|
QListWidget::ExtendedSelection
|
|
: QListWidget::SingleSelection);
|
|
}
|
|
m_selectionChanged = true;
|
|
m_optionsChanged = true;
|
|
}
|
|
|
|
// update contents listbox/combobox based on options in m_element
|
|
if ( m_optionsChanged ) {
|
|
if (element()->m_recalcListItems)
|
|
element()->recalcListItems();
|
|
const QVector<HTMLGenericFormElementImpl*> listItems = element()->listItems();
|
|
int listIndex;
|
|
|
|
if(m_useListBox)
|
|
static_cast<KListWidget*>(m_widget)->clear();
|
|
else
|
|
static_cast<KComboBox*>(m_widget)->clear();
|
|
|
|
for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) {
|
|
if (listItems[listIndex]->id() == ID_OPTGROUP) {
|
|
DOMString text = listItems[listIndex]->getAttribute(ATTR_LABEL);
|
|
if (text.isNull())
|
|
text = "";
|
|
|
|
text = text.implementation()->collapseWhiteSpace(false, false);
|
|
|
|
if(m_useListBox) {
|
|
QListWidgetItem *item = new QListWidgetItem(QString(text.implementation()->s, text.implementation()->l));
|
|
static_cast<KListWidget*>(m_widget)->insertItem(listIndex,item);
|
|
} else {
|
|
static_cast<KComboBox*>(m_widget)->insertItem(listIndex, QString(text.implementation()->s, text.implementation()->l));
|
|
}
|
|
|
|
bool disabled = !listItems[listIndex]->getAttribute(ATTR_DISABLED).isNull();
|
|
if (disabled)
|
|
clearItemFlags(listIndex, Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
else
|
|
clearItemFlags(listIndex, Qt::ItemIsSelectable);
|
|
}
|
|
else if (listItems[listIndex]->id() == ID_OPTION) {
|
|
HTMLOptionElementImpl* optElem = static_cast<HTMLOptionElementImpl*>(listItems[listIndex]);
|
|
|
|
DOMString domText = optElem->text();
|
|
// Prefer label if set
|
|
DOMString label = optElem->getAttribute(ATTR_LABEL);
|
|
if (!label.isEmpty())
|
|
domText = label;
|
|
domText = domText.implementation()->collapseWhiteSpace(false, false);
|
|
|
|
QString text;
|
|
|
|
ElementImpl* parentOptGroup = optElem->parentNode()->id() == ID_OPTGROUP ?
|
|
static_cast<ElementImpl*>(optElem->parentNode()) : 0;
|
|
|
|
if (parentOptGroup) {
|
|
text = QLatin1String(" ") + domText.string();
|
|
} else {
|
|
text = domText.string();
|
|
}
|
|
|
|
if(m_useListBox)
|
|
static_cast<KListWidget*>(m_widget)->insertItem(listIndex,text);
|
|
else
|
|
static_cast<KComboBox*>(m_widget)->insertItem(listIndex, text);
|
|
|
|
bool disabled = !optElem->getAttribute(ATTR_DISABLED).isNull();
|
|
if (parentOptGroup)
|
|
disabled = disabled || !parentOptGroup->getAttribute(ATTR_DISABLED).isNull();
|
|
|
|
if (disabled)
|
|
clearItemFlags(listIndex, Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
}
|
|
else {
|
|
KHTMLAssert(false);
|
|
}
|
|
|
|
m_selectionChanged = true;
|
|
}
|
|
|
|
// QComboBox caches the size hint unless you call setFont (ref: TT docu)
|
|
if(!m_useListBox) {
|
|
KComboBox *that = static_cast<KComboBox*>(m_widget);
|
|
that->setFont( that->font() );
|
|
}
|
|
setNeedsLayoutAndMinMaxRecalc();
|
|
m_optionsChanged = false;
|
|
}
|
|
|
|
// update selection
|
|
if (m_selectionChanged) {
|
|
updateSelection();
|
|
}
|
|
|
|
m_ignoreSelectEvents = false;
|
|
|
|
RenderFormElement::updateFromElement();
|
|
}
|
|
|
|
short RenderSelect::baselinePosition( bool f ) const
|
|
{
|
|
if (m_useListBox)
|
|
return RenderFormElement::baselinePosition(f);
|
|
|
|
int bTop = shouldDisableNativeBorders() ? borderTop() : 0;
|
|
int bBottom = shouldDisableNativeBorders() ? borderBottom() : 0;
|
|
int ret = (height()-RenderWidget::paddingTop()-RenderWidget::paddingBottom()-bTop-bBottom+1)/2;
|
|
ret += marginTop() + RenderWidget::paddingTop() + bTop;
|
|
ret += ((fontMetrics( f ).ascent())/2)-2;
|
|
return ret;
|
|
}
|
|
|
|
void RenderSelect::calcMinMaxWidth()
|
|
{
|
|
KHTMLAssert( !minMaxKnown() );
|
|
|
|
if (m_optionsChanged)
|
|
updateFromElement();
|
|
|
|
// ### ugly HACK FIXME!!!
|
|
setMinMaxKnown();
|
|
layoutIfNeeded();
|
|
setNeedsLayoutAndMinMaxRecalc();
|
|
// ### end FIXME
|
|
|
|
RenderFormElement::calcMinMaxWidth();
|
|
}
|
|
|
|
void RenderSelect::layout( )
|
|
{
|
|
KHTMLAssert(needsLayout());
|
|
KHTMLAssert(minMaxKnown());
|
|
|
|
// ### maintain selection properly between type/size changes, and work
|
|
// out how to handle multiselect->singleselect (probably just select
|
|
// first selected one)
|
|
|
|
// calculate size
|
|
if(m_useListBox) {
|
|
KListWidget* w = static_cast<KListWidget*>(m_widget);
|
|
|
|
int width = 0;
|
|
int height = 0;
|
|
|
|
QAbstractItemModel *m = w->model();
|
|
QAbstractItemDelegate *d = w->itemDelegate();
|
|
QStyleOptionViewItem so;
|
|
so.font = w->font();
|
|
|
|
for ( int rowIndex = 0 ; rowIndex < w->count() ; rowIndex++ ) {
|
|
QModelIndex mi = m->index(rowIndex, 0);
|
|
QSize s = d->sizeHint( so, mi);
|
|
width = qMax(width, s.width());
|
|
height = qMax(height, s.height());
|
|
}
|
|
|
|
if ( !height )
|
|
height = w->fontMetrics().height();
|
|
if ( !width )
|
|
width = w->fontMetrics().width( 'x' );
|
|
|
|
int size = m_size;
|
|
// check if multiple and size was not given or invalid
|
|
// Internet Exploder sets size to qMin(number of elements, 4)
|
|
// Netscape seems to simply set it to "number of elements"
|
|
// the average of that is IMHO qMin(number of elements, 10)
|
|
// so I did that ;-)
|
|
if(size < 1)
|
|
size = qMin(w->count(), 10);
|
|
|
|
QStyleOptionFrameV3 opt;
|
|
opt.initFrom(w);
|
|
opt.lineWidth = w->lineWidth();
|
|
opt.midLineWidth = w->midLineWidth();
|
|
opt.frameShape = w->frameShape();
|
|
QRect r = w->style()->subElementRect(QStyle::SE_ShapedFrameContents, &opt, w);
|
|
QRect o = opt.rect;
|
|
int hfw = (r.left()-o.left()) + (o.right()-r.right());
|
|
int vfw = (r.top()-o.top()) + (o.bottom()-r.bottom());
|
|
|
|
width += hfw + w->verticalScrollBar()->sizeHint().width();
|
|
// FIXME BASELINE: the 3 lines below could be removed.
|
|
int lhs = m_widget->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
|
|
if (lhs>0)
|
|
width += lhs;
|
|
height = size*height + vfw;
|
|
|
|
assert( includesPadding() );
|
|
width -= RenderWidget::paddingLeft() + RenderWidget::paddingRight();
|
|
height -= RenderWidget::paddingTop() + RenderWidget::paddingBottom();
|
|
|
|
setIntrinsicWidth( width );
|
|
setIntrinsicHeight( height );
|
|
}
|
|
else {
|
|
QSize s(m_widget->sizeHint());
|
|
int w = s.width();
|
|
int h = s.height();
|
|
|
|
if (shouldDisableNativeBorders()) {
|
|
const int dfw = 2 * m_widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, m_widget);
|
|
w -= dfw;
|
|
h -= dfw;
|
|
}
|
|
|
|
setIntrinsicWidth(w);
|
|
setIntrinsicHeight(h);
|
|
}
|
|
|
|
/// uuh, ignore the following line..
|
|
setNeedsLayout(true);
|
|
RenderFormElement::layout();
|
|
|
|
// and now disable the widget in case there is no <option> given
|
|
const QVector<HTMLGenericFormElementImpl*> listItems = element()->listItems();
|
|
|
|
bool foundOption = false;
|
|
for (int i = 0; i < listItems.size() && !foundOption; i++)
|
|
foundOption = (listItems[i]->id() == ID_OPTION);
|
|
|
|
m_widget->setEnabled(foundOption && ! element()->disabled());
|
|
}
|
|
|
|
void RenderSelect::slotSelected(int index) // emitted by the combobox only
|
|
{
|
|
if ( m_ignoreSelectEvents ) return;
|
|
|
|
KHTMLAssert( !m_useListBox );
|
|
|
|
const QVector<HTMLGenericFormElementImpl*> listItems = element()->listItems();
|
|
if(index >= 0 && index < int(listItems.size()))
|
|
{
|
|
bool found = ( listItems[index]->id() == ID_OPTION );
|
|
|
|
if ( !found ) {
|
|
// this one is not selectable, we need to find an option element
|
|
while ( index < listItems.size() ) {
|
|
if ( listItems[index]->id() == ID_OPTION ) {
|
|
found = true;
|
|
break;
|
|
}
|
|
++index;
|
|
}
|
|
|
|
if ( !found ) {
|
|
while ( index >= 0 ) {
|
|
if ( listItems[index]->id() == ID_OPTION ) {
|
|
found = true;
|
|
break;
|
|
}
|
|
--index;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( found ) {
|
|
bool changed = false;
|
|
|
|
for ( int i = 0; i < listItems.size(); ++i )
|
|
if ( listItems[i]->id() == ID_OPTION && i != index )
|
|
{
|
|
HTMLOptionElementImpl* opt = static_cast<HTMLOptionElementImpl*>( listItems[i] );
|
|
changed |= (opt->m_selected == true);
|
|
opt->m_selected = false;
|
|
}
|
|
|
|
HTMLOptionElementImpl* opt = static_cast<HTMLOptionElementImpl*>(listItems[index]);
|
|
changed |= (opt->m_selected == false);
|
|
opt->m_selected = true;
|
|
|
|
if ( index != static_cast<ComboBoxWidget*>( m_widget )->currentIndex() )
|
|
static_cast<ComboBoxWidget*>( m_widget )->setCurrentIndex( index );
|
|
|
|
// When selecting an optgroup item, and we move forward to we
|
|
// shouldn't emit onChange. Hence this bool, the if above doesn't do it.
|
|
if ( changed )
|
|
{
|
|
ref();
|
|
element()->onChange();
|
|
deref();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void RenderSelect::slotSelectionChanged() // emitted by the listbox only
|
|
{
|
|
if ( m_ignoreSelectEvents ) return;
|
|
|
|
// don't use listItems() here as we have to avoid recalculations - changing the
|
|
// option list will make use update options not in the way the user expects them
|
|
const QVector<HTMLGenericFormElementImpl*> listItems = element()->m_listItems;
|
|
for ( int i = 0; i < listItems.count(); i++ )
|
|
// don't use setSelected() here because it will cause us to be called
|
|
// again with updateSelection.
|
|
if ( listItems[i]->id() == ID_OPTION )
|
|
static_cast<HTMLOptionElementImpl*>( listItems[i] )
|
|
->m_selected = static_cast<KListWidget*>( m_widget )->item(i)->isSelected();
|
|
|
|
ref();
|
|
element()->onChange();
|
|
deref();
|
|
}
|
|
|
|
void RenderSelect::setOptionsChanged(bool _optionsChanged)
|
|
{
|
|
m_optionsChanged = _optionsChanged;
|
|
}
|
|
|
|
void RenderSelect::setPadding()
|
|
{
|
|
RenderFormElement::setPadding();
|
|
}
|
|
|
|
ListBoxWidget* RenderSelect::createListBox()
|
|
{
|
|
ListBoxWidget *lb = new ListBoxWidget(view()->widget());
|
|
lb->setSelectionMode(m_multiple ? QListWidget::ExtendedSelection : QListWidget::SingleSelection);
|
|
connect( lb, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged()) );
|
|
m_ignoreSelectEvents = false;
|
|
lb->setMouseTracking(true);
|
|
|
|
return lb;
|
|
}
|
|
|
|
ComboBoxWidget *RenderSelect::createComboBox()
|
|
{
|
|
ComboBoxWidget *cb = new ComboBoxWidget(view()->widget());
|
|
connect(cb, SIGNAL(activated(int)), this, SLOT(slotSelected(int)));
|
|
return cb;
|
|
}
|
|
|
|
void RenderSelect::updateSelection()
|
|
{
|
|
const QVector<HTMLGenericFormElementImpl*> listItems = element()->listItems();
|
|
int i;
|
|
if (m_useListBox) {
|
|
// if multi-select, we select only the new selected index
|
|
KListWidget *listBox = static_cast<KListWidget*>(m_widget);
|
|
for (i = 0; i < int(listItems.size()); i++)
|
|
listBox->item(i)->setSelected(listItems[i]->id() == ID_OPTION &&
|
|
static_cast<HTMLOptionElementImpl*>(listItems[i])->selectedBit());
|
|
}
|
|
else {
|
|
bool found = false;
|
|
int firstOption = i = listItems.size();
|
|
while (i--)
|
|
if (listItems[i]->id() == ID_OPTION) {
|
|
if (found)
|
|
static_cast<HTMLOptionElementImpl*>(listItems[i])->m_selected = false;
|
|
else if (static_cast<HTMLOptionElementImpl*>(listItems[i])->selectedBit()) {
|
|
static_cast<KComboBox*>( m_widget )->setCurrentIndex(i);
|
|
found = true;
|
|
}
|
|
firstOption = i;
|
|
}
|
|
|
|
if (!found && firstOption != listItems.size()) {
|
|
// select first option (IE7/Gecko behaviour)
|
|
static_cast<HTMLOptionElementImpl*>(listItems[firstOption])->m_selected = true;
|
|
static_cast<KComboBox*>( m_widget )->setCurrentIndex(firstOption);
|
|
}
|
|
}
|
|
|
|
m_selectionChanged = false;
|
|
}
|
|
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
TextAreaWidget::TextAreaWidget(int wrap, QWidget* parent)
|
|
: KTextEdit(parent)
|
|
{
|
|
m_kwp->setIsRedirected( true );
|
|
|
|
if(wrap != DOM::HTMLTextAreaElementImpl::ta_NoWrap)
|
|
setLineWrapMode(QTextEdit::WidgetWidth);
|
|
else
|
|
setLineWrapMode(QTextEdit::NoWrap);
|
|
|
|
setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
|
|
setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded );
|
|
|
|
KCursor::setAutoHideCursor(viewport(), true);
|
|
setAcceptRichText (false);
|
|
setMouseTracking(true);
|
|
}
|
|
|
|
TextAreaWidget::~TextAreaWidget()
|
|
{
|
|
}
|
|
|
|
void TextAreaWidget::scrollContentsBy( int dx, int dy )
|
|
{
|
|
KTextEdit::scrollContentsBy(dx, dy);
|
|
update();
|
|
}
|
|
|
|
bool TextAreaWidget::event( QEvent *e )
|
|
{
|
|
#if 0
|
|
if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
|
|
QKeyEvent* ke = (QKeyEvent*) e;
|
|
if ( ke->modifiers() & Qt::ControlModifier ) {
|
|
switch ( ke->key() ) {
|
|
case Qt::Key_Left:
|
|
case Qt::Key_Right:
|
|
case Qt::Key_Up:
|
|
case Qt::Key_Down:
|
|
case Qt::Key_Home:
|
|
case Qt::Key_End:
|
|
ke->accept();
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
// accept all wheel events so that they are not propagated to the view
|
|
// once either end of the widget is reached.
|
|
bool ret = KTextEdit::event(e);
|
|
if (e->type() == QEvent::Wheel) {
|
|
e->accept();
|
|
ret = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void TextAreaWidget::keyPressEvent(QKeyEvent *e)
|
|
{
|
|
// The ComboBoxWidget::keyPressEvent() comment about having to
|
|
// deal with events coming from EventPropagator::sendEvent()
|
|
// directly applies here, too.
|
|
if ((e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab) &&
|
|
tabChangesFocus()) {
|
|
e->ignore();
|
|
return;
|
|
}
|
|
KTextEdit::keyPressEvent(e);
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
|
|
: RenderFormElement(element)
|
|
{
|
|
TextAreaWidget *edit = new TextAreaWidget(element->wrap(), view());
|
|
setQWidget(edit);
|
|
const KHTMLSettings *settings = view()->part()->settings();
|
|
edit->setCheckSpellingEnabled( settings->autoSpellCheck() );
|
|
edit->setTabChangesFocus( ! settings->allowTabulation() );
|
|
|
|
connect(edit,SIGNAL(textChanged()),this,SLOT(slotTextChanged()));
|
|
|
|
setText(element->value().string());
|
|
m_textAlignment = edit->alignment();
|
|
}
|
|
|
|
RenderTextArea::~RenderTextArea()
|
|
{
|
|
element()->m_value = text();
|
|
}
|
|
|
|
void RenderTextArea::handleFocusOut()
|
|
{
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
|
|
if ( w && element()->m_changed ) {
|
|
element()->m_changed = false;
|
|
element()->onChange();
|
|
}
|
|
}
|
|
|
|
void RenderTextArea::calcMinMaxWidth()
|
|
{
|
|
KHTMLAssert( !minMaxKnown() );
|
|
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
const QFontMetrics &m = style()->fontMetrics();
|
|
w->setTabStopWidth(8 * m.width(" "));
|
|
int lvs = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing));
|
|
int lhs = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing));
|
|
int llm = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutLeftMargin));
|
|
int lrm = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutRightMargin));
|
|
int lbm = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
|
|
int ltm = qMax(0, w->style()->pixelMetric(QStyle::PM_LayoutTopMargin));
|
|
|
|
QStyleOptionFrameV3 opt;
|
|
opt.initFrom(w);
|
|
opt.lineWidth = w->lineWidth();
|
|
opt.midLineWidth = w->midLineWidth();
|
|
opt.frameShape = w->frameShape();
|
|
QRect r = w->style()->subElementRect(QStyle::SE_ShapedFrameContents, &opt, w);
|
|
QRect o = opt.rect;
|
|
int hfw = (r.left()-o.left()) + (o.right()-r.right());
|
|
int vfw = (r.top()-o.top()) + (o.bottom()-r.bottom());
|
|
|
|
QSize size( qMax(element()->cols(), 1L)*m.width('x') + hfw + llm+lrm +
|
|
w->verticalScrollBar()->sizeHint().width()+lhs,
|
|
qMax(element()->rows(), 1L)*m.lineSpacing() + vfw + lbm+ltm +
|
|
(w->lineWrapMode() == QTextEdit::NoWrap ?
|
|
w->horizontalScrollBar()->sizeHint().height()+lvs : 0)
|
|
);
|
|
|
|
assert( includesPadding() );
|
|
size.rwidth() -= RenderWidget::paddingLeft() + RenderWidget::paddingRight();
|
|
size.rheight() -= RenderWidget::paddingTop() + RenderWidget::paddingBottom();
|
|
|
|
setIntrinsicWidth( size.width() );
|
|
setIntrinsicHeight( size.height() );
|
|
|
|
RenderFormElement::calcMinMaxWidth();
|
|
}
|
|
|
|
void RenderTextArea::setStyle(RenderStyle* _style)
|
|
{
|
|
RenderFormElement::setStyle(_style);
|
|
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
|
|
if (m_textAlignment != textAlignment()) {
|
|
m_textAlignment = textAlignment();
|
|
bool unsubmittedFormChange = element()->m_unsubmittedFormChange;
|
|
bool blocked = w->blockSignals(true);
|
|
int cx = w->horizontalScrollBar()->value();
|
|
int cy = w->verticalScrollBar()->value();
|
|
QTextCursor tc = w->textCursor();
|
|
// Set alignment on all textarea's paragraphs
|
|
w->selectAll();
|
|
w->setAlignment(m_textAlignment);
|
|
w->setTextCursor(tc);
|
|
w->horizontalScrollBar()->setValue(cx);
|
|
w->verticalScrollBar()->setValue(cy);
|
|
w->blockSignals(blocked);
|
|
element()->m_unsubmittedFormChange = unsubmittedFormChange;
|
|
}
|
|
|
|
if (style()->overflowX() == OSCROLL)
|
|
w->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
|
|
else if (style()->overflowX() == OHIDDEN)
|
|
w->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
|
|
else
|
|
w->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
|
|
if (style()->overflowY() == OSCROLL)
|
|
w->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
|
|
else if(style()->overflowY() == OHIDDEN)
|
|
w->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
|
|
else
|
|
w->setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded );
|
|
}
|
|
|
|
short RenderTextArea::scrollWidth() const
|
|
{
|
|
return RenderObject::scrollWidth();
|
|
}
|
|
|
|
int RenderTextArea::scrollHeight() const
|
|
{
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
int contentHeight = qRound(w->document()->size().height());
|
|
return qMax(contentHeight, RenderObject::clientHeight());
|
|
}
|
|
|
|
void RenderTextArea::setText(const QString& newText)
|
|
{
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
|
|
// When this is called, m_value in the element must have just
|
|
// been set to new value --- see if we have any work to do
|
|
|
|
QString oldText = text();
|
|
int oldTextLen = oldText.length();
|
|
int newTextLen = newText.length();
|
|
if (newTextLen != oldTextLen || newText != oldText) {
|
|
bool blocked = w->blockSignals(true);
|
|
int cx = w->horizontalScrollBar()->value();
|
|
int cy = w->verticalScrollBar()->value();
|
|
// Not using setPlaintext as it resets text alignment property
|
|
int minLen = qMin(newTextLen, oldTextLen);
|
|
int ex = 0;
|
|
while (ex < minLen && (newText.at(ex) == oldText.at(ex)))
|
|
++ex;
|
|
QTextCursor tc = w->textCursor();
|
|
tc.setPosition(ex, QTextCursor::MoveAnchor);
|
|
tc.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
|
|
tc.insertText(newText.right(newTextLen - ex));
|
|
|
|
if (oldTextLen == 0)
|
|
tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
|
else
|
|
tc.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
|
|
w->setTextCursor(tc);
|
|
w->horizontalScrollBar()->setValue(cx);
|
|
w->verticalScrollBar()->setValue(cy);
|
|
w->blockSignals(blocked);
|
|
}
|
|
}
|
|
|
|
void RenderTextArea::updateFromElement()
|
|
{
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
w->setReadOnly(element()->readOnly());
|
|
w->setClickMessage(element()->placeholder().string());
|
|
RenderFormElement::updateFromElement();
|
|
}
|
|
|
|
QString RenderTextArea::text()
|
|
{
|
|
// ### We may want to cache this when physical, since the DOM no longer caches,
|
|
// but seeing how text() has always been called on textChanged(), it's probably not needed
|
|
|
|
QString txt;
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
#ifdef __GNUC__
|
|
#warning "Physical wrap mode needs testing (also in ::selection*)"
|
|
#endif
|
|
if (element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical) {
|
|
QTextCursor tc(w->document());
|
|
while (!tc.atEnd()) {
|
|
tc.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
|
|
txt += tc.selectedText();
|
|
if (tc.movePosition(QTextCursor::Right)) {
|
|
txt += QLatin1String("\n");
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
txt = w->toPlainText();
|
|
return txt;
|
|
}
|
|
|
|
void RenderTextArea::highLightWord( unsigned int length, unsigned int pos )
|
|
{
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
if ( w )
|
|
w->highlightWord( length, pos );
|
|
}
|
|
|
|
void RenderTextArea::slotTextChanged()
|
|
{
|
|
element()->m_changed = true;
|
|
if (element()->m_value != text())
|
|
element()->m_unsubmittedFormChange = true;
|
|
}
|
|
|
|
void RenderTextArea::select()
|
|
{
|
|
static_cast<TextAreaWidget *>(m_widget)->selectAll();
|
|
}
|
|
|
|
long RenderTextArea::selectionStart()
|
|
{
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
return w->textCursor().selectionStart();
|
|
}
|
|
|
|
long RenderTextArea::selectionEnd()
|
|
{
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
return w->textCursor().selectionEnd();
|
|
}
|
|
|
|
static void setPhysWrapPos(QTextCursor& otc, bool selStart, int idx)
|
|
{
|
|
QTextCursor tc = otc;
|
|
tc.setPosition(0);
|
|
tc.movePosition(QTextCursor::EndOfLine);
|
|
while (!tc.atEnd()) {
|
|
if (tc.movePosition(QTextCursor::Down) && tc.position()< idx)
|
|
--idx;
|
|
if (tc.position() >= idx)
|
|
break;
|
|
}
|
|
otc.setPosition(idx, selStart ? QTextCursor::MoveAnchor : QTextCursor::KeepAnchor );
|
|
}
|
|
|
|
void RenderTextArea::setSelectionStart(long offset) {
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
QTextCursor tc = w->textCursor();
|
|
if (element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical)
|
|
setPhysWrapPos(tc, true /*selStart*/, offset);
|
|
else
|
|
tc.setPosition(offset);
|
|
w->setTextCursor(tc);
|
|
}
|
|
|
|
void RenderTextArea::setSelectionEnd(long offset) {
|
|
TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
|
|
QTextCursor tc = w->textCursor();
|
|
if (element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical)
|
|
setPhysWrapPos(tc, false /*selStart*/, offset);
|
|
else
|
|
tc.setPosition(offset, QTextCursor::KeepAnchor);
|
|
w->setTextCursor(tc);
|
|
}
|
|
|
|
void RenderTextArea::setSelectionRange(long start, long end) {
|
|
setSelectionStart(start);
|
|
setSelectionEnd(end);
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#include "moc_render_form.cpp"
|
|
// kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on;
|