khtml Library API Documentation

render_form.cpp

00001 /*
00002  * This file is part of the DOM implementation for KDE.
00003  *
00004  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
00005  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
00006  *           (C) 2000 Dirk Mueller (mueller@kde.org)
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Library General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Library General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Library General Public License
00019  * along with this library; see the file COPYING.LIB.  If not, write to
00020  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00021  * Boston, MA 02111-1307, USA.
00022  *
00023  * $Id: render_form.cpp,v 1.198.2.10 2003/08/28 23:53:15 mueller Exp $
00024  */
00025 
00026 #include <kdebug.h>
00027 #include <klocale.h>
00028 #include <kfiledialog.h>
00029 #include <kcompletionbox.h>
00030 #include <kcursor.h>
00031 
00032 #include <qstyle.h>
00033 
00034 #include "misc/helper.h"
00035 #include "xml/dom2_eventsimpl.h"
00036 #include "html/html_formimpl.h"
00037 #include "misc/htmlhashes.h"
00038 
00039 #include "rendering/render_form.h"
00040 #include <assert.h>
00041 
00042 #include "khtmlview.h"
00043 #include "khtml_ext.h"
00044 #include "xml/dom_docimpl.h"
00045 
00046 #include <qpopupmenu.h>
00047 
00048 using namespace khtml;
00049 
00050 RenderFormElement::RenderFormElement(HTMLGenericFormElementImpl *element)
00051     : RenderWidget(element)
00052 {
00053     // init RenderObject attributes
00054     setInline(true);   // our object is Inline
00055 
00056     m_state = 0;
00057     m_isDoubleClick = false;
00058 }
00059 
00060 RenderFormElement::~RenderFormElement()
00061 {
00062 }
00063 
00064 short RenderFormElement::baselinePosition( bool f ) const
00065 {
00066     return RenderWidget::baselinePosition( f ) - 2 - style()->fontMetrics().descent();
00067 }
00068 
00069 
00070 void RenderFormElement::updateFromElement()
00071 {
00072     m_widget->setEnabled(!element()->disabled());
00073     RenderWidget::updateFromElement();
00074 }
00075 
00076 void RenderFormElement::layout()
00077 {
00078     KHTMLAssert( !layouted() );
00079     KHTMLAssert( minMaxKnown() );
00080 
00081     // minimum height
00082     m_height = 0;
00083 
00084     calcWidth();
00085     calcHeight();
00086 
00087     if ( m_widget )
00088         resizeWidget(m_width-borderLeft()-borderRight()-paddingLeft()-paddingRight(),
00089                      m_height-borderTop()-borderBottom()-paddingTop()-paddingBottom());
00090 
00091     if ( !style()->width().isPercent() )
00092         setLayouted();
00093 }
00094 
00095 void RenderFormElement::slotClicked()
00096 {
00097     ref();
00098     QMouseEvent e2( QEvent::MouseButtonRelease, m_mousePos, 1, m_state);
00099 
00100     element()->dispatchMouseEvent(&e2, EventImpl::CLICK_EVENT, m_isDoubleClick + 1);
00101     m_isDoubleClick = false;
00102     deref();
00103 }
00104 
00105 void RenderFormElement::slotPressed()
00106 {
00107     ref();
00108     QMouseEvent e2( QEvent::MouseButtonPress, m_mousePos, 1, m_state);
00109     element()->dispatchMouseEvent(&e2, EventImpl::MOUSEDOWN_EVENT, 1);
00110     deref();
00111 }
00112 
00113 void RenderFormElement::slotReleased()
00114 {
00115     ref();
00116     QMouseEvent e2( QEvent::MouseButtonRelease, m_mousePos, 1, m_state);
00117     element()->dispatchMouseEvent(&e2, EventImpl::MOUSEUP_EVENT, 1);
00118     deref();
00119 }
00120 
00121 // -------------------------------------------------------------------------
00122 
00123 RenderButton::RenderButton(HTMLGenericFormElementImpl *element)
00124     : RenderFormElement(element)
00125 {
00126 }
00127 
00128 short RenderButton::baselinePosition( bool f ) const
00129 {
00130     return RenderWidget::baselinePosition( f ) - 2;
00131 }
00132 
00133 // -------------------------------------------------------------------------------
00134 
00135 RenderCheckBox::RenderCheckBox(HTMLInputElementImpl *element)
00136     : RenderButton(element)
00137 {
00138     QCheckBox* b = new QCheckBox(view()->viewport());
00139     b->setAutoMask(true);
00140     b->setMouseTracking(true);
00141     setQWidget(b);
00142     connect(b,SIGNAL(stateChanged(int)),this,SLOT(slotStateChanged(int)));
00143     connect(b, SIGNAL(clicked()), this, SLOT(slotClicked()));
00144     connect(b, SIGNAL(pressed()), this, SLOT(slotPressed()));
00145     connect(b, SIGNAL(released()), this, SLOT(slotReleased()));
00146 }
00147 
00148 
00149 void RenderCheckBox::calcMinMaxWidth()
00150 {
00151     KHTMLAssert( !minMaxKnown() );
00152 
00153     QCheckBox *cb = static_cast<QCheckBox *>( m_widget );
00154     QSize s( cb->style().pixelMetric( QStyle::PM_IndicatorWidth ),
00155              cb->style().pixelMetric( QStyle::PM_IndicatorHeight ) );
00156     setIntrinsicWidth( s.width() );
00157     setIntrinsicHeight( s.height() );
00158 
00159     RenderButton::calcMinMaxWidth();
00160 }
00161 
00162 void RenderCheckBox::updateFromElement()
00163 {
00164     widget()->setChecked(element()->checked());
00165 
00166     RenderButton::updateFromElement();
00167 }
00168 
00169 void RenderCheckBox::slotStateChanged(int state)
00170 {
00171     element()->setChecked(state == 2);
00172 }
00173 
00174 // -------------------------------------------------------------------------------
00175 
00176 RenderRadioButton::RenderRadioButton(HTMLInputElementImpl *element)
00177     : RenderButton(element)
00178 {
00179     QRadioButton* b = new QRadioButton(view()->viewport());
00180     b->setAutoMask(true);
00181     b->setMouseTracking(true);
00182     setQWidget(b);
00183     connect(b, SIGNAL(clicked()), this, SLOT(slotClicked()));
00184     connect(b, SIGNAL(pressed()), this, SLOT(slotPressed()));
00185     connect(b, SIGNAL(released()), this, SLOT(slotReleased()));
00186 }
00187 
00188 void RenderRadioButton::updateFromElement()
00189 {
00190     widget()->setChecked(element()->checked());
00191 
00192     RenderButton::updateFromElement();
00193 }
00194 
00195 void RenderRadioButton::slotClicked()
00196 {
00197     element()->setChecked(true);
00198 
00199     // emit mouseClick event etc
00200     RenderButton::slotClicked();
00201 }
00202 
00203 void RenderRadioButton::calcMinMaxWidth()
00204 {
00205     KHTMLAssert( !minMaxKnown() );
00206 
00207     QRadioButton *rb = static_cast<QRadioButton *>( m_widget );
00208     QSize s( rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorWidth ),
00209              rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorHeight ) );
00210     setIntrinsicWidth( s.width() );
00211     setIntrinsicHeight( s.height() );
00212 
00213     RenderButton::calcMinMaxWidth();
00214 }
00215 
00216 // -------------------------------------------------------------------------------
00217 
00218 
00219 RenderSubmitButton::RenderSubmitButton(HTMLInputElementImpl *element)
00220     : RenderButton(element)
00221 {
00222     QPushButton* p = new QPushButton(view()->viewport());
00223     setQWidget(p);
00224     p->setAutoMask(true);
00225     p->setMouseTracking(true);
00226     connect(p, SIGNAL(clicked()), this, SLOT(slotClicked()));
00227     connect(p, SIGNAL(pressed()), this, SLOT(slotPressed()));
00228     connect(p, SIGNAL(released()), this, SLOT(slotReleased()));
00229 }
00230 
00231 QString RenderSubmitButton::rawText()
00232 {
00233     QString value = element()->value().isEmpty() ? defaultLabel() : element()->value().string();
00234     value = value.stripWhiteSpace();
00235     QString raw;
00236     for(unsigned int i = 0; i < value.length(); i++) {
00237         raw += value[i];
00238         if(value[i] == '&')
00239             raw += '&';
00240     }
00241     return raw;
00242 }
00243 
00244 void RenderSubmitButton::calcMinMaxWidth()
00245 {
00246     KHTMLAssert( !minMaxKnown() );
00247 
00248     QString raw = rawText();
00249     QPushButton* pb = static_cast<QPushButton*>(m_widget);
00250     pb->setText(raw);
00251     pb->setFont(style()->font());
00252 
00253     bool empty = raw.isEmpty();
00254     if ( empty )
00255         raw = QString::fromLatin1("X");
00256     QFontMetrics fm = pb->fontMetrics();
00257     QSize ts = fm.size( ShowPrefix, raw);
00258     QSize s(pb->style().sizeFromContents( QStyle::CT_PushButton, pb, ts )
00259             .expandedTo(QApplication::globalStrut()));
00260     int margin = pb->style().pixelMetric( QStyle::PM_ButtonMargin, pb) +
00261          pb->style().pixelMetric( QStyle::PM_DefaultFrameWidth, pb ) * 2;
00262     int w = ts.width() + margin;
00263     int h = s.height();
00264     if (pb->isDefault() || pb->autoDefault()) {
00265     int dbw = pb->style().pixelMetric( QStyle::PM_ButtonDefaultIndicator, pb ) * 2;
00266     w += dbw;
00267     }
00268 
00269     // add 30% margins to the width (heuristics to make it look similar to IE)
00270     s = QSize( w*13/10, h ).expandedTo(QApplication::globalStrut());
00271 
00272     setIntrinsicWidth( s.width() );
00273     setIntrinsicHeight( s.height() );
00274 
00275     RenderButton::calcMinMaxWidth();
00276 }
00277 
00278 void RenderSubmitButton::updateFromElement()
00279 {
00280     QString oldText = static_cast<QPushButton*>(m_widget)->text();
00281     QString newText = rawText();
00282     static_cast<QPushButton*>(m_widget)->setText(newText);
00283     if ( oldText != newText ) {
00284         setMinMaxKnown(false);
00285     setLayouted(false);
00286     }
00287     RenderFormElement::updateFromElement();
00288 }
00289 
00290 QString RenderSubmitButton::defaultLabel() {
00291     return i18n("Submit");
00292 }
00293 
00294 short RenderSubmitButton::baselinePosition( bool f ) const
00295 {
00296     return RenderFormElement::baselinePosition( f );
00297 }
00298 
00299 // -------------------------------------------------------------------------------
00300 
00301 RenderImageButton::RenderImageButton(HTMLInputElementImpl *element)
00302     : RenderImage(element)
00303 {
00304     // ### support DOMActivate event when clicked
00305 }
00306 
00307 
00308 // -------------------------------------------------------------------------------
00309 
00310 RenderResetButton::RenderResetButton(HTMLInputElementImpl *element)
00311     : RenderSubmitButton(element)
00312 {
00313 }
00314 
00315 QString RenderResetButton::defaultLabel() {
00316     return i18n("Reset");
00317 }
00318 
00319 
00320 // -------------------------------------------------------------------------------
00321 
00322 RenderPushButton::RenderPushButton(HTMLInputElementImpl *element)
00323     : RenderSubmitButton(element)
00324 {
00325 }
00326 
00327 QString RenderPushButton::defaultLabel()
00328 {
00329     return QString::null;
00330 }
00331 
00332 // -------------------------------------------------------------------------------
00333 
00334 LineEditWidget::LineEditWidget(QWidget *parent)
00335         : KLineEdit(parent)
00336 {
00337     setMouseTracking(true);
00338 }
00339 
00340 QPopupMenu *LineEditWidget::createPopupMenu()
00341 {
00342     QPopupMenu *popup = KLineEdit::createPopupMenu();
00343     if ( !popup )
00344         return 0L;
00345     connect( popup, SIGNAL( activated( int ) ),
00346              this, SLOT( extendedMenuActivated( int ) ) );
00347     popup->insertSeparator();
00348     popup->insertItem( i18n("Clear History"), ClearHistory );
00349     return popup;
00350 }
00351 
00352 void LineEditWidget::extendedMenuActivated( int id)
00353 {
00354     switch ( id )
00355     {
00356     case ClearHistory:
00357         clearMenuHistory();
00358         break;
00359     default:
00360         break;
00361     }
00362 }
00363 
00364 void LineEditWidget::clearMenuHistory()
00365 {
00366     emit clearCompletionHistory();
00367 }
00368 
00369 
00370 bool LineEditWidget::event( QEvent *e )
00371 {
00372     if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
00373         QKeyEvent* ke = (QKeyEvent*) e;
00374         if ( ke->state() & ControlButton ) {
00375             switch ( ke->key() ) {
00376                 case Key_Left:
00377                 case Key_Right:
00378                 case Key_Up:
00379                 case Key_Down:
00380                 case Key_Home:
00381                 case Key_End:
00382                     ke->accept();
00383                 default:
00384                 break;
00385             }
00386         }
00387     }
00388     else if ( e->type() == QEvent::MouseButtonPress )
00389         emit pressed();
00390     else if ( e->type() == QEvent::MouseButtonRelease )
00391         emit released();
00392     return KLineEdit::event( e );
00393 }
00394 
00395 // -----------------------------------------------------------------------------
00396 
00397 RenderLineEdit::RenderLineEdit(HTMLInputElementImpl *element)
00398     : RenderFormElement(element)
00399 {
00400     LineEditWidget *edit = new LineEditWidget(view()->viewport());
00401     connect(edit,SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
00402     connect(edit,SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
00403     connect(edit,SIGNAL(pressed()), this, SLOT(slotPressed()));
00404     connect(edit,SIGNAL(released()), this, SLOT(slotReleased()));
00405     connect(edit, SIGNAL(clearCompletionHistory()), this, SLOT( slotClearCompletionHistory()));
00406     if(element->inputType() == HTMLInputElementImpl::PASSWORD)
00407         edit->setEchoMode( QLineEdit::Password );
00408 
00409     if ( element->autoComplete() ) {
00410         QStringList completions = view()->formCompletionItems(element->name().string());
00411         if (completions.count()) {
00412             edit->completionObject()->setItems(completions);
00413             edit->setContextMenuEnabled(true);
00414         }
00415     }
00416 
00417     setQWidget(edit);
00418 }
00419 
00420 void RenderLineEdit::slotClearCompletionHistory()
00421 {
00422     if ( element()->autoComplete() ) {
00423         view()->clearCompletionHistory(element()->name().string());
00424         static_cast<LineEditWidget*>(m_widget)->completionObject()->clear();
00425     }
00426 }
00427 
00428 void RenderLineEdit::slotReturnPressed()
00429 {
00430     // don't submit the form when return was pressed in a completion-popup
00431     KCompletionBox *box = widget()->completionBox(false);
00432     if ( box && box->isVisible() && box->currentItem() != -1 )
00433     return;
00434 
00435     // Emit onChange if necessary
00436     // Works but might not be enough, dirk said he had another solution at
00437     // hand (can't remember which) - David
00438     handleFocusOut();
00439 
00440     HTMLFormElementImpl* fe = element()->form();
00441     if ( fe )
00442         fe->submitFromKeyboard();
00443 }
00444 
00445 void RenderLineEdit::handleFocusOut()
00446 {
00447     if ( widget() && widget()->edited() ) {
00448         element()->onChange();
00449         widget()->setEdited( false );
00450     }
00451 }
00452 
00453 void RenderLineEdit::calcMinMaxWidth()
00454 {
00455     KHTMLAssert( !minMaxKnown() );
00456 
00457     const QFontMetrics &fm = style()->fontMetrics();
00458     QSize s;
00459 
00460     int size = element()->size();
00461 
00462     int h = fm.lineSpacing();
00463     int w = fm.width( 'x' ) * (size > 0 ? size+1 : 17); // "some"
00464     s = QSize(w + 2 + 2*widget()->frameWidth(),
00465               QMAX(h, 14) + 2 + 2*widget()->frameWidth())
00466         .expandedTo(QApplication::globalStrut());
00467 
00468     setIntrinsicWidth( s.width() );
00469     setIntrinsicHeight( s.height() );
00470 
00471     RenderFormElement::calcMinMaxWidth();
00472 }
00473 
00474 void RenderLineEdit::updateFromElement()
00475 {
00476     int ml = element()->maxLength();
00477     if ( ml < 0 || ml > 1024 )
00478         ml = 1024;
00479     if ( widget()->maxLength() != ml ) {
00480         widget()->blockSignals( true );
00481         widget()->setMaxLength( ml );
00482         widget()->blockSignals( false );
00483     }
00484 
00485     if (element()->value().string() != widget()->text()) {
00486         widget()->blockSignals(true);
00487         int pos = widget()->cursorPosition();
00488         widget()->setText(element()->value().string());
00489 
00490         widget()->setEdited( false );
00491 
00492         widget()->setCursorPosition(pos);
00493         widget()->blockSignals(false);
00494     }
00495     widget()->setReadOnly(element()->readOnly());
00496 
00497     RenderFormElement::updateFromElement();
00498 }
00499 
00500 void RenderLineEdit::slotTextChanged(const QString &string)
00501 {
00502     // don't use setValue here!
00503     element()->m_value = string;
00504 }
00505 
00506 void RenderLineEdit::select()
00507 {
00508     static_cast<LineEditWidget*>(m_widget)->selectAll();
00509 }
00510 
00511 // ---------------------------------------------------------------------------
00512 
00513 RenderFieldset::RenderFieldset(HTMLGenericFormElementImpl *element)
00514     : RenderFlow(element)
00515 {
00516 }
00517 
00518 bool RenderFieldset::findLegend( int &lx, int &ly, int &lw, int &lh)
00519 {
00520     RenderObject *r = this, *ref = 0;
00521     int minx = 0, curx = 0, maxw = 0;
00522     if( r->firstChild() && r->firstChild()->element() &&
00523         r->firstChild()->element()->id() == ID_LEGEND)
00524             r = r->firstChild();
00525     else
00526         return false;
00527     if(!r->firstChild() || r->isSpecial())
00528         return false;
00529     ly = r->yPos();
00530     minx = r->width();
00531     curx = r->xPos();
00532     lh = r->height();
00533     ref = r;
00534 
00535     while(r) {
00536         if(r->firstChild())
00537             r = r->firstChild();
00538         else if(r->nextSibling())
00539             r = r->nextSibling();
00540         else {
00541             RenderObject *next = 0;
00542             while(!next) {
00543                 r = r->parent();
00544                 if(!r || r == (RenderObject *)ref ) goto end;
00545                 next = r->nextSibling();
00546             }
00547             r = next;
00548         }
00549         if(r->isSpecial())
00550             continue;
00551         curx += r->xPos();
00552         if(r->width() && curx<minx)
00553              minx = curx;
00554         if(curx-minx+r->width() > maxw) {
00555                 maxw = curx-minx+r->width();
00556         }
00557         if(!r->childrenInline())
00558             curx -= r->xPos();
00559     }
00560     end:
00561         lx = minx - ref->paddingLeft();
00562         lw = maxw + ref->paddingLeft() + ref->paddingRight();
00563         if(lx < 0 || lx+lw > width())
00564             return false;
00565         return !!maxw;
00566 }
00567 
00568 void RenderFieldset::paintBoxDecorations(QPainter *p,int, int _y,
00569                                        int, int _h, int _tx, int _ty)
00570 {
00571     //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
00572 
00573     int w = width();
00574     int h = height() + borderTopExtra() + borderBottomExtra();
00575     int lx = 0, ly = 0, lw = 0, lh = 0;
00576     bool legend = findLegend(lx, ly, lw, lh);
00577 
00578     if(legend) {
00579         int yOff = ly + lh/2 - borderTop()/2;
00580         h -= yOff;
00581         _ty += yOff;
00582     }
00583     _ty -= borderTopExtra();
00584 
00585     int my = QMAX(_ty,_y);
00586     int end = QMIN( _y + _h,  _ty + h );
00587     int mh = end - my;
00588 
00589     paintBackground(p, style()->backgroundColor(), style()->backgroundImage(), my, mh, _tx, _ty, w, h);
00590 
00591     if ( style()->hasBorder() ) {
00592     if ( legend )
00593         paintBorderMinusLegend(p, _tx, _ty, w, h, style(), lx, lw);
00594     else
00595         paintBorder(p, _tx, _ty, w, h, style());
00596     }
00597 }
00598 
00599 void RenderFieldset::paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w, int h,
00600                                             const RenderStyle* style, int lx, int lw)
00601 {
00602 
00603     const QColor& tc = style->borderTopColor();
00604     const QColor& bc = style->borderBottomColor();
00605 
00606     EBorderStyle ts = style->borderTopStyle();
00607     EBorderStyle bs = style->borderBottomStyle();
00608     EBorderStyle ls = style->borderLeftStyle();
00609     EBorderStyle rs = style->borderRightStyle();
00610 
00611     bool render_t = ts > BHIDDEN;
00612     bool render_l = ls > BHIDDEN;
00613     bool render_r = rs > BHIDDEN;
00614     bool render_b = bs > BHIDDEN;
00615 
00616     if(render_t) {
00617         drawBorder(p, _tx, _ty, _tx + lx, _ty +  style->borderTopWidth(), BSTop, tc, style->color(), ts,
00618                    (render_l && ls<=DOUBLE?style->borderLeftWidth():0), 0);
00619         drawBorder(p, _tx+lx+lw, _ty, _tx + w, _ty +  style->borderTopWidth(), BSTop, tc, style->color(), ts,
00620                    0, (render_r && rs<=DOUBLE?style->borderRightWidth():0));
00621     }
00622 
00623     if(render_b)
00624         drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs,
00625                    (render_l && ls<=DOUBLE?style->borderLeftWidth():0),
00626            (render_r && rs<=DOUBLE?style->borderRightWidth():0));
00627 
00628     if(render_l)
00629     {
00630     const QColor& lc = style->borderLeftColor();
00631 
00632     bool ignore_top =
00633       (tc == lc) &&
00634       (ls <= OUTSET) &&
00635       (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
00636 
00637     bool ignore_bottom =
00638       (bc == lc) &&
00639       (ls <= OUTSET) &&
00640       (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
00641 
00642         drawBorder(p, _tx, _ty, _tx + style->borderLeftWidth(), _ty + h, BSLeft, lc, style->color(), ls,
00643            ignore_top?0:style->borderTopWidth(),
00644            ignore_bottom?0:style->borderBottomWidth());
00645     }
00646 
00647     if(render_r)
00648     {
00649     const QColor& rc = style->borderRightColor();
00650 
00651     bool ignore_top =
00652       (tc == rc) &&
00653       (rs <= SOLID || rs == INSET) &&
00654       (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET);
00655 
00656     bool ignore_bottom =
00657       (bc == rc) &&
00658       (rs <= SOLID || rs == INSET) &&
00659       (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET);
00660 
00661         drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _tx + w, _ty + h, BSRight, rc, style->color(), rs,
00662            ignore_top?0:style->borderTopWidth(),
00663            ignore_bottom?0:style->borderBottomWidth());
00664     }
00665 }
00666 
00667 // -------------------------------------------------------------------------
00668 
00669 RenderFileButton::RenderFileButton(HTMLInputElementImpl *element)
00670     : RenderFormElement(element)
00671 {
00672     // this sucks, it creates a grey background
00673     QHBox *w = new QHBox(view()->viewport());
00674 
00675     m_edit = new LineEditWidget(w);
00676 
00677     connect(m_edit, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed()));
00678     connect(m_edit, SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &)));
00679 
00680     m_button = new QPushButton(i18n("Browse..."), w);
00681     m_button->setFocusPolicy(QWidget::ClickFocus);
00682     connect(m_button,SIGNAL(clicked()), this, SLOT(slotClicked()));
00683     connect(m_button, SIGNAL(pressed()), this, SLOT(slotPressed()));
00684     connect(m_button, SIGNAL(released()), this, SLOT(slotReleased()));
00685 
00686     w->setStretchFactor(m_edit, 2);
00687     w->setFocusProxy(m_edit);
00688 
00689     setQWidget(w);
00690     m_haveFocus = false;
00691 }
00692 
00693 void RenderFileButton::calcMinMaxWidth()
00694 {
00695     KHTMLAssert( !minMaxKnown() );
00696 
00697     const QFontMetrics &fm = style()->fontMetrics();
00698     int size = element()->size();
00699 
00700     int h = fm.lineSpacing();
00701     int w = fm.width( 'x' ) * (size > 0 ? size : 17); // "some"
00702     QSize s = m_edit->style().sizeFromContents(QStyle::CT_LineEdit, m_edit,
00703           QSize(w + 2 + 2*m_edit->frameWidth(), kMax(h, 14) + 2 + 2*m_edit->frameWidth()))
00704         .expandedTo(QApplication::globalStrut());
00705     QSize bs = m_button->sizeHint();
00706 
00707     setIntrinsicWidth( s.width() + bs.width() );
00708     setIntrinsicHeight( kMax(s.height(), bs.height()) );
00709 
00710     RenderFormElement::calcMinMaxWidth();
00711 }
00712 
00713 void RenderFileButton::handleFocusOut()
00714 {
00715     if ( m_edit && m_edit->edited() ) {
00716         element()->onChange();
00717         m_edit->setEdited( false );
00718     }
00719 }
00720 
00721 void RenderFileButton::slotClicked()
00722 {
00723     QString file_name = KFileDialog::getOpenFileName(QString::null, QString::null, 0, i18n("Browse"));
00724     if (!file_name.isNull()) {
00725         element()->m_value = DOMString(file_name);
00726         m_edit->setText(file_name);
00727     }
00728 }
00729 
00730 void RenderFileButton::updateFromElement()
00731 {
00732     m_edit->blockSignals(true);
00733     m_edit->setText(element()->value().string());
00734     m_edit->blockSignals(false);
00735     int ml = element()->maxLength();
00736     if ( ml < 0 || ml > 1024 )
00737         ml = 1024;
00738     m_edit->setMaxLength( ml );
00739     m_edit->setEdited( false );
00740 
00741     RenderFormElement::updateFromElement();
00742 }
00743 
00744 void RenderFileButton::slotReturnPressed()
00745 {
00746     if (element()->form())
00747     element()->form()->submitFromKeyboard();
00748 }
00749 
00750 void RenderFileButton::slotTextChanged(const QString &string)
00751 {
00752    element()->m_value = DOMString(string);
00753 }
00754 
00755 void RenderFileButton::select()
00756 {
00757     m_edit->selectAll();
00758 }
00759 
00760 // -------------------------------------------------------------------------
00761 
00762 RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element)
00763     : RenderFormElement(element)
00764 {
00765 
00766 }
00767 
00768 // -------------------------------------------------------------------------
00769 
00770 RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element)
00771     : RenderFlow(element)
00772 {
00773     setInline(false);
00774 }
00775 
00776 // -------------------------------------------------------------------------------
00777 
00778 ComboBoxWidget::ComboBoxWidget(QWidget *parent)
00779     : KComboBox(false, parent)
00780 {
00781     setAutoMask(true);
00782     if (listBox()) listBox()->installEventFilter(this);
00783     setMouseTracking(true);
00784 }
00785 
00786 bool ComboBoxWidget::event(QEvent *e)
00787 {
00788     if (e->type()==QEvent::KeyPress)
00789     {
00790     QKeyEvent *ke = static_cast<QKeyEvent *>(e);
00791     switch(ke->key())
00792     {
00793     case Key_Return:
00794     case Key_Enter:
00795         popup();
00796         ke->accept();
00797         return true;
00798     default:
00799         return KComboBox::event(e);
00800     }
00801     }
00802     return KComboBox::event(e);
00803 }
00804 
00805 bool ComboBoxWidget::eventFilter(QObject *dest, QEvent *e)
00806 {
00807     if (dest==listBox() &&  e->type()==QEvent::KeyPress)
00808     {
00809     QKeyEvent *ke = static_cast<QKeyEvent *>(e);
00810     bool forward = false;
00811     switch(ke->key())
00812     {
00813     case Key_Tab:
00814         forward=true;
00815     case Key_BackTab:
00816         // ugly hack. emulate popdownlistbox() (private in QComboBox)
00817         // we re-use ke here to store the reference to the generated event.
00818         ke = new QKeyEvent(QEvent::KeyPress, Key_Escape, 0, 0);
00819         QApplication::sendEvent(dest,ke);
00820         focusNextPrevChild(forward);
00821         delete ke;
00822         return true;
00823     default:
00824         return KComboBox::eventFilter(dest, e);
00825     }
00826     }
00827     return KComboBox::eventFilter(dest, e);
00828 }
00829 
00830 // -------------------------------------------------------------------------
00831 
00832 RenderSelect::RenderSelect(HTMLSelectElementImpl *element)
00833     : RenderFormElement(element)
00834 {
00835     m_ignoreSelectEvents = false;
00836     m_multiple = element->multiple();
00837     m_size = element->size();
00838     m_useListBox = (m_multiple || m_size > 1);
00839     m_selectionChanged = true;
00840     m_optionsChanged = true;
00841 
00842     if(m_useListBox)
00843         setQWidget(createListBox());
00844     else
00845         setQWidget(createComboBox());
00846 }
00847 
00848 void RenderSelect::updateFromElement()
00849 {
00850     m_ignoreSelectEvents = true;
00851 
00852     // change widget type
00853     bool oldMultiple = m_multiple;
00854     unsigned oldSize = m_size;
00855     bool oldListbox = m_useListBox;
00856 
00857     m_multiple = element()->multiple();
00858     m_size = element()->size();
00859     m_useListBox = (m_multiple || m_size > 1);
00860 
00861     if (oldMultiple != m_multiple || oldSize != m_size) {
00862         if (m_useListBox != oldListbox) {
00863             // type of select has changed
00864             if(m_useListBox)
00865                 setQWidget(createListBox());
00866             else
00867                 setQWidget(createComboBox());
00868         }
00869 
00870         if (m_useListBox && oldMultiple != m_multiple) {
00871             static_cast<KListBox*>(m_widget)->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
00872         }
00873         m_selectionChanged = true;
00874         m_optionsChanged = true;
00875     }
00876 
00877     // update contents listbox/combobox based on options in m_element
00878     if ( m_optionsChanged ) {
00879         if (element()->m_recalcListItems)
00880             element()->recalcListItems();
00881         QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
00882         int listIndex;
00883 
00884         if(m_useListBox) {
00885             static_cast<KListBox*>(m_widget)->clear();
00886         }
00887 
00888         else
00889             static_cast<KComboBox*>(m_widget)->clear();
00890 
00891         for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) {
00892             if (listItems[listIndex]->id() == ID_OPTGROUP) {
00893                 DOMString text = listItems[listIndex]->getAttribute(ATTR_LABEL);
00894                 if (text.isNull())
00895                     text = "";
00896 
00897                 if(m_useListBox) {
00898                     QListBoxText *item = new QListBoxText(QString(text.implementation()->s, text.implementation()->l));
00899                     static_cast<KListBox*>(m_widget)
00900                         ->insertItem(item, listIndex);
00901                     item->setSelectable(false);
00902                 }
00903                 else
00904                     static_cast<KComboBox*>(m_widget)
00905                         ->insertItem(QString(text.implementation()->s, text.implementation()->l), listIndex);
00906             }
00907             else if (listItems[listIndex]->id() == ID_OPTION) {
00908                 HTMLOptionElementImpl* optElem = static_cast<HTMLOptionElementImpl*>(listItems[listIndex]);
00909                 QString text = optElem->text().string();
00910                 if (optElem->parentNode()->id() == ID_OPTGROUP)
00911                 {
00912                     // Prefer label if set
00913                     DOMString label = optElem->getAttribute(ATTR_LABEL);
00914                     if (!label.isEmpty())
00915                         text = label.string();
00916                     text = QString::fromLatin1("    ")+text;
00917                 }
00918 
00919                 if(m_useListBox)
00920                     static_cast<KListBox*>(m_widget)->insertItem(text, listIndex);
00921                 else
00922                     static_cast<KComboBox*>(m_widget)->insertItem(text, listIndex);
00923             }
00924             else
00925                 KHTMLAssert(false);
00926             m_selectionChanged = true;
00927         }
00928         setMinMaxKnown(false);
00929         setLayouted(false);
00930         m_optionsChanged = false;
00931     }
00932 
00933     // update selection
00934     if (m_selectionChanged) {
00935         updateSelection();
00936     }
00937 
00938 
00939     m_ignoreSelectEvents = false;
00940 
00941     RenderFormElement::updateFromElement();
00942 }
00943 
00944 void RenderSelect::calcMinMaxWidth()
00945 {
00946     KHTMLAssert( !minMaxKnown() );
00947 
00948     if (m_optionsChanged)
00949         updateFromElement();
00950 
00951     // ### ugly HACK FIXME!!!
00952     setMinMaxKnown();
00953     if ( !layouted() )
00954         layout();
00955     setLayouted( false );
00956     setMinMaxKnown( false );
00957     // ### end FIXME
00958 
00959     RenderFormElement::calcMinMaxWidth();
00960 }
00961 
00962 void RenderSelect::layout( )
00963 {
00964     KHTMLAssert(!layouted());
00965     KHTMLAssert(minMaxKnown());
00966 
00967     // ### maintain selection properly between type/size changes, and work
00968     // out how to handle multiselect->singleselect (probably just select
00969     // first selected one)
00970 
00971     // calculate size
00972     if(m_useListBox) {
00973         KListBox* w = static_cast<KListBox*>(m_widget);
00974 
00975         QListBoxItem* p = w->firstItem();
00976         int width = 0;
00977         int height = 0;
00978         while(p) {
00979             width = QMAX(width, p->width(p->listBox()));
00980             height = QMAX(height, p->height(p->listBox()));
00981             p = p->next();
00982         }
00983 
00984         int size = m_size;
00985         // check if multiple and size was not given or invalid
00986         // Internet Exploder sets size to QMIN(number of elements, 4)
00987         // Netscape seems to simply set it to "number of elements"
00988         // the average of that is IMHO QMIN(number of elements, 10)
00989         // so I did that ;-)
00990         if(size < 1)
00991             size = QMIN(static_cast<KListBox*>(m_widget)->count(), 10);
00992 
00993         width += 2*w->frameWidth() + w->verticalScrollBar()->sizeHint().width();
00994         height = size*height + 2*w->frameWidth();
00995 
00996         setIntrinsicWidth( width );
00997         setIntrinsicHeight( height );
00998     }
00999     else {
01000         QSize s(m_widget->sizeHint());
01001         setIntrinsicWidth( s.width() );
01002         setIntrinsicHeight( s.height() );
01003     }
01004 
01006     setLayouted( false );
01007     RenderFormElement::layout();
01008 
01009     // and now disable the widget in case there is no <option> given
01010     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
01011 
01012     bool foundOption = false;
01013     for (uint i = 0; i < listItems.size() && !foundOption; i++)
01014     foundOption = (listItems[i]->id() == ID_OPTION);
01015 
01016     m_widget->setEnabled(foundOption && ! element()->disabled());
01017 }
01018 
01019 void RenderSelect::slotSelected(int index) // emitted by the combobox only
01020 {
01021     if ( m_ignoreSelectEvents ) return;
01022 
01023     KHTMLAssert( !m_useListBox );
01024 
01025     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
01026     if(index >= 0 && index < int(listItems.size()))
01027     {
01028         bool found = ( listItems[index]->id() == ID_OPTION );
01029 
01030         if ( !found ) {
01031             // this one is not selectable,  we need to find an option element
01032             while ( ( unsigned ) index < listItems.size() ) {
01033                 if ( listItems[index]->id() == ID_OPTION ) {
01034                     found = true;
01035                     break;
01036                 }
01037                 ++index;
01038             }
01039 
01040             if ( !found ) {
01041                 while ( index >= 0 ) {
01042                     if ( listItems[index]->id() == ID_OPTION ) {
01043                         found = true;
01044                         break;
01045                     }
01046                     --index;
01047                 }
01048             }
01049         }
01050 
01051         if ( found ) {
01052             bool changed = false;
01053 
01054             for ( unsigned int i = 0; i < listItems.size(); ++i )
01055                 if ( listItems[i]->id() == ID_OPTION && i != (unsigned int) index )
01056                 {
01057                     HTMLOptionElementImpl* opt = static_cast<HTMLOptionElementImpl*>( listItems[i] );
01058                     changed |= (opt->m_selected == true);
01059                     opt->m_selected = false;
01060                 }
01061 
01062             HTMLOptionElementImpl* opt = static_cast<HTMLOptionElementImpl*>(listItems[index]);
01063             changed |= (opt->m_selected == false);
01064             opt->m_selected = true;
01065 
01066             if ( index != static_cast<ComboBoxWidget*>( m_widget )->currentItem() )
01067                 static_cast<ComboBoxWidget*>( m_widget )->setCurrentItem( index );
01068 
01069             // When selecting an optgroup item, and we move forward to we
01070             // shouldn't emit onChange. Hence this bool, the if above doesn't do it.
01071             if ( changed )
01072             {
01073                 ref();
01074                 element()->onChange();
01075                 deref();
01076             }
01077         }
01078     }
01079 }
01080 
01081 
01082 void RenderSelect::slotSelectionChanged() // emitted by the listbox only
01083 {
01084     if ( m_ignoreSelectEvents ) return;
01085 
01086     // don't use listItems() here as we have to avoid recalculations - changing the
01087     // option list will make use update options not in the way the user expects them
01088     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->m_listItems;
01089     for ( unsigned i = 0; i < listItems.count(); i++ )
01090         // don't use setSelected() here because it will cause us to be called
01091         // again with updateSelection.
01092         if ( listItems[i]->id() == ID_OPTION )
01093             static_cast<HTMLOptionElementImpl*>( listItems[i] )
01094                 ->m_selected = static_cast<KListBox*>( m_widget )->isSelected( i );
01095 
01096     ref();
01097     element()->onChange();
01098     deref();
01099 }
01100 
01101 void RenderSelect::setOptionsChanged(bool _optionsChanged)
01102 {
01103     m_optionsChanged = _optionsChanged;
01104 }
01105 
01106 KListBox* RenderSelect::createListBox()
01107 {
01108     KListBox *lb = new KListBox(view()->viewport());
01109     lb->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single);
01110     // ### looks broken
01111     //lb->setAutoMask(true);
01112     connect( lb, SIGNAL( selectionChanged() ), this, SLOT( slotSelectionChanged() ) );
01113     connect( lb, SIGNAL( clicked( QListBoxItem * ) ), this, SLOT( slotClicked() ) );
01114     m_ignoreSelectEvents = false;
01115     lb->setMouseTracking(true);
01116 
01117     return lb;
01118 }
01119 
01120 ComboBoxWidget *RenderSelect::createComboBox()
01121 {
01122     ComboBoxWidget *cb = new ComboBoxWidget(view()->viewport());
01123     connect(cb, SIGNAL(activated(int)), this, SLOT(slotSelected(int)));
01124     return cb;
01125 }
01126 
01127 void RenderSelect::updateSelection()
01128 {
01129     QMemArray<HTMLGenericFormElementImpl*> listItems = element()->listItems();
01130     int i;
01131     if (m_useListBox) {
01132         // if multi-select, we select only the new selected index
01133         KListBox *listBox = static_cast<KListBox*>(m_widget);
01134         for (i = 0; i < int(listItems.size()); i++)
01135             listBox->setSelected(i,listItems[i]->id() == ID_OPTION &&
01136                                 static_cast<HTMLOptionElementImpl*>(listItems[i])->selected());
01137     }
01138     else {
01139         bool found = false;
01140         unsigned firstOption = listItems.size();
01141         i = listItems.size();
01142         while (i--)
01143             if (listItems[i]->id() == ID_OPTION) {
01144                 if (found)
01145                     static_cast<HTMLOptionElementImpl*>(listItems[i])->m_selected = false;
01146                 else if (static_cast<HTMLOptionElementImpl*>(listItems[i])->selected()) {
01147                     static_cast<KComboBox*>( m_widget )->setCurrentItem(i);
01148                     found = true;
01149                 }
01150                 firstOption = i;
01151             }
01152 
01153         Q_ASSERT(firstOption == listItems.size() || found);
01154     }
01155 
01156     m_selectionChanged = false;
01157 }
01158 
01159 
01160 // -------------------------------------------------------------------------
01161 
01162 TextAreaWidget::TextAreaWidget(int wrap, QWidget* parent)
01163     : KTextEdit(parent)
01164 {
01165     if(wrap != DOM::HTMLTextAreaElementImpl::ta_NoWrap) {
01166         setWordWrap(QTextEdit::WidgetWidth);
01167         setHScrollBarMode( AlwaysOff );
01168         setVScrollBarMode( AlwaysOn );
01169     }
01170     else {
01171         setWordWrap(QTextEdit::NoWrap);
01172         setHScrollBarMode( Auto );
01173         setVScrollBarMode( Auto );
01174     }
01175     KCursor::setAutoHideCursor(viewport(), true);
01176     setTextFormat(QTextEdit::PlainText);
01177     setAutoMask(true);
01178     setMouseTracking(true);
01179 }
01180 
01181 bool TextAreaWidget::event( QEvent *e )
01182 {
01183     if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) {
01184         QKeyEvent* ke = (QKeyEvent*) e;
01185         if ( ke->state() & ControlButton ) {
01186             switch ( ke->key() ) {
01187                 case Key_Left:
01188                 case Key_Right:
01189                 case Key_Up:
01190                 case Key_Down:
01191                 case Key_Home:
01192                 case Key_End:
01193                     ke->accept();
01194                 default:
01195                 break;
01196             }
01197         }
01198     }
01199     return KTextEdit::event( e );
01200 }
01201 
01202 // -------------------------------------------------------------------------
01203 
01204 RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
01205     : RenderFormElement(element)
01206 {
01207     TextAreaWidget *edit = new TextAreaWidget(element->wrap(), view());
01208     setQWidget(edit);
01209 
01210     connect(edit,SIGNAL(textChanged()),this,SLOT(slotTextChanged()));
01211 }
01212 
01213 RenderTextArea::~RenderTextArea()
01214 {
01215     if ( element()->m_dirtyvalue ) {
01216         element()->m_value = text();
01217         element()->m_dirtyvalue = false;
01218     }
01219 }
01220 
01221 void RenderTextArea::handleFocusOut()
01222 {
01223     TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
01224     if ( w && element()->m_dirtyvalue ) {
01225         element()->m_value = text();
01226         element()->m_dirtyvalue = false;
01227         element()->onChange();
01228     }
01229 }
01230 
01231 void RenderTextArea::calcMinMaxWidth()
01232 {
01233     KHTMLAssert( !minMaxKnown() );
01234 
01235     TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
01236     const QFontMetrics &m = style()->fontMetrics();
01237     w->setTabStopWidth(8 * m.width(" "));
01238     QSize size( QMAX(element()->cols(), 1)*m.width('x') + w->frameWidth() +
01239                 w->verticalScrollBar()->sizeHint().width(),
01240                 QMAX(element()->rows(), 1)*m.lineSpacing() + w->frameWidth()*4 +
01241                 (w->wordWrap() == QTextEdit::NoWrap ?
01242                  w->horizontalScrollBar()->sizeHint().height() : 0)
01243         );
01244 
01245     setIntrinsicWidth( size.width() );
01246     setIntrinsicHeight( size.height() );
01247 
01248     RenderFormElement::calcMinMaxWidth();
01249 }
01250 
01251 void RenderTextArea::updateFromElement()
01252 {
01253     TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
01254     w->setReadOnly(element()->readOnly());
01255     QString elementText = element()->value().string();
01256     if ( elementText != text() )
01257     {
01258         w->blockSignals(true);
01259         int line, col;
01260         w->getCursorPosition( &line, &col );
01261         w->setText( elementText );
01262         w->setCursorPosition( line, col );
01263         w->blockSignals(false);
01264     }
01265     element()->m_dirtyvalue = false;
01266 
01267     RenderFormElement::updateFromElement();
01268 }
01269 
01270 void RenderTextArea::close( )
01271 {
01272     element()->setValue( element()->defaultValue() );
01273 
01274     RenderFormElement::close();
01275 }
01276 
01277 QString RenderTextArea::text()
01278 {
01279     QString txt;
01280     TextAreaWidget* w = static_cast<TextAreaWidget*>(m_widget);
01281 
01282     if(element()->wrap() == DOM::HTMLTextAreaElementImpl::ta_Physical) {
01283         // yeah, QTextEdit has no accessor for getting the visually wrapped text
01284         for (int p=0; p < w->paragraphs(); ++p) {
01285             int pl = w->paragraphLength(p);
01286             int ll = 0;
01287             int lindex = w->lineOfChar(p, 0);
01288             QString paragraphText = w->text(p);
01289             for (int l = 0; l < pl; ++l) {
01290                 if (lindex != w->lineOfChar(p, l)) {
01291                     paragraphText.insert(l+ll++, QString::fromLatin1("\n"));
01292                     lindex = w->lineOfChar(p, l);
01293                 }
01294             }
01295             txt += paragraphText;
01296             if (p < w->paragraphs() - 1)
01297                 txt += QString::fromLatin1("\n");
01298         }
01299     }
01300     else
01301         txt = w->text();
01302 
01303     return txt;
01304 }
01305 
01306 void RenderTextArea::slotTextChanged()
01307 {
01308     element()->m_dirtyvalue = true;
01309 }
01310 
01311 void RenderTextArea::select()
01312 {
01313     static_cast<TextAreaWidget *>(m_widget)->selectAll();
01314 }
01315 
01316 // ---------------------------------------------------------------------------
01317 
01318 #include "render_form.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Mar 16 18:45:39 2005 by doxygen 1.3.3 written by Dimitri van Heesch, © 1997-2001