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