kdelibs/khtml/rendering/render_form.cpp
Ivailo Monev 39f1e04295 generic: add back khtml and kjs with some changes
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2015-11-09 23:23:53 +02:00

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;