summaryrefslogtreecommitdiffstats
path: root/khtml/rendering/render_form.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'khtml/rendering/render_form.cpp')
-rw-r--r--khtml/rendering/render_form.cpp1898
1 files changed, 1898 insertions, 0 deletions
diff --git a/khtml/rendering/render_form.cpp b/khtml/rendering/render_form.cpp
new file mode 100644
index 000000000..f8daba1e3
--- /dev/null
+++ b/khtml/rendering/render_form.cpp
@@ -0,0 +1,1898 @@
+/*
+ * 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)
+ *
+ * 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 <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 <kspell.h>
+#include <kurlcompletion.h>
+#include <kwin.h>
+
+#include <qstyle.h>
+
+#include "misc/helper.h"
+#include "xml/dom2_eventsimpl.h"
+#include "html/html_formimpl.h"
+#include "misc/htmlhashes.h"
+
+#include "rendering/render_form.h"
+#include <assert.h>
+
+#include "khtmlview.h"
+#include "khtml_ext.h"
+#include "xml/dom_docimpl.h"
+
+#include <qpopupmenu.h>
+#include <qbitmap.h>
+
+using namespace khtml;
+
+RenderFormElement::RenderFormElement(HTMLGenericFormElementImpl *element)
+ : RenderWidget(element)
+{
+ // init RenderObject attributes
+ setInline(true); // our object is Inline
+
+ m_state = 0;
+}
+
+RenderFormElement::~RenderFormElement()
+{
+}
+
+short RenderFormElement::baselinePosition( bool f ) const
+{
+ return RenderWidget::baselinePosition( f ) - 2 - style()->fontMetrics().descent();
+}
+
+void RenderFormElement::updateFromElement()
+{
+ m_widget->setEnabled(!element()->disabled());
+ RenderWidget::updateFromElement();
+}
+
+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);
+}
+
+Qt::AlignmentFlags RenderFormElement::textAlignment() const
+{
+ switch (style()->textAlign()) {
+ case LEFT:
+ case KHTML_LEFT:
+ return AlignLeft;
+ case RIGHT:
+ case KHTML_RIGHT:
+ return AlignRight;
+ case CENTER:
+ case KHTML_CENTER:
+ return AlignHCenter;
+ case JUSTIFY:
+ // Just fall into the auto code for justify.
+ case TAAUTO:
+ return style()->direction() == RTL ? AlignRight : AlignLeft;
+ }
+ assert(false); // Should never be reached.
+ return AlignLeft;
+}
+
+// -------------------------------------------------------------------------
+
+RenderButton::RenderButton(HTMLGenericFormElementImpl *element)
+ : RenderFormElement(element)
+{
+}
+
+short RenderButton::baselinePosition( bool f ) const
+{
+ return RenderWidget::baselinePosition( f ) - 2;
+}
+
+// -------------------------------------------------------------------------------
+
+
+RenderCheckBox::RenderCheckBox(HTMLInputElementImpl *element)
+ : RenderButton(element)
+{
+ QCheckBox* b = new QCheckBox(view()->viewport(), "__khtml");
+ 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)));
+}
+
+
+void RenderCheckBox::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ QCheckBox *cb = static_cast<QCheckBox *>( m_widget );
+ QSize s( cb->style().pixelMetric( QStyle::PM_IndicatorWidth ),
+ cb->style().pixelMetric( QStyle::PM_IndicatorHeight ) );
+ setIntrinsicWidth( s.width() );
+ setIntrinsicHeight( s.height() );
+
+ RenderButton::calcMinMaxWidth();
+}
+
+void RenderCheckBox::updateFromElement()
+{
+ widget()->setChecked(element()->checked());
+
+ RenderButton::updateFromElement();
+}
+
+void RenderCheckBox::slotStateChanged(int state)
+{
+ element()->setChecked(state == QButton::On);
+ element()->setIndeterminate(state == QButton::NoChange);
+
+ ref();
+ element()->onChange();
+ deref();
+}
+
+// -------------------------------------------------------------------------------
+
+RenderRadioButton::RenderRadioButton(HTMLInputElementImpl *element)
+ : RenderButton(element)
+{
+ QRadioButton* b = new QRadioButton(view()->viewport(), "__khtml");
+ b->setMouseTracking(true);
+ setQWidget(b);
+
+ // prevent firing toggled() signals on initialization
+ b->setChecked(element->checked());
+
+ connect(b,SIGNAL(toggled(bool)),this,SLOT(slotToggled(bool)));
+}
+
+void RenderRadioButton::updateFromElement()
+{
+ widget()->setChecked(element()->checked());
+
+ RenderButton::updateFromElement();
+}
+
+void RenderRadioButton::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ QRadioButton *rb = static_cast<QRadioButton *>( m_widget );
+ QSize s( rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorWidth ),
+ rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorHeight ) );
+ setIntrinsicWidth( s.width() );
+ setIntrinsicHeight( s.height() );
+
+ RenderButton::calcMinMaxWidth();
+}
+
+void RenderRadioButton::slotToggled(bool activated)
+{
+ if(activated) {
+ ref();
+ element()->onChange();
+ deref();
+ }
+}
+
+// -------------------------------------------------------------------------------
+
+
+RenderSubmitButton::RenderSubmitButton(HTMLInputElementImpl *element)
+ : RenderButton(element)
+{
+ QPushButton* p = new QPushButton(view()->viewport(), "__khtml");
+ setQWidget(p);
+ p->setAutoMask(true);
+ p->setMouseTracking(true);
+}
+
+QString RenderSubmitButton::rawText()
+{
+ QString value = element()->valueWithDefault().string();
+ value = value.stripWhiteSpace();
+ QString raw;
+ for(unsigned int i = 0; i < value.length(); i++) {
+ raw += value[i];
+ if(value[i] == '&')
+ raw += '&';
+ }
+ return raw;
+}
+
+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 = QString::fromLatin1("X");
+ QFontMetrics fm = pb->fontMetrics();
+ QSize ts = fm.size( ShowPrefix, raw);
+ QSize s(pb->style().sizeFromContents( QStyle::CT_PushButton, pb, ts )
+ .expandedTo(QApplication::globalStrut()));
+ int margin = pb->style().pixelMetric( QStyle::PM_ButtonMargin, pb) +
+ pb->style().pixelMetric( QStyle::PM_DefaultFrameWidth, pb ) * 2;
+ int w = ts.width() + margin;
+ int h = s.height();
+ if (pb->isDefault() || pb->autoDefault()) {
+ int dbw = pb->style().pixelMetric( QStyle::PM_ButtonDefaultIndicator, pb ) * 2;
+ w += dbw;
+ }
+
+ // add 30% margins to the width (heuristics to make it look similar to IE)
+ s = QSize( w*13/10, 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
+{
+ return RenderFormElement::baselinePosition( f );
+}
+
+// -------------------------------------------------------------------------------
+
+RenderResetButton::RenderResetButton(HTMLInputElementImpl *element)
+ : RenderSubmitButton(element)
+{
+}
+
+// -------------------------------------------------------------------------------
+
+LineEditWidget::LineEditWidget(DOM::HTMLInputElementImpl* input, KHTMLView* view, QWidget* parent)
+ : KLineEdit(parent, "__khtml"), m_input(input), m_view(view), m_spell(0)
+{
+ setMouseTracking(true);
+ KActionCollection *ac = new KActionCollection(this);
+ m_spellAction = KStdAction::spelling( this, SLOT( slotCheckSpelling() ), ac );
+}
+
+LineEditWidget::~LineEditWidget()
+{
+ delete m_spell;
+ m_spell = 0L;
+}
+
+void LineEditWidget::slotCheckSpelling()
+{
+ if ( text().isEmpty() ) {
+ return;
+ }
+
+ delete m_spell;
+ m_spell = new KSpell( this, i18n( "Spell Checking" ), this, SLOT( slotSpellCheckReady( KSpell *) ), 0, true, true);
+
+ connect( m_spell, SIGNAL( death() ),this, SLOT( spellCheckerFinished() ) );
+ connect( m_spell, SIGNAL( misspelling( const QString &, const QStringList &, unsigned int ) ),this, SLOT( spellCheckerMisspelling( const QString &, const QStringList &, unsigned int ) ) );
+ connect( m_spell, SIGNAL( corrected( const QString &, const QString &, unsigned int ) ),this, SLOT( spellCheckerCorrected( const QString &, const QString &, unsigned int ) ) );
+}
+
+void LineEditWidget::spellCheckerMisspelling( const QString &_text, const QStringList &, unsigned int pos)
+{
+ highLightWord( _text.length(),pos );
+}
+
+void LineEditWidget::highLightWord( unsigned int length, unsigned int pos )
+{
+ setSelection ( pos, length );
+}
+
+void LineEditWidget::spellCheckerCorrected( const QString &old, const QString &corr, unsigned int pos )
+{
+ if( old!= corr )
+ {
+ setSelection ( pos, old.length() );
+ insert( corr );
+ setSelection ( pos, corr.length() );
+ }
+}
+
+void LineEditWidget::spellCheckerFinished()
+{
+}
+
+void LineEditWidget::slotSpellCheckReady( KSpell *s )
+{
+ s->check( text() );
+ connect( s, SIGNAL( done( const QString & ) ), this, SLOT( slotSpellCheckDone( const QString & ) ) );
+}
+
+void LineEditWidget::slotSpellCheckDone( const QString &s )
+{
+ if( s != text() )
+ setText( s );
+}
+
+
+QPopupMenu *LineEditWidget::createPopupMenu()
+{
+ QPopupMenu *popup = KLineEdit::createPopupMenu();
+
+ if ( !popup ) {
+ return 0L;
+ }
+
+ connect( popup, SIGNAL( activated( int ) ),
+ this, SLOT( extendedMenuActivated( int ) ) );
+
+ if (m_input->autoComplete()) {
+ popup->insertSeparator();
+ int id = popup->insertItem( SmallIconSet("history_clear"), i18n("Clear &History"), ClearHistory );
+ popup->setItemEnabled( id, (compObj() && !compObj()->isEmpty()) );
+ }
+
+ if (echoMode() == QLineEdit::Normal &&
+ !isReadOnly()) {
+ popup->insertSeparator();
+
+ m_spellAction->plug(popup);
+ m_spellAction->setEnabled( !text().isEmpty() );
+ }
+
+ return popup;
+}
+
+
+void LineEditWidget::extendedMenuActivated( int id)
+{
+ switch ( id )
+ {
+ case ClearHistory:
+ m_view->clearCompletionHistory(m_input->name().string());
+ if (compObj())
+ compObj()->clear();
+ default:
+ break;
+ }
+}
+
+bool LineEditWidget::event( QEvent *e )
+{
+ if (KLineEdit::event(e))
+ return true;
+
+ if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
+ QKeyEvent* ke = (QKeyEvent*) e;
+ if ( ke->state() & ControlButton ) {
+ switch ( ke->key() ) {
+ case Key_Left:
+ case Key_Right:
+ case Key_Up:
+ case Key_Down:
+ case Key_Home:
+ case Key_End:
+ ke->accept();
+ default:
+ break;
+ }
+ }
+ }
+ 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)
+{
+ LineEditWidget *edit = new LineEditWidget(element, view(), view()->viewport());
+ connect(edit,SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
+ connect(edit,SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const 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->setContextMenuEnabled(true);
+ edit->completionBox()->setTabHandling( false );
+ }
+ }
+
+ setQWidget(edit);
+}
+
+void RenderLineEdit::setStyle(RenderStyle* _style)
+{
+ RenderFormElement::setStyle( _style );
+
+ widget()->setAlignment(textAlignment());
+}
+
+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->currentItem() != -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()->edited() ) {
+ element()->onChange();
+ widget()->setEdited( false );
+ }
+}
+
+void RenderLineEdit::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ const QFontMetrics &fm = style()->fontMetrics();
+ QSize s;
+
+ int size = element()->size();
+
+ int h = fm.lineSpacing();
+ int w = fm.width( 'x' ) * (size > 0 ? size+1 : 17); // "some"
+ s = QSize(w + 2 + 2*widget()->frameWidth(),
+ kMax(h, 14) + 2 + 2*widget()->frameWidth())
+ .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()) {
+ widget()->blockSignals(true);
+ int pos = widget()->cursorPosition();
+ widget()->setText(element()->value().string());
+
+ widget()->setEdited( false );
+
+ widget()->setCursorPosition(pos);
+ widget()->blockSignals(false);
+ }
+ widget()->setReadOnly(element()->readOnly());
+
+ RenderFormElement::updateFromElement();
+}
+
+void RenderLineEdit::slotTextChanged(const QString &string)
+{
+ // don't use setValue here!
+ element()->m_value = 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)
+{
+}
+
+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, kMax((b-h)/2, 0));
+ m_height = kMax(b,h) + paddingTop();
+ }
+ return legend;
+}
+
+RenderObject* RenderFieldset::findLegend()
+{
+ 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)
+{
+ //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
+
+ 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;
+ h -= yOff;
+ _ty += yOff - borderTopExtra();
+
+ int my = kMax(_ty,pI.r.y());
+ int end = kMin( pI.r.y() + pI.r.height(), _ty + h );
+ int mh = end - my;
+
+ paintBackground(pI.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
+
+ if ( style()->hasBorder() )
+ paintBorderMinusLegend(pI.p, _tx, _ty, w, h, style(), legend->xPos(), legend->width());
+}
+
+void RenderFieldset::paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w, int h,
+ const RenderStyle* style, int lx, int lw)
+{
+
+ 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;
+
+ if(render_t) {
+ 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);
+ 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);
+
+ drawBorder(p, _tx, _ty, _tx + style->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);
+
+ drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _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)
+{
+ KURLRequester* w = new KURLRequester( view()->viewport(), "__khtml" );
+
+ w->setMode(KFile::File | KFile::ExistingOnly);
+ w->completionObject()->setDir(KGlobalSettings::documentPath());
+
+ connect(w->lineEdit(), SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
+ connect(w->lineEdit(), SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
+ connect(w, SIGNAL(urlSelected(const QString &)),this,SLOT(slotUrlSelected(const QString &)));
+
+ setQWidget(w);
+ m_haveFocus = false;
+}
+
+
+
+void RenderFileButton::calcMinMaxWidth()
+{
+ KHTMLAssert( !minMaxKnown() );
+
+ const QFontMetrics &fm = style()->fontMetrics();
+ int size = element()->size();
+
+ int h = fm.lineSpacing();
+ int w = fm.width( 'x' ) * (size > 0 ? size+1 : 17); // "some"
+ KLineEdit* edit = static_cast<KURLRequester*>( m_widget )->lineEdit();
+ QSize s = edit->style().sizeFromContents(QStyle::CT_LineEdit,
+ edit,
+ QSize(w + 2 + 2*edit->frameWidth(), kMax(h, 14) + 2 + 2*edit->frameWidth()))
+ .expandedTo(QApplication::globalStrut());
+ QSize bs = static_cast<KURLRequester*>( m_widget )->minimumSizeHint() - edit->minimumSizeHint();
+
+ setIntrinsicWidth( s.width() + bs.width() );
+ setIntrinsicHeight( kMax(s.height(), bs.height()) );
+
+ RenderFormElement::calcMinMaxWidth();
+}
+
+void RenderFileButton::handleFocusOut()
+{
+ if ( widget()->lineEdit() && widget()->lineEdit()->edited() ) {
+ element()->onChange();
+ widget()->lineEdit()->setEdited( false );
+ }
+}
+
+void RenderFileButton::updateFromElement()
+{
+ KLineEdit* edit = widget()->lineEdit();
+ edit->blockSignals(true);
+ edit->setText(element()->value().string());
+ edit->blockSignals(false);
+ edit->setEdited( false );
+
+ RenderFormElement::updateFromElement();
+}
+
+void RenderFileButton::slotReturnPressed()
+{
+ handleFocusOut();
+
+ if (element()->form())
+ element()->form()->submitFromKeyboard();
+}
+
+void RenderFileButton::slotTextChanged(const QString &/*string*/)
+{
+ element()->m_value = KURL( widget()->url() ).prettyURL( 0, KURL::StripFileProtocol );
+}
+
+void RenderFileButton::slotUrlSelected(const QString &)
+{
+ element()->onChange();
+}
+
+void RenderFileButton::select()
+{
+ widget()->lineEdit()->selectAll();
+}
+
+// -------------------------------------------------------------------------
+
+RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element)
+ : RenderFormElement(element)
+{
+
+}
+
+// -------------------------------------------------------------------------
+
+RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element)
+ : RenderBlock(element)
+{
+}
+
+// -------------------------------------------------------------------------------
+
+ComboBoxWidget::ComboBoxWidget(QWidget *parent)
+ : KComboBox(false, parent, "__khtml")
+{
+ setAutoMask(true);
+ if (listBox()) listBox()->installEventFilter(this);
+ setMouseTracking(true);
+}
+
+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 Key_Return:
+ case Key_Enter:
+ popup();
+ ke->accept();
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+bool ComboBoxWidget::eventFilter(QObject *dest, QEvent *e)
+{
+ if (dest==listBox() && e->type()==QEvent::KeyPress)
+ {
+ QKeyEvent *ke = static_cast<QKeyEvent *>(e);
+ bool forward = false;
+ switch(ke->key())
+ {
+ case Key_Tab:
+ forward=true;
+ case 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, Key_Escape, 0, 0);
+ QApplication::sendEvent(dest,ke);
+ focusNextPrevChild(forward);
+ delete ke;
+ return true;
+ default:
+ return KComboBox::eventFilter(dest, e);
+ }
+ }
+ return KComboBox::eventFilter(dest, 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());
+}
+
+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());
+ }
+
+ if (m_useListBox && oldMultiple != m_multiple) {
+ static_cast<KListBox*>(m_widget)->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
+ }
+ 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();
+ QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
+ int listIndex;
+
+ if(m_useListBox) {
+ static_cast<KListBox*>(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 = "";
+
+ if(m_useListBox) {
+ QListBoxText *item = new QListBoxText(QString(text.implementation()->s, text.implementation()->l));
+ static_cast<KListBox*>(m_widget)
+ ->insertItem(item, listIndex);
+ item->setSelectable(false);
+ }
+ else {
+ static_cast<KComboBox*>(m_widget)
+ ->insertItem(QString(text.implementation()->s, text.implementation()->l), listIndex);
+ static_cast<KComboBox*>(m_widget)->listBox()->item(listIndex)->setSelectable(false);
+ }
+ }
+ else if (listItems[listIndex]->id() == ID_OPTION) {
+ HTMLOptionElementImpl* optElem = static_cast<HTMLOptionElementImpl*>(listItems[listIndex]);
+ QString text = optElem->text().string();
+ if (optElem->parentNode()->id() == ID_OPTGROUP)
+ {
+ // Prefer label if set
+ DOMString label = optElem->getAttribute(ATTR_LABEL);
+ if (!label.isEmpty())
+ text = label.string();
+ text = QString::fromLatin1(" ")+text;
+ }
+
+ if(m_useListBox) {
+ KListBox *l = static_cast<KListBox*>(m_widget);
+ l->insertItem(text, listIndex);
+ DOMString disabled = optElem->getAttribute(ATTR_DISABLED);
+ if (!disabled.isNull() && l->item( listIndex )) {
+ l->item( listIndex )->setSelectable( false );
+ }
+ } else
+ static_cast<KComboBox*>(m_widget)->insertItem(text, listIndex);
+ }
+ 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();
+}
+
+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) {
+ KListBox* w = static_cast<KListBox*>(m_widget);
+
+ QListBoxItem* p = w->firstItem();
+ int width = 0;
+ int height = 0;
+ while(p) {
+ width = kMax(width, p->width(p->listBox()));
+ height = kMax(height, p->height(p->listBox()));
+ p = p->next();
+ }
+ 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 kMin(number of elements, 4)
+ // Netscape seems to simply set it to "number of elements"
+ // the average of that is IMHO kMin(number of elements, 10)
+ // so I did that ;-)
+ if(size < 1)
+ size = kMin(static_cast<KListBox*>(m_widget)->count(), 10u);
+
+ width += 2*w->frameWidth() + w->verticalScrollBar()->sizeHint().width();
+ height = size*height + 2*w->frameWidth();
+
+ setIntrinsicWidth( width );
+ setIntrinsicHeight( height );
+ }
+ else {
+ QSize s(m_widget->sizeHint());
+ setIntrinsicWidth( s.width() );
+ setIntrinsicHeight( s.height() );
+ }
+
+ /// uuh, ignore the following line..
+ setNeedsLayout(true);
+ RenderFormElement::layout();
+
+ // and now disable the widget in case there is no <option> given
+ QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
+
+ bool foundOption = false;
+ for (uint 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 );
+
+ QMemArray<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 ( ( unsigned ) 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 ( unsigned int i = 0; i < listItems.size(); ++i )
+ if ( listItems[i]->id() == ID_OPTION && i != (unsigned int) 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 )->currentItem() )
+ static_cast<ComboBoxWidget*>( m_widget )->setCurrentItem( 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
+ QMemArray<HTMLGenericFormElementImpl*> listItems = element()->m_listItems;
+ for ( unsigned 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<KListBox*>( m_widget )->isSelected( i );
+
+ ref();
+ element()->onChange();
+ deref();
+}
+
+void RenderSelect::setOptionsChanged(bool _optionsChanged)
+{
+ m_optionsChanged = _optionsChanged;
+}
+
+KListBox* RenderSelect::createListBox()
+{
+ KListBox *lb = new KListBox(view()->viewport(), "__khtml");
+ lb->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
+ // ### looks broken
+ //lb->setAutoMask(true);
+ connect( lb, SIGNAL( selectionChanged() ), this, SLOT( slotSelectionChanged() ) );
+// connect( lb, SIGNAL( clicked( QListBoxItem * ) ), this, SLOT( slotClicked() ) );
+ m_ignoreSelectEvents = false;
+ lb->setMouseTracking(true);
+
+ return lb;
+}
+
+ComboBoxWidget *RenderSelect::createComboBox()
+{
+ ComboBoxWidget *cb = new ComboBoxWidget(view()->viewport());
+ connect(cb, SIGNAL(activated(int)), this, SLOT(slotSelected(int)));
+ return cb;
+}
+
+void RenderSelect::updateSelection()
+{
+ QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
+ int i;
+ if (m_useListBox) {
+ // if multi-select, we select only the new selected index
+ KListBox *listBox = static_cast<KListBox*>(m_widget);
+ for (i = 0; i < int(listItems.size()); i++)
+ listBox->setSelected(i,listItems[i]->id() == ID_OPTION &&
+ static_cast<HTMLOptionElementImpl*>(listItems[i])->selected());
+ }
+ else {
+ bool found = false;
+ unsigned firstOption = listItems.size();
+ 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])->selected()) {
+ static_cast<KComboBox*>( m_widget )->setCurrentItem(i);
+ found = true;
+ }
+ firstOption = i;
+ }
+
+ Q_ASSERT(firstOption == listItems.size() || found);
+ }
+
+ m_selectionChanged = false;
+}
+
+
+// -------------------------------------------------------------------------
+
+TextAreaWidget::TextAreaWidget(int wrap, QWidget* parent)
+ : KTextEdit(parent, "__khtml"), m_findDlg(0), m_find(0), m_repDlg(0), m_replace(0)
+{
+ if(wrap != DOM::HTMLTextAreaElementImpl::ta_NoWrap) {
+ setWordWrap(QTextEdit::WidgetWidth);
+ setHScrollBarMode( AlwaysOff );
+ setVScrollBarMode( AlwaysOn );
+ }
+ else {
+ setWordWrap(QTextEdit::NoWrap);
+ setHScrollBarMode( Auto );
+ setVScrollBarMode( Auto );
+ }
+ KCursor::setAutoHideCursor(viewport(), true);
+ setTextFormat(QTextEdit::PlainText);
+ setAutoMask(true);
+ setMouseTracking(true);
+
+ KActionCollection *ac = new KActionCollection(this);
+ m_findAction = KStdAction::find( this, SLOT( slotFind() ), ac );
+ m_findNextAction = KStdAction::findNext( this, SLOT( slotFindNext() ), ac );
+ m_replaceAction = KStdAction::replace( this, SLOT( slotReplace() ), ac );
+}
+
+
+TextAreaWidget::~TextAreaWidget()
+{
+ delete m_replace;
+ m_replace = 0L;
+ delete m_find;
+ m_find = 0L;
+ delete m_repDlg;
+ m_repDlg = 0L;
+ delete m_findDlg;
+ m_findDlg = 0L;
+}
+
+
+QPopupMenu *TextAreaWidget::createPopupMenu(const QPoint& pos)
+{
+ QPopupMenu *popup = KTextEdit::createPopupMenu(pos);
+
+ if ( !popup ) {
+ return 0L;
+ }
+
+ if (!isReadOnly()) {
+ popup->insertSeparator();
+
+ m_findAction->plug(popup);
+ m_findAction->setEnabled( !text().isEmpty() );
+
+ m_findNextAction->plug(popup);
+ m_findNextAction->setEnabled( m_find != 0 );
+
+ m_replaceAction->plug(popup);
+ m_replaceAction->setEnabled( !text().isEmpty() );
+ }
+
+ return popup;
+}
+
+
+void TextAreaWidget::slotFindHighlight(const QString& text, int matchingIndex, int matchingLength)
+{
+ Q_UNUSED(text)
+ //kdDebug() << "Highlight: [" << text << "] mi:" << matchingIndex << " ml:" << matchingLength << endl;
+ if (sender() == m_replace) {
+ setSelection(m_repPara, matchingIndex, m_repPara, matchingIndex + matchingLength);
+ setCursorPosition(m_repPara, matchingIndex);
+ } else {
+ setSelection(m_findPara, matchingIndex, m_findPara, matchingIndex + matchingLength);
+ setCursorPosition(m_findPara, matchingIndex);
+ }
+ ensureCursorVisible();
+}
+
+
+void TextAreaWidget::slotReplaceText(const QString &text, int replacementIndex, int /*replacedLength*/, int matchedLength) {
+ Q_UNUSED(text)
+ //kdDebug() << "Replace: [" << text << "] ri:" << replacementIndex << " rl:" << replacedLength << " ml:" << matchedLength << endl;
+ setSelection(m_repPara, replacementIndex, m_repPara, replacementIndex + matchedLength);
+ removeSelectedText();
+ insertAt(m_repDlg->replacement(), m_repPara, replacementIndex);
+ if (m_replace->options() & KReplaceDialog::PromptOnReplace) {
+ ensureCursorVisible();
+ }
+}
+
+
+void TextAreaWidget::slotDoReplace()
+{
+ if (!m_repDlg) {
+ // Should really assert()
+ return;
+ }
+
+ delete m_replace;
+ m_replace = new KReplace(m_repDlg->pattern(), m_repDlg->replacement(), m_repDlg->options(), this);
+ if (m_replace->options() & KFindDialog::FromCursor) {
+ getCursorPosition(&m_repPara, &m_repIndex);
+ } else if (m_replace->options() & KFindDialog::FindBackwards) {
+ m_repPara = paragraphs() - 1;
+ m_repIndex = paragraphLength(m_repPara) - 1;
+ } else {
+ m_repPara = 0;
+ m_repIndex = 0;
+ }
+
+ // Connect highlight signal to code which handles highlighting
+ // of found text.
+ connect(m_replace, SIGNAL(highlight(const QString &, int, int)),
+ this, SLOT(slotFindHighlight(const QString &, int, int)));
+ connect(m_replace, SIGNAL(findNext()), this, SLOT(slotReplaceNext()));
+ connect(m_replace, SIGNAL(replace(const QString &, int, int, int)),
+ this, SLOT(slotReplaceText(const QString &, int, int, int)));
+
+ m_repDlg->close();
+ slotReplaceNext();
+}
+
+
+void TextAreaWidget::slotReplaceNext()
+{
+ if (!m_replace) {
+ // assert?
+ return;
+ }
+
+ if (!(m_replace->options() & KReplaceDialog::PromptOnReplace)) {
+ viewport()->setUpdatesEnabled(false);
+ }
+
+ KFind::Result res = KFind::NoMatch;
+ while (res == KFind::NoMatch) {
+ // If we're done.....
+ if (m_replace->options() & KFindDialog::FindBackwards) {
+ if (m_repIndex == 0 && m_repPara == 0) {
+ break;
+ }
+ } else {
+ if (m_repPara == paragraphs() - 1 &&
+ m_repIndex == paragraphLength(m_repPara) - 1) {
+ break;
+ }
+ }
+
+ if (m_replace->needData()) {
+ m_replace->setData(text(m_repPara), m_repIndex);
+ }
+
+ res = m_replace->replace();
+
+ if (res == KFind::NoMatch) {
+ if (m_replace->options() & KFindDialog::FindBackwards) {
+ if (m_repPara == 0) {
+ m_repIndex = 0;
+ } else {
+ m_repPara--;
+ m_repIndex = paragraphLength(m_repPara) - 1;
+ }
+ } else {
+ if (m_repPara == paragraphs() - 1) {
+ m_repIndex = paragraphLength(m_repPara) - 1;
+ } else {
+ m_repPara++;
+ m_repIndex = 0;
+ }
+ }
+ }
+ }
+
+ if (!(m_replace->options() & KReplaceDialog::PromptOnReplace)) {
+ viewport()->setUpdatesEnabled(true);
+ repaintChanged();
+ }
+
+ if (res == KFind::NoMatch) { // at end
+ m_replace->displayFinalDialog();
+ delete m_replace;
+ m_replace = 0;
+ ensureCursorVisible();
+ //or if ( m_replace->shouldRestart() ) { reinit (w/o FromCursor) and call slotReplaceNext(); }
+ } else {
+ //m_replace->closeReplaceNextDialog();
+ }
+}
+
+
+void TextAreaWidget::slotDoFind()
+{
+ if (!m_findDlg) {
+ // Should really assert()
+ return;
+ }
+
+ delete m_find;
+ m_find = new KFind(m_findDlg->pattern(), m_findDlg->options(), this);
+ if (m_find->options() & KFindDialog::FromCursor) {
+ getCursorPosition(&m_findPara, &m_findIndex);
+ } else if (m_find->options() & KFindDialog::FindBackwards) {
+ m_findPara = paragraphs() - 1;
+ m_findIndex = paragraphLength(m_findPara) - 1;
+ } else {
+ m_findPara = 0;
+ m_findIndex = 0;
+ }
+
+ // Connect highlight signal to code which handles highlighting
+ // of found text.
+ connect(m_find, SIGNAL(highlight(const QString &, int, int)),
+ this, SLOT(slotFindHighlight(const QString &, int, int)));
+ connect(m_find, SIGNAL(findNext()), this, SLOT(slotFindNext()));
+
+ m_findDlg->close();
+ m_find->closeFindNextDialog();
+ slotFindNext();
+}
+
+
+void TextAreaWidget::slotFindNext()
+{
+ if (!m_find) {
+ // assert?
+ return;
+ }
+
+ KFind::Result res = KFind::NoMatch;
+ while (res == KFind::NoMatch) {
+ // If we're done.....
+ if (m_find->options() & KFindDialog::FindBackwards) {
+ if (m_findIndex == 0 && m_findPara == 0) {
+ break;
+ }
+ } else {
+ if (m_findPara == paragraphs() - 1 &&
+ m_findIndex == paragraphLength(m_findPara) - 1) {
+ break;
+ }
+ }
+
+ if (m_find->needData()) {
+ m_find->setData(text(m_findPara), m_findIndex);
+ }
+
+ res = m_find->find();
+
+ if (res == KFind::NoMatch) {
+ if (m_find->options() & KFindDialog::FindBackwards) {
+ if (m_findPara == 0) {
+ m_findIndex = 0;
+ } else {
+ m_findPara--;
+ m_findIndex = paragraphLength(m_findPara) - 1;
+ }
+ } else {
+ if (m_findPara == paragraphs() - 1) {
+ m_findIndex = paragraphLength(m_findPara) - 1;
+ } else {
+ m_findPara++;
+ m_findIndex = 0;
+ }
+ }
+ }
+ }
+
+ if (res == KFind::NoMatch) { // at end
+ m_find->displayFinalDialog();
+ delete m_find;
+ m_find = 0;
+ //or if ( m_find->shouldRestart() ) { reinit (w/o FromCursor) and call slotFindNext(); }
+ } else {
+ //m_find->closeFindNextDialog();
+ }
+}
+
+
+void TextAreaWidget::slotFind()
+{
+ if( text().isEmpty() ) // saves having to track the text changes
+ return;
+
+ if ( m_findDlg ) {
+ KWin::activateWindow( m_findDlg->winId() );
+ } else {
+ m_findDlg = new KFindDialog(false, this, "KHTML Text Area Find Dialog");
+ connect( m_findDlg, SIGNAL(okClicked()), this, SLOT(slotDoFind()) );
+ }
+ m_findDlg->show();
+}
+
+
+void TextAreaWidget::slotReplace()
+{
+ if( text().isEmpty() ) // saves having to track the text changes
+ return;
+
+ if ( m_repDlg ) {
+ KWin::activateWindow( m_repDlg->winId() );
+ } else {
+ m_repDlg = new KReplaceDialog(this, "KHTMLText Area Replace Dialog", 0,
+ QStringList(), QStringList(), false);
+ connect( m_repDlg, SIGNAL(okClicked()), this, SLOT(slotDoReplace()) );
+ }
+ m_repDlg->show();
+}
+
+
+bool TextAreaWidget::event( QEvent *e )
+{
+ if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
+ QKeyEvent* ke = (QKeyEvent*) e;
+ if ( ke->state() & ControlButton ) {
+ switch ( ke->key() ) {
+ case Key_Left:
+ case Key_Right:
+ case Key_Up:
+ case Key_Down:
+ case Key_Home:
+ case Key_End:
+ ke->accept();
+ default:
+ break;
+ }
+ }
+ }
+ return KTextEdit::event( e );
+}
+
+// -------------------------------------------------------------------------
+
+RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
+ : RenderFormElement(element)
+{
+ scrollbarsStyled = false;
+
+ 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()));
+}
+
+RenderTextArea::~RenderTextArea()
+{
+ if ( element()->m_dirtyvalue ) {
+ element()->m_value = text();
+ element()->m_dirtyvalue = false;
+ }
+}
+
+void RenderTextArea::handleFocusOut()
+{
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ if ( w && element()->m_dirtyvalue ) {
+ element()->m_value = text();
+ element()->m_dirtyvalue = false;
+ }
+
+ 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(" "));
+ QSize size( kMax(element()->cols(), 1L)*m.width('x') + w->frameWidth() +
+ w->verticalScrollBar()->sizeHint().width(),
+ kMax(element()->rows(), 1L)*m.lineSpacing() + w->frameWidth()*4 +
+ (w->wordWrap() == QTextEdit::NoWrap ?
+ w->horizontalScrollBar()->sizeHint().height() : 0)
+ );
+
+ setIntrinsicWidth( size.width() );
+ setIntrinsicHeight( size.height() );
+
+ RenderFormElement::calcMinMaxWidth();
+}
+
+void RenderTextArea::setStyle(RenderStyle* _style)
+{
+ bool unsubmittedFormChange = element()->m_unsubmittedFormChange;
+
+ RenderFormElement::setStyle(_style);
+
+ widget()->blockSignals(true);
+ widget()->setAlignment(textAlignment());
+ widget()->blockSignals(false);
+
+ scrollbarsStyled = false;
+
+ element()->m_unsubmittedFormChange = unsubmittedFormChange;
+}
+
+void RenderTextArea::layout()
+{
+ KHTMLAssert( needsLayout() );
+
+ RenderFormElement::layout();
+
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+
+ if (!scrollbarsStyled) {
+ w->horizontalScrollBar()->setPalette(style()->palette());
+ w->verticalScrollBar()->setPalette(style()->palette());
+ scrollbarsStyled=true;
+ }
+}
+
+void RenderTextArea::updateFromElement()
+{
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ w->setReadOnly(element()->readOnly());
+ QString elementText = element()->value().string();
+ if ( elementText != text() )
+ {
+ w->blockSignals(true);
+ int line, col;
+ w->getCursorPosition( &line, &col );
+ int cx = w->contentsX();
+ int cy = w->contentsY();
+ w->setText( elementText );
+ w->setCursorPosition( line, col );
+ w->scrollBy( cx, cy );
+ w->blockSignals(false);
+ }
+ element()->m_dirtyvalue = false;
+
+ RenderFormElement::updateFromElement();
+}
+
+void RenderTextArea::close( )
+{
+ element()->setValue( element()->defaultValue() );
+
+ RenderFormElement::close();
+}
+
+
+QString RenderTextArea::text()
+{
+ QString txt;
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+
+ if(element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical) {
+ // yeah, QTextEdit has no accessor for getting the visually wrapped text
+ for (int p=0; p < w->paragraphs(); ++p) {
+ int ll = 0;
+ int lindex = w->lineOfChar(p, 0);
+ QString paragraphText = w->text(p);
+ int pl = w->paragraphLength(p);
+ paragraphText = paragraphText.left(pl); //Snip invented space.
+ for (int l = 0; l < pl; ++l) {
+ if (lindex != w->lineOfChar(p, l)) {
+ paragraphText.insert(l+ll++, QString::fromLatin1("\n"));
+ lindex = w->lineOfChar(p, l);
+ }
+ }
+ txt += paragraphText;
+ if (p < w->paragraphs() - 1)
+ txt += QString::fromLatin1("\n");
+ }
+ }
+ else
+ txt = w->text();
+
+ return txt;
+}
+
+int RenderTextArea::queryParagraphInfo(int para, Mode m, int param) {
+ /* We have to be a bit careful here, as we need to match up the positions
+ to what our value returns here*/
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int length = 0;
+
+ bool physWrap = element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical;
+
+ QString paragraphText = w->text(para);
+ int pl = w->paragraphLength(para);
+ int physicalPL = pl;
+ if (m == ParaPortionLength)
+ pl = param;
+
+ if (physWrap) {
+ //Go through all the chars of paragraph, and count line changes, chars, etc.
+ int lindex = w->lineOfChar(para, 0);
+ for (int c = 0; c < pl; ++c) {
+ ++length;
+ // Is there a change after this char?
+ if (c+1 < physicalPL && lindex != w->lineOfChar(para, c+1)) {
+ lindex = w->lineOfChar(para, c+1);
+ ++length;
+ }
+ if (m == ParaPortionOffset && length > param)
+ return c;
+ }
+ } else {
+ //Make sure to count the LF, CR as appropriate. ### this is stupid now, simplify
+ for (int c = 0; c < pl; ++c) {
+ ++length;
+ if (m == ParaPortionOffset && length > param)
+ return c;
+ }
+ }
+ if (m == ParaPortionOffset)
+ return pl;
+ if (m == ParaPortionLength)
+ return length;
+ return length + 1;
+}
+
+long RenderTextArea::computeCharOffset(int para, int index) {
+ if (para < 0)
+ return 0;
+
+ long pos = 0;
+ for (int cp = 0; cp < para; ++cp)
+ pos += queryParagraphInfo(cp, ParaLength);
+
+ if (index >= 0)
+ pos += queryParagraphInfo(para, ParaPortionLength, index);
+ return pos;
+}
+
+void RenderTextArea::computeParagraphAndIndex(long offset, int* para, int* index) {
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+
+ if (!w->paragraphs()) {
+ *para = -1;
+ *index = -1;
+ return;
+ }
+
+ //Find the paragraph that contains us..
+ int containingPar = 0;
+ long endPos = 0;
+ long startPos = 0;
+ for (int p = 0; p < w->paragraphs(); ++p) {
+ int len = queryParagraphInfo(p, ParaLength);
+ endPos += len;
+ if (endPos > offset) {
+ containingPar = p;
+ break;
+ }
+ startPos += len;
+ }
+
+ *para = containingPar;
+
+ //Now, scan within the paragraph to find the position..
+ long localOffset = offset - startPos;
+
+ *index = queryParagraphInfo(containingPar, ParaPortionOffset, localOffset);
+}
+
+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_dirtyvalue = true;
+ 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);
+ int para, index, dummy1, dummy2;
+ w->getSelection(&para, &index, &dummy1, &dummy2);
+ if (para == -1 || index == -1)
+ w->getCursorPosition(&para, &index);
+
+ return computeCharOffset(para, index);
+}
+
+long RenderTextArea::selectionEnd()
+{
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int para, index, dummy1, dummy2;
+ w->getSelection(&dummy1, &dummy2, &para, &index);
+ if (para == -1 || index == -1)
+ w->getCursorPosition(&para, &index);
+
+ return computeCharOffset(para, index);
+}
+
+void RenderTextArea::setSelectionStart(long offset) {
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int fromPara, fromIndex, toPara, toIndex;
+ w->getSelection(&fromPara, &fromIndex, &toPara, &toIndex);
+ computeParagraphAndIndex(offset, &fromPara, &fromIndex);
+ if (toPara == -1 || toIndex == -1) {
+ toPara = fromPara;
+ toIndex = fromIndex;
+ }
+ w->setSelection(fromPara, fromIndex, toPara, toIndex);
+}
+
+void RenderTextArea::setSelectionEnd(long offset) {
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int fromPara, fromIndex, toPara, toIndex;
+ w->getSelection(&fromPara, &fromIndex, &toPara, &toIndex);
+ computeParagraphAndIndex(offset, &toPara, &toIndex);
+ w->setSelection(fromPara, fromIndex, toPara, toIndex);
+}
+
+void RenderTextArea::setSelectionRange(long start, long end) {
+ TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
+ int fromPara, fromIndex, toPara, toIndex;
+ computeParagraphAndIndex(start, &fromPara, &fromIndex);
+ computeParagraphAndIndex(end, &toPara, &toIndex);
+ w->setSelection(fromPara, fromIndex, toPara, toIndex);
+}
+// ---------------------------------------------------------------------------
+
+#include "render_form.moc"