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