khtml Library API Documentation

render_form.cpp

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