/* * This file is part of the DOM implementation for KDE. * * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) * Copyright (C) 2004 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include #include "misc/helper.h" #include "xml/dom2_eventsimpl.h" #include "html/html_formimpl.h" #include "misc/htmlhashes.h" #include "rendering/render_form.h" #include #include "khtmlview.h" #include "khtml_ext.h" #include "xml/dom_docimpl.h" #include #if APPLE_CHANGES #include "KWQFileButton.h" #include "KWQSlider.h" #endif using namespace khtml; using namespace DOM; RenderFormElement::RenderFormElement(HTMLGenericFormElementImpl *element) : RenderWidget(element) { setInline(true); } RenderFormElement::~RenderFormElement() { } short RenderFormElement::baselinePosition( bool f, bool isRootLineBox ) const { #if APPLE_CHANGES return marginTop() + widget()->baselinePosition(m_height); #else return RenderWidget::baselinePosition( f, isRootLineBox ) - 2 - style()->fontMetrics().descent(); #endif } void RenderFormElement::setStyle(RenderStyle* s) { #if APPLE_CHANGES if (canHaveIntrinsicMargins()) addIntrinsicMarginsIfAllowed(s); #endif RenderWidget::setStyle(s); #if APPLE_CHANGES // Do not paint a background or border for Aqua form elements setShouldPaintBackgroundOrBorder(false); #endif m_widget->setFont(style()->font()); } void RenderFormElement::updateFromElement() { m_widget->setEnabled(!element()->disabled()); #if APPLE_CHANGES m_widget->setPalette(QPalette(style()->backgroundColor(), style()->color())); #else QColor color = style()->color(); QColor backgroundColor = style()->backgroundColor(); if ( color.isValid() || backgroundColor.isValid() ) { QPalette pal(m_widget->palette()); int contrast_ = KGlobalSettings::contrast(); int highlightVal = 100 + (2*contrast_+4)*16/10; int lowlightVal = 100 + (2*contrast_+4)*10; if (backgroundColor.isValid()) { for ( int i = 0; i < QPalette::NColorGroups; i++ ) { pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Background, backgroundColor ); pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Light, backgroundColor.light(highlightVal) ); pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Dark, backgroundColor.dark(lowlightVal) ); pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Mid, backgroundColor.dark(120) ); pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Midlight, backgroundColor.light(110) ); pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Button, backgroundColor ); pal.setColor( (QPalette::ColorGroup)i, QColorGroup::Base, backgroundColor ); } } if ( color.isValid() ) { struct ColorSet { QPalette::ColorGroup cg; QColorGroup::ColorRole cr; }; const struct ColorSet toSet [] = { { QPalette::Active, QColorGroup::Foreground }, { QPalette::Active, QColorGroup::ButtonText }, { QPalette::Active, QColorGroup::Text }, { QPalette::Inactive, QColorGroup::Foreground }, { QPalette::Inactive, QColorGroup::ButtonText }, { QPalette::Inactive, QColorGroup::Text }, { QPalette::Disabled,QColorGroup::ButtonText }, { QPalette::NColorGroups, QColorGroup::NColorRoles }, }; const ColorSet *set = toSet; while( set->cg != QPalette::NColorGroups ) { pal.setColor( set->cg, set->cr, color ); ++set; } QColor disfg = color; int h, s, v; disfg.hsv( &h, &s, &v ); if (v > 128) // dark bg, light fg - need a darker disabled fg disfg = disfg.dark(lowlightVal); else if (disfg != Qt::black) // light bg, dark fg - need a lighter disabled fg - but only if !black disfg = disfg.light(highlightVal); else // black fg - use darkgrey disabled fg disfg = Qt::darkGray; pal.setColor(QPalette::Disabled,QColorGroup::Foreground,disfg); } m_widget->setPalette(pal); } else m_widget->unsetPalette(); #endif } void RenderFormElement::layout() { KHTMLAssert( needsLayout() ); KHTMLAssert( minMaxKnown() ); // minimum height m_height = 0; calcWidth(); calcHeight(); #if !APPLE_CHANGES if ( m_widget ) resizeWidget(m_widget, m_width-borderLeft()-borderRight()-paddingLeft()-paddingRight(), m_height-borderLeft()-borderRight()-paddingLeft()-paddingRight()); #endif setNeedsLayout(false); } void RenderFormElement::slotClicked() { // FIXME: Should share code with KHTMLView::dispatchMouseEvent, which does a lot of the same stuff. RenderArena *arena = ref(); #if APPLE_CHANGES QMouseEvent event(QEvent::MouseButtonRelease); // gets "current event" element()->dispatchMouseEvent(&event, EventImpl::CLICK_EVENT, event.clickCount()); #else // We also send the KHTML_CLICK or KHTML_DBLCLICK event for // CLICK. This is not part of the DOM specs, but is used for // compatibility with the traditional onclick="" and ondblclick="" // attributes, as there is no way to tell the difference between // single & double clicks using DOM (only the click count is // stored, which is not necessarily the same) QMouseEvent e2(QEvent::MouseButtonRelease, m_mousePos, m_button, m_state); element()->dispatchMouseEvent(&e2, EventImpl::CLICK_EVENT, m_clickCount); element()->dispatchMouseEvent(&e2, m_isDoubleClick ? EventImpl::DBLCLICK_EVENT : EventImpl::CLICK_EVENT, m_clickCount); #endif deref(arena); } Qt::AlignmentFlags RenderFormElement::textAlignment() const { switch (style()->textAlign()) { case LEFT: case KHTML_LEFT: return AlignLeft; case RIGHT: case KHTML_RIGHT: return AlignRight; case CENTER: case KHTML_CENTER: return AlignHCenter; case JUSTIFY: // Just fall into the auto code for justify. case TAAUTO: return style()->direction() == RTL ? AlignRight : AlignLeft; } assert(false); // Should never be reached. return AlignLeft; } #if APPLE_CHANGES void RenderFormElement::addIntrinsicMarginsIfAllowed(RenderStyle* _style) { // Cut out the intrinsic margins completely if we end up using mini controls. if (_style->font().pixelSize() < 11) return; int m = intrinsicMargin(); if (_style->width().isVariable()) { if (_style->marginLeft().quirk) _style->setMarginLeft(Length(m, Fixed)); if (_style->marginRight().quirk) _style->setMarginRight(Length(m, Fixed)); } if (_style->height().isVariable()) { if (_style->marginTop().quirk) _style->setMarginTop(Length(m, Fixed)); if (_style->marginBottom().quirk) _style->setMarginBottom(Length(m, Fixed)); } } #endif // ------------------------------------------------------------------------- RenderButton::RenderButton(HTMLGenericFormElementImpl *element) : RenderFormElement(element) { } short RenderButton::baselinePosition( bool f, bool isRootLineBox ) const { #if APPLE_CHANGES return RenderFormElement::baselinePosition( f, isRootLineBox ); #else return RenderWidget::baselinePosition( f, isRootLineBox ) - 2; #endif } void RenderButton::calcMinMaxWidth() { RenderFormElement::calcMinMaxWidth(); m_minWidth = m_maxWidth; setMinMaxKnown(); } // ------------------------------------------------------------------------------- RenderCheckBox::RenderCheckBox(HTMLInputElementImpl *element) : RenderButton(element) { QCheckBox* b = new QCheckBox(view()->viewport()); b->setAutoMask(true); b->setMouseTracking(true); setQWidget(b); connect(b,SIGNAL(stateChanged(int)),this,SLOT(slotStateChanged(int))); connect(b, SIGNAL(clicked()), this, SLOT(slotClicked())); } void RenderCheckBox::calcMinMaxWidth() { KHTMLAssert( !minMaxKnown() ); #if APPLE_CHANGES // Let the widget tell us how big it wants to be. QSize s(widget()->sizeHint()); #else QCheckBox *cb = static_cast( m_widget ); QSize s( cb->style().pixelMetric( QStyle::PM_IndicatorWidth ), cb->style().pixelMetric( QStyle::PM_IndicatorHeight ) ); #endif setIntrinsicWidth( s.width() ); setIntrinsicHeight( s.height() ); RenderButton::calcMinMaxWidth(); } void RenderCheckBox::updateFromElement() { widget()->setChecked(element()->checked()); RenderButton::updateFromElement(); } // From the Qt documentation: // state is 2 if the button is on, 1 if it is in the "no change" state or 0 if the button is off. void RenderCheckBox::slotStateChanged(int state) { element()->setChecked(state == 2); element()->onChange(); } // ------------------------------------------------------------------------------- RenderRadioButton::RenderRadioButton(HTMLInputElementImpl *element) : RenderButton(element) { QRadioButton* b = new QRadioButton(view()->viewport()); b->setAutoMask(true); b->setMouseTracking(true); setQWidget(b); connect(b, SIGNAL(clicked()), this, SLOT(slotClicked())); } void RenderRadioButton::updateFromElement() { widget()->setChecked(element()->checked()); RenderButton::updateFromElement(); } void RenderRadioButton::slotClicked() { element()->setChecked(true); // emit mouseClick event etc RenderButton::slotClicked(); } void RenderRadioButton::calcMinMaxWidth() { KHTMLAssert( !minMaxKnown() ); #if APPLE_CHANGES // Let the widget tell us how big it wants to be. QSize s(widget()->sizeHint()); #else QRadioButton *rb = static_cast( m_widget ); QSize s( rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorWidth ), rb->style().pixelMetric( QStyle::PM_ExclusiveIndicatorHeight ) ); #endif setIntrinsicWidth( s.width() ); setIntrinsicHeight( s.height() ); RenderButton::calcMinMaxWidth(); } // ------------------------------------------------------------------------------- RenderSubmitButton::RenderSubmitButton(HTMLInputElementImpl *element) : RenderButton(element) { QPushButton* p = new QPushButton(view()->viewport()); setQWidget(p); p->setAutoMask(true); p->setMouseTracking(true); connect(p, SIGNAL(clicked()), this, SLOT(slotClicked())); } QString RenderSubmitButton::rawText() { QString value = element()->valueWithDefault().string(); value = value.stripWhiteSpace(); value.replace(QChar('\\'), backslashAsCurrencySymbol()); #if APPLE_CHANGES return value; #else QString raw; for(unsigned int i = 0; i < value.length(); i++) { raw += value[i]; if(value[i] == '&') raw += '&'; } return raw; #endif } void RenderSubmitButton::calcMinMaxWidth() { KHTMLAssert( !minMaxKnown() ); #if APPLE_CHANGES // Let the widget tell us how big it wants to be. QSize s(widget()->sizeHint()); setIntrinsicWidth(s.width()); setIntrinsicHeight(s.height()); #else QString raw = rawText(); QPushButton* pb = static_cast(m_widget); pb->setText(raw); pb->setFont(style()->font()); bool empty = raw.isEmpty(); if ( empty ) raw = QString::fromLatin1("XXXX"); QFontMetrics fm = pb->fontMetrics(); int margin = pb->style().pixelMetric( QStyle::PM_ButtonMargin, pb); QSize s(pb->style().sizeFromContents( QStyle::CT_PushButton, pb, fm.size( ShowPrefix, raw)) .expandedTo(QApplication::globalStrut())); setIntrinsicWidth( s.width() - margin / 2 ); setIntrinsicHeight( s.height() - margin / 2); #endif RenderButton::calcMinMaxWidth(); } #if APPLE_CHANGES void RenderSubmitButton::setStyle(RenderStyle *s) { RenderButton::setStyle(s); QPushButton *w = static_cast(m_widget); w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR); } #endif void RenderSubmitButton::updateFromElement() { QPushButton *w = static_cast(m_widget); QString oldText = w->text(); QString newText = rawText(); w->setText(newText); if ( oldText != newText ) setNeedsLayoutAndMinMaxRecalc(); RenderFormElement::updateFromElement(); } short RenderSubmitButton::baselinePosition( bool f, bool isRootLineBox ) const { return RenderFormElement::baselinePosition( f, isRootLineBox ); } // ------------------------------------------------------------------------------- RenderImageButton::RenderImageButton(HTMLInputElementImpl *element) : RenderImage(element) { // ### support DOMActivate event when clicked } // ------------------------------------------------------------------------------- // FIXME: No real reason to need separate classes for RenderResetButton and // RenderSubmitButton now that the default label is handled on the DOM side. RenderResetButton::RenderResetButton(HTMLInputElementImpl *element) : RenderSubmitButton(element) { } // ------------------------------------------------------------------------------- // FIXME: No real reason to need separate classes for RenderPushButton and // RenderSubmitButton now that the default label is handled on the DOM side. RenderPushButton::RenderPushButton(HTMLInputElementImpl *element) : RenderSubmitButton(element) { } // ------------------------------------------------------------------------------- #if !APPLE_CHANGES LineEditWidget::LineEditWidget(QWidget *parent) : KLineEdit(parent) { setMouseTracking(true); } bool LineEditWidget::event( QEvent *e ) { if ( e->type() == QEvent::AccelAvailable && isReadOnly() ) { QKeyEvent* ke = (QKeyEvent*) e; if ( ke->state() & ControlButton ) { switch ( ke->key() ) { case Key_Left: case Key_Right: case Key_Up: case Key_Down: case Key_Home: case Key_End: ke->accept(); default: break; } } } return KLineEdit::event( e ); } #endif // ----------------------------------------------------------------------------- RenderLineEdit::RenderLineEdit(HTMLInputElementImpl *element) : RenderFormElement(element), m_updating(false) { #if APPLE_CHANGES QLineEdit::Type type; switch (element->inputType()) { case HTMLInputElementImpl::PASSWORD: type = QLineEdit::Password; break; case HTMLInputElementImpl::SEARCH: type = QLineEdit::Search; break; default: type = QLineEdit::Normal; } KLineEdit *edit = new KLineEdit(type); if (type == QLineEdit::Search) edit->setLiveSearch(false); #else LineEditWidget *edit = new LineEditWidget(view()->viewport()); #endif connect(edit,SIGNAL(returnPressed()), this, SLOT(slotReturnPressed())); connect(edit,SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &))); connect(edit,SIGNAL(clicked()),this,SLOT(slotClicked())); #if APPLE_CHANGES connect(edit,SIGNAL(performSearch()), this, SLOT(slotPerformSearch())); #endif #if !APPLE_CHANGES if(element->inputType() == HTMLInputElementImpl::PASSWORD) edit->setEchoMode( QLineEdit::Password ); if ( element->autoComplete() ) { QStringList completions = view()->formCompletionItems(element->name().string()); if (completions.count()) { edit->completionObject()->setItems(completions); edit->setContextMenuEnabled(true); } } #endif setQWidget(edit); } void RenderLineEdit::slotReturnPressed() { #if !APPLE_CHANGES // don't submit the form when return was pressed in a completion-popup KCompletionBox *box = widget()->completionBox(false); if ( box && box->isVisible() && box->currentItem() != -1 ) return; #endif // Emit onChange if necessary // Works but might not be enough, dirk said he had another solution at // hand (can't remember which) - David if (isTextField() && isEdited()) { element()->onChange(); setEdited(false); } HTMLFormElementImpl* fe = element()->form(); if ( fe ) fe->submitClick(); } #if APPLE_CHANGES void RenderLineEdit::slotPerformSearch() { // Fire the "search" DOM event. element()->dispatchHTMLEvent(EventImpl::SEARCH_EVENT, true, false); } void RenderLineEdit::addSearchResult() { if (widget()) widget()->addSearchResult(); } #endif void RenderLineEdit::calcMinMaxWidth() { KHTMLAssert( !minMaxKnown() ); #if APPLE_CHANGES // Let the widget tell us how big it wants to be. m_updating = true; int size = element()->size(); QSize s(widget()->sizeForCharacterWidth(size > 0 ? size : 20)); m_updating = false; #else const QFontMetrics &fm = style()->fontMetrics(); QSize s; int size = element()->size(); int h = fm.lineSpacing(); int w = fm.width( 'x' ) * (size > 0 ? size : 17); // "some" s = QSize(w + 2 + 2*widget()->frameWidth(), QMAX(h, 14) + 2 + 2*widget()->frameWidth()) .expandedTo(QApplication::globalStrut()); #endif setIntrinsicWidth( s.width() ); setIntrinsicHeight( s.height() ); RenderFormElement::calcMinMaxWidth(); } void RenderLineEdit::setStyle(RenderStyle *s) { RenderFormElement::setStyle(s); KLineEdit *w = widget(); w->setAlignment(textAlignment()); #if APPLE_CHANGES w->setWritingDirection(style()->direction() == RTL ? QPainter::RTL : QPainter::LTR); #endif } void RenderLineEdit::updateFromElement() { HTMLInputElementImpl *e = element(); KLineEdit *w = widget(); int ml = e->maxLength(); if ( ml <= 0 || ml > 1024 ) ml = 1024; if ( w->maxLength() != ml ) w->setMaxLength( ml ); if (!e->valueMatchesRenderer()) { QString widgetText = w->text(); QString newText = e->value().string(); newText.replace(QChar('\\'), backslashAsCurrencySymbol()); if (widgetText != newText) { w->blockSignals(true); int pos = w->cursorPosition(); m_updating = true; w->setText(newText); m_updating = false; w->setEdited( false ); w->setCursorPosition(pos); w->blockSignals(false); } e->setValueMatchesRenderer(); } w->setReadOnly(e->readOnly()); #if APPLE_CHANGES // Handle updating the search attributes. w->setPlaceholderString(e->getAttribute(ATTR_PLACEHOLDER).string()); if (w->type() == QLineEdit::Search) { w->setLiveSearch(!e->getAttribute(ATTR_INCREMENTAL).isNull()); w->setAutoSaveName(e->getAttribute(ATTR_AUTOSAVE).string()); w->setMaxResults(e->maxResults()); } #endif RenderFormElement::updateFromElement(); } void RenderLineEdit::slotTextChanged(const QString &string) { if (m_updating) // Don't alter the value if we are in the middle of initing the control, since return; // we are getting the value from the DOM and it's not user input. // A null string value is used to indicate that the form control has not altered the original // default value. That means that we should never use the null string value when the user // empties a textfield, but should always force an empty textfield to use the empty string. QString newText = string.isNull() ? "" : string; newText.replace(backslashAsCurrencySymbol(), QChar('\\')); element()->setValueFromRenderer(newText); } long RenderLineEdit::selectionStart() { KLineEdit *lineEdit = static_cast(m_widget); int start = lineEdit->selectionStart(); if (start == -1) start = lineEdit->cursorPosition(); return start; } long RenderLineEdit::selectionEnd() { KLineEdit *lineEdit = static_cast(m_widget); int start = lineEdit->selectionStart(); if (start == -1) return lineEdit->cursorPosition(); return start + (int)lineEdit->selectedText().length(); } void RenderLineEdit::setSelectionStart(long start) { int realStart = MAX(start, 0); int length = MAX(selectionEnd() - realStart, 0); static_cast(m_widget)->setSelection(realStart, length); } void RenderLineEdit::setSelectionEnd(long end) { int start = selectionStart(); int realEnd = MAX(end, 0); int length = realEnd - start; if (length < 0) { start = realEnd; length = 0; } static_cast(m_widget)->setSelection(start, length); } void RenderLineEdit::select() { static_cast(m_widget)->selectAll(); } void RenderLineEdit::setSelectionRange(long start, long end) { int realStart = MAX(start, 0); int length = MAX(end - realStart, 0); static_cast(m_widget)->setSelection(realStart, length); } bool RenderLineEdit::isEdited() const { return static_cast(m_widget)->edited(); } void RenderLineEdit::setEdited(bool x) { static_cast(m_widget)->setEdited(x); } // --------------------------------------------------------------------------- RenderFieldset::RenderFieldset(HTMLGenericFormElementImpl *element) : RenderBlock(element) { } RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren) { RenderObject* legend = findLegend(); if (legend) { if (relayoutChildren) legend->setNeedsLayout(true); legend->layoutIfNeeded(); int xPos = borderLeft() + paddingLeft() + legend->marginLeft(); if (style()->direction() == RTL) xPos = m_width - paddingRight() - borderRight() - legend->width() - legend->marginRight(); int b = borderTop(); int h = legend->height(); legend->setPos(xPos, QMAX((b-h)/2, 0)); m_height = QMAX(b,h) + paddingTop(); } return legend; } RenderObject* RenderFieldset::findLegend() { for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) { if (!legend->isFloatingOrPositioned() && legend->element() && legend->element()->id() == ID_LEGEND) return legend; } return 0; } void RenderFieldset::paintBoxDecorations(PaintInfo& i, int _tx, int _ty) { //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl; int w = width(); int h = height() + borderTopExtra() + borderBottomExtra(); RenderObject* legend = findLegend(); if (!legend) return RenderBlock::paintBoxDecorations(i, _tx, _ty); int yOff = (legend->yPos() > 0) ? 0 : (legend->height()-borderTop())/2; h -= yOff; _ty += yOff - borderTopExtra(); int my = kMax(_ty, i.r.y()); int end = kMin(i.r.y() + i.r.height(), _ty + h); int mh = end - my; paintBackground(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h); if (style()->hasBorder()) paintBorderMinusLegend(i.p, _tx, _ty, w, h, style(), legend->xPos(), legend->width()); } void RenderFieldset::paintBorderMinusLegend(QPainter *p, int _tx, int _ty, int w, int h, const RenderStyle* style, int lx, int lw) { const QColor& tc = style->borderTopColor(); const QColor& bc = style->borderBottomColor(); EBorderStyle ts = style->borderTopStyle(); EBorderStyle bs = style->borderBottomStyle(); EBorderStyle ls = style->borderLeftStyle(); EBorderStyle rs = style->borderRightStyle(); bool render_t = ts > BHIDDEN; bool render_l = ls > BHIDDEN; bool render_r = rs > BHIDDEN; bool render_b = bs > BHIDDEN; if(render_t) { drawBorder(p, _tx, _ty, _tx + lx, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0), 0); drawBorder(p, _tx+lx+lw, _ty, _tx + w, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, 0, (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0)); } if(render_b) drawBorder(p, _tx, _ty + h - style->borderBottomWidth(), _tx + w, _ty + h, BSBottom, bc, style->color(), bs, (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE)?style->borderLeftWidth():0), (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE)?style->borderRightWidth():0)); if(render_l) { const QColor& lc = style->borderLeftColor(); bool ignore_top = (tc == lc) && (ls >= OUTSET) && (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET); bool ignore_bottom = (bc == lc) && (ls >= OUTSET) && (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET); drawBorder(p, _tx, _ty, _tx + style->borderLeftWidth(), _ty + h, BSLeft, lc, style->color(), ls, ignore_top?0:style->borderTopWidth(), ignore_bottom?0:style->borderBottomWidth()); } if(render_r) { const QColor& rc = style->borderRightColor(); bool ignore_top = (tc == rc) && (rs >= DOTTED || rs == INSET) && (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET); bool ignore_bottom = (bc == rc) && (rs >= DOTTED || rs == INSET) && (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET); drawBorder(p, _tx + w - style->borderRightWidth(), _ty, _tx + w, _ty + h, BSRight, rc, style->color(), rs, ignore_top?0:style->borderTopWidth(), ignore_bottom?0:style->borderBottomWidth()); } } void RenderFieldset::setStyle(RenderStyle* _style) { RenderBlock::setStyle(_style); // WinIE renders fieldsets with display:inline like they're inline-blocks. For us, // an inline-block is just a block element with replaced set to true and inline set // to true. Ensure that if we ended up being inline that we set our replaced flag // so that we're treated like an inline-block. if (isInline()) setReplaced(true); } // ------------------------------------------------------------------------- RenderFileButton::RenderFileButton(HTMLInputElementImpl *element) : RenderFormElement(element) { #if APPLE_CHANGES KWQFileButton *w = new KWQFileButton(view()->part()); connect(w, SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &))); connect(w, SIGNAL(clicked()), this, SLOT(slotClicked())); setQWidget(w); #else QHBox *w = new QHBox(view()->viewport()); m_edit = new LineEditWidget(w); connect(m_edit, SIGNAL(returnPressed()), this, SLOT(slotReturnPressed())); connect(m_edit, SIGNAL(textChanged(const QString &)),this,SLOT(slotTextChanged(const QString &))); m_button = new QPushButton(i18n("Browse..."), w); m_button->setFocusPolicy(QWidget::ClickFocus); connect(m_button,SIGNAL(clicked()), this, SLOT(slotClicked())); w->setStretchFactor(m_edit, 2); w->setFocusProxy(m_edit); setQWidget(w); #endif } void RenderFileButton::calcMinMaxWidth() { KHTMLAssert( !minMaxKnown() ); #if APPLE_CHANGES // Let the widget tell us how big it wants to be. int size = element()->size(); QSize s(static_cast(widget())->sizeForCharacterWidth(size > 0 ? size : 20)); #else const QFontMetrics &fm = style()->fontMetrics(); QSize s; int size = element()->size(); int h = fm.lineSpacing(); int w = fm.width( 'x' ) * (size > 0 ? size : 17); // "some" w += 6 + fm.width( m_button->text() ) + 2*fm.width( ' ' ); s = QSize(w + 2 + 2*m_edit->frameWidth(), QMAX(h, 14) + 2 + 2*m_edit->frameWidth()) .expandedTo(QApplication::globalStrut()); #endif setIntrinsicWidth( s.width() ); setIntrinsicHeight( s.height() ); RenderFormElement::calcMinMaxWidth(); } void RenderFileButton::slotClicked() { #if APPLE_CHANGES RenderFormElement::slotClicked(); #else QString file_name = KFileDialog::getOpenFileName(QString::null, QString::null, 0, i18n("Browse...")); if (!file_name.isNull()) { element()->m_value = DOMString(file_name); m_edit->setText(file_name); } #endif } void RenderTextArea::setEdited(bool x) { m_dirty = x; } void RenderFileButton::updateFromElement() { #if APPLE_CHANGES static_cast(widget())->setFilename(element()->value().string()); #else m_edit->blockSignals(true); m_edit->setText(element()->value().string()); m_edit->blockSignals(false); int ml = element()->maxLength(); if ( ml <= 0 || ml > 1024 ) ml = 1024; m_edit->setMaxLength( ml ); m_edit->setEdited( false ); #endif RenderFormElement::updateFromElement(); } void RenderFileButton::slotReturnPressed() { if (element()->form()) element()->form()->prepareSubmit(); } void RenderFileButton::slotTextChanged(const QString &string) { element()->m_value = DOMString(string); element()->onChange(); } void RenderFileButton::select() { #if !APPLE_CHANGES m_edit->selectAll(); #endif } #if APPLE_CHANGES void RenderFileButton::click(bool sendMouseEvents) { static_cast(widget())->click(sendMouseEvents); } #endif // ------------------------------------------------------------------------- RenderLabel::RenderLabel(HTMLGenericFormElementImpl *element) : RenderFormElement(element) { } // ------------------------------------------------------------------------- RenderLegend::RenderLegend(HTMLGenericFormElementImpl *element) : RenderBlock(element) { } // ------------------------------------------------------------------------------- ComboBoxWidget::ComboBoxWidget(QWidget *parent) : KComboBox(false, parent) { setAutoMask(true); if (listBox()) listBox()->installEventFilter(this); setMouseTracking(true); } bool ComboBoxWidget::event(QEvent *e) { #if !APPLE_CHANGES if (e->type()==QEvent::KeyPress) { QKeyEvent *ke = static_cast(e); switch(ke->key()) { case Key_Return: case Key_Enter: popup(); ke->accept(); return true; default: return KComboBox::event(e); } } #endif return KComboBox::event(e); } bool ComboBoxWidget::eventFilter(QObject *dest, QEvent *e) { #if !APPLE_CHANGES if (dest==listBox() && e->type()==QEvent::KeyPress) { QKeyEvent *ke = static_cast(e); bool forward = false; switch(ke->key()) { case Key_Tab: forward=true; case Key_BackTab: // ugly hack. emulate popdownlistbox() (private in QComboBox) // we re-use ke here to store the reference to the generated event. ke = new QKeyEvent(QEvent::KeyPress, Key_Escape, 0, 0); QApplication::sendEvent(dest,ke); focusNextPrevChild(forward); delete ke; return true; default: return KComboBox::eventFilter(dest, e); } } #endif return KComboBox::eventFilter(dest, e); } // ------------------------------------------------------------------------- RenderSelect::RenderSelect(HTMLSelectElementImpl *element) : RenderFormElement(element) { m_ignoreSelectEvents = false; m_multiple = element->multiple(); m_size = element->size(); m_useListBox = (m_multiple || m_size > 1); m_selectionChanged = true; m_optionsChanged = true; if(m_useListBox) setQWidget(createListBox()); else setQWidget(createComboBox()); } #if APPLE_CHANGES void RenderSelect::setWidgetWritingDirection() { QPainter::TextDirection d = style()->direction() == RTL ? QPainter::RTL : QPainter::LTR; if (m_useListBox) static_cast(m_widget)->setWritingDirection(d); else static_cast(m_widget)->setWritingDirection(d); } void RenderSelect::setStyle(RenderStyle *s) { RenderFormElement::setStyle(s); setWidgetWritingDirection(); } #endif void RenderSelect::updateFromElement() { m_ignoreSelectEvents = true; // change widget type bool oldMultiple = m_multiple; unsigned oldSize = m_size; bool oldListbox = m_useListBox; m_multiple = element()->multiple(); m_size = element()->size(); m_useListBox = (m_multiple || m_size > 1); if (oldMultiple != m_multiple || oldSize != m_size) { if (m_useListBox != oldListbox) { // type of select has changed delete m_widget; if(m_useListBox) setQWidget(createListBox()); else setQWidget(createComboBox()); #if APPLE_CHANGES setWidgetWritingDirection(); #endif } if (m_useListBox && oldMultiple != m_multiple) { static_cast(m_widget)->setSelectionMode(m_multiple ? QListBox::Extended : QListBox::Single); } m_selectionChanged = true; m_optionsChanged = true; } // update contents listbox/combobox based on options in m_element if ( m_optionsChanged ) { if (element()->m_recalcListItems) element()->recalcListItems(); QMemArray listItems = element()->listItems(); int listIndex; if (m_useListBox) static_cast(m_widget)->clear(); else static_cast(m_widget)->clear(); for (listIndex = 0; listIndex < int(listItems.size()); listIndex++) { if (listItems[listIndex]->id() == ID_OPTGROUP) { QString label = listItems[listIndex]->getAttribute(ATTR_LABEL).string(); label.replace(QChar('\\'), backslashAsCurrencySymbol()); // In WinIE, an optgroup can't start or end with whitespace (other than the indent // we give it). We match this behavior. label = label.stripWhiteSpace(); // We want to collapse our whitespace too. This will match other browsers. label = label.simplifyWhiteSpace(); #if APPLE_CHANGES if (m_useListBox) static_cast(m_widget)->appendGroupLabel(label); else static_cast(m_widget)->appendGroupLabel(label); #else if(m_useListBox) { QListBoxText *item = new QListBoxText(label); static_cast(m_widget) ->insertItem(item, listIndex); item->setSelectable(false); } else static_cast(m_widget)->insertItem(label, listIndex); #endif } else if (listItems[listIndex]->id() == ID_OPTION) { QString itemText = static_cast(listItems[listIndex])->text().string(); itemText.replace(QChar('\\'), backslashAsCurrencySymbol()); // In WinIE, leading and trailing whitespace is ignored in options. We match this behavior. itemText = itemText.stripWhiteSpace(); // We want to collapse our whitespace too. This will match other browsers. itemText = itemText.simplifyWhiteSpace(); if (listItems[listIndex]->parentNode()->id() == ID_OPTGROUP) itemText.prepend(" "); #if APPLE_CHANGES if (m_useListBox) static_cast(m_widget)->appendItem(itemText); else static_cast(m_widget)->appendItem(itemText); #else if(m_useListBox) static_cast(m_widget)->insertItem(itemText, listIndex); else static_cast(m_widget)->insertItem(itemText, listIndex); #endif } else if (listItems[listIndex]->id() == ID_HR) { if (!m_useListBox) { static_cast(m_widget)->appendSeparator(); } } else KHTMLAssert(false); m_selectionChanged = true; } #if APPLE_CHANGES if (m_useListBox) static_cast(m_widget)->doneAppendingItems(); #endif setNeedsLayoutAndMinMaxRecalc(); m_optionsChanged = false; } // update selection if (m_selectionChanged) { updateSelection(); } m_ignoreSelectEvents = false; RenderFormElement::updateFromElement(); } #if APPLE_CHANGES short RenderSelect::baselinePosition( bool f, bool isRootLineBox ) const { if (m_useListBox) { // FIXME: Should get the hardcoded constant of 7 by calling a QListBox function, // as we do for other widget classes. return RenderWidget::baselinePosition( f, isRootLineBox ) - 7; } return RenderFormElement::baselinePosition( f, isRootLineBox ); } #endif void RenderSelect::calcMinMaxWidth() { KHTMLAssert( !minMaxKnown() ); if (m_optionsChanged) updateFromElement(); // ### ugly HACK FIXME!!! setMinMaxKnown(); layoutIfNeeded(); setNeedsLayoutAndMinMaxRecalc(); // ### end FIXME RenderFormElement::calcMinMaxWidth(); } void RenderSelect::layout( ) { KHTMLAssert(needsLayout()); KHTMLAssert(minMaxKnown()); // ### maintain selection properly between type/size changes, and work // out how to handle multiselect->singleselect (probably just select // first selected one) // calculate size if(m_useListBox) { KListBox* w = static_cast(m_widget); #if !APPLE_CHANGES QListBoxItem* p = w->firstItem(); int width = 0; int height = 0; while(p) { width = QMAX(width, p->width(p->listBox())); height = QMAX(height, p->height(p->listBox())); p = p->next(); } #endif int size = m_size; // check if multiple and size was not given or invalid // Internet Exploder sets size to QMIN(number of elements, 4) // Netscape seems to simply set it to "number of elements" // the average of that is IMHO QMIN(number of elements, 10) // so I did that ;-) if(size < 1) size = QMIN(static_cast(m_widget)->count(), 10); #if APPLE_CHANGES // Let the widget tell us how big it wants to be. QSize s(w->sizeForNumberOfLines(size)); setIntrinsicWidth( s.width() ); setIntrinsicHeight( s.height() ); #else width += 2*w->frameWidth() + w->verticalScrollBar()->sizeHint().width(); height = size*height + 2*w->frameWidth(); setIntrinsicWidth( width ); setIntrinsicHeight( height ); #endif } else { QSize s(m_widget->sizeHint()); setIntrinsicWidth( s.width() ); setIntrinsicHeight( s.height() ); } RenderFormElement::layout(); // and now disable the widget in case there is no