diff options
Diffstat (limited to 'Source/WebCore/platform/qt/RenderThemeQt.cpp')
-rw-r--r-- | Source/WebCore/platform/qt/RenderThemeQt.cpp | 1333 |
1 files changed, 1333 insertions, 0 deletions
diff --git a/Source/WebCore/platform/qt/RenderThemeQt.cpp b/Source/WebCore/platform/qt/RenderThemeQt.cpp new file mode 100644 index 0000000..2cc3625 --- /dev/null +++ b/Source/WebCore/platform/qt/RenderThemeQt.cpp @@ -0,0 +1,1333 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * 2006 Dirk Mueller <mueller@kde.org> + * 2006 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * All rights reserved. + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderThemeQt.h" + +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "CSSValueKeywords.h" +#include "Chrome.h" +#include "ChromeClientQt.h" +#include "Color.h" +#include "Document.h" +#include "Font.h" +#include "FontSelector.h" +#include "GraphicsContext.h" +#include "HTMLInputElement.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#if USE(QT_MOBILE_THEME) +#include "QtMobileWebStyle.h" +#endif +#include "NotImplemented.h" +#include "Page.h" +#include "QWebPageClient.h" +#include "QtStyleOptionWebComboBox.h" +#include "RenderBox.h" +#if ENABLE(PROGRESS_TAG) +#include "RenderProgress.h" +#endif +#include "RenderSlider.h" +#include "RenderTheme.h" +#include "ScrollbarThemeQt.h" +#include "TimeRanges.h" +#include "UserAgentStyleSheets.h" + +#include <QApplication> +#include <QColor> +#include <QFile> +#include <QLineEdit> +#include <QPainter> +#include <QPushButton> +#include <QStyleFactory> +#include <QStyleOptionButton> +#include <QStyleOptionFrameV2> +#if ENABLE(PROGRESS_TAG) +#include <QStyleOptionProgressBarV2> +#endif +#include <QStyleOptionSlider> +#include <QWidget> + + +namespace WebCore { + +using namespace HTMLNames; + +inline static void initStyleOption(QWidget *widget, QStyleOption& option) +{ + if (widget) + option.initFrom(widget); + else { + /* + If a widget is not directly available for rendering, we fallback to default + value for an active widget. + */ + option.state = QStyle::State_Active | QStyle::State_Enabled; + } +} + + +StylePainter::StylePainter(RenderThemeQt* theme, const PaintInfo& paintInfo) +{ + init(paintInfo.context ? paintInfo.context : 0, theme->qStyle()); +} + +StylePainter::StylePainter(ScrollbarThemeQt* theme, GraphicsContext* context) +{ + init(context, theme->style()); +} + +void StylePainter::init(GraphicsContext* context, QStyle* themeStyle) +{ + painter = static_cast<QPainter*>(context->platformContext()); + widget = 0; + QPaintDevice* dev = 0; + if (painter) + dev = painter->device(); + if (dev && dev->devType() == QInternal::Widget) + widget = static_cast<QWidget*>(dev); + style = themeStyle; + + if (painter) { + // the styles often assume being called with a pristine painter where no brush is set, + // so reset it manually + oldBrush = painter->brush(); + painter->setBrush(Qt::NoBrush); + + // painting the widget with anti-aliasing will make it blurry + // disable it here and restore it later + oldAntialiasing = painter->testRenderHint(QPainter::Antialiasing); + painter->setRenderHint(QPainter::Antialiasing, false); + } +} + +StylePainter::~StylePainter() +{ + if (painter) { + painter->setBrush(oldBrush); + painter->setRenderHints(QPainter::Antialiasing, oldAntialiasing); + } +} + +PassRefPtr<RenderTheme> RenderThemeQt::create(Page* page) +{ + return adoptRef(new RenderThemeQt(page)); +} + +PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) +{ + if (page) + return RenderThemeQt::create(page); + + static RenderTheme* fallback = RenderThemeQt::create(0).releaseRef(); + return fallback; +} + +RenderThemeQt::RenderThemeQt(Page* page) + : RenderTheme() + , m_page(page) + , m_lineEdit(0) +{ + QPushButton button; + button.setAttribute(Qt::WA_MacSmallSize); + QFont defaultButtonFont = QApplication::font(&button); + QFontInfo fontInfo(defaultButtonFont); + m_buttonFontFamily = defaultButtonFont.family(); +#ifdef Q_WS_MAC + m_buttonFontPixelSize = fontInfo.pixelSize(); +#endif + +#if USE(QT_MOBILE_THEME) + m_fallbackStyle = new QtMobileWebStyle; +#else + m_fallbackStyle = QStyleFactory::create(QLatin1String("windows")); +#endif +} + +RenderThemeQt::~RenderThemeQt() +{ + delete m_fallbackStyle; +#ifndef QT_NO_LINEEDIT + delete m_lineEdit; +#endif +} + +#if USE(QT_MOBILE_THEME) +bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const +{ + switch (style->appearance()) { + case PushButtonPart: + case ButtonPart: + case MenulistPart: + case TextFieldPart: + case TextAreaPart: + return true; + case CheckboxPart: + case RadioPart: + return false; + default: + return RenderTheme::isControlStyled(style, border, fill, backgroundColor); + } +} + +int RenderThemeQt::popupInternalPaddingBottom(RenderStyle* style) const +{ + return 1; +} +#endif + +// for some widget painting, we need to fallback to Windows style +QStyle* RenderThemeQt::fallbackStyle() const +{ + return (m_fallbackStyle) ? m_fallbackStyle : QApplication::style(); +} + +QStyle* RenderThemeQt::qStyle() const +{ +#if USE(QT_MOBILE_THEME) + return fallbackStyle(); +#endif + + if (m_page) { + QWebPageClient* pageClient = m_page->chrome()->client()->platformPageClient(); + + if (pageClient) + return pageClient->style(); + } + + return QApplication::style(); +} + +String RenderThemeQt::extraDefaultStyleSheet() +{ + String result = RenderTheme::extraDefaultStyleSheet(); +#if ENABLE(NO_LISTBOX_RENDERING) + result += String(themeQtNoListboxesUserAgentStyleSheet, sizeof(themeQtNoListboxesUserAgentStyleSheet)); +#endif +#if USE(QT_MOBILE_THEME) + result += String(themeQtMobileUserAgentStyleSheet, sizeof(themeQtMobileUserAgentStyleSheet)); +#endif + return result; +} + +bool RenderThemeQt::supportsHover(const RenderStyle*) const +{ + return true; +} + +bool RenderThemeQt::supportsFocusRing(const RenderStyle* style) const +{ + switch (style->appearance()) { + case CheckboxPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case ButtonPart: + case ButtonBevelPart: + case ListboxPart: + case ListItemPart: + case MenulistPart: + case MenulistButtonPart: + case SliderHorizontalPart: + case SliderVerticalPart: + case SliderThumbHorizontalPart: + case SliderThumbVerticalPart: + case SearchFieldPart: + case SearchFieldResultsButtonPart: + case SearchFieldCancelButtonPart: + case TextFieldPart: + case TextAreaPart: + return true; + default: + return false; + } +} + +int RenderThemeQt::baselinePosition(const RenderObject* o) const +{ + if (!o->isBox()) + return 0; + + if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart) + return toRenderBox(o)->marginTop() + toRenderBox(o)->height() - 2; // Same as in old khtml + return RenderTheme::baselinePosition(o); +} + +bool RenderThemeQt::controlSupportsTints(const RenderObject* o) const +{ + if (!isEnabled(o)) + return false; + + // Checkboxes only have tint when checked. + if (o->style()->appearance() == CheckboxPart) + return isChecked(o); + + // For now assume other controls have tint if enabled. + return true; +} + +bool RenderThemeQt::supportsControlTints() const +{ + return true; +} + +int RenderThemeQt::findFrameLineWidth(QStyle* style) const +{ +#ifndef QT_NO_LINEEDIT + if (!m_lineEdit) + m_lineEdit = new QLineEdit(); +#endif + + QStyleOptionFrameV2 opt; + QWidget* widget = 0; +#ifndef QT_NO_LINEEDIT + widget = m_lineEdit; +#endif + return style->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, widget); +} + +static QRect inflateButtonRect(const QRect& originalRect, QStyle* style) +{ + QStyleOptionButton option; + option.state |= QStyle::State_Small; + option.rect = originalRect; + + QRect layoutRect = style->subElementRect(QStyle::SE_PushButtonLayoutItem, &option, 0); + if (!layoutRect.isNull()) { + int paddingLeft = layoutRect.left() - originalRect.left(); + int paddingRight = originalRect.right() - layoutRect.right(); + int paddingTop = layoutRect.top() - originalRect.top(); + int paddingBottom = originalRect.bottom() - layoutRect.bottom(); + + return originalRect.adjusted(-paddingLeft, -paddingTop, paddingRight, paddingBottom); + } + return originalRect; +} + +void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& rect) +{ + switch (o->style()->appearance()) { + case CheckboxPart: + break; + case RadioPart: + break; + case PushButtonPart: + case ButtonPart: { + QRect inflatedRect = inflateButtonRect(rect, qStyle()); + rect = IntRect(inflatedRect.x(), inflatedRect.y(), inflatedRect.width(), inflatedRect.height()); + break; + } + case MenulistPart: + break; + default: + break; + } +} + +Color RenderThemeQt::platformActiveSelectionBackgroundColor() const +{ + QPalette pal = QApplication::palette(); + return pal.brush(QPalette::Active, QPalette::Highlight).color(); +} + +Color RenderThemeQt::platformInactiveSelectionBackgroundColor() const +{ + QPalette pal = QApplication::palette(); + return pal.brush(QPalette::Inactive, QPalette::Highlight).color(); +} + +Color RenderThemeQt::platformActiveSelectionForegroundColor() const +{ + QPalette pal = QApplication::palette(); + return pal.brush(QPalette::Active, QPalette::HighlightedText).color(); +} + +Color RenderThemeQt::platformInactiveSelectionForegroundColor() const +{ + QPalette pal = QApplication::palette(); + return pal.brush(QPalette::Inactive, QPalette::HighlightedText).color(); +} + +Color RenderThemeQt::platformFocusRingColor() const +{ + QPalette pal = QApplication::palette(); + return pal.brush(QPalette::Active, QPalette::Highlight).color(); +} + +void RenderThemeQt::systemFont(int, FontDescription&) const +{ + // no-op +} + +Color RenderThemeQt::systemColor(int cssValueId) const +{ + QPalette pal = QApplication::palette(); + switch (cssValueId) { + case CSSValueButtontext: + return pal.brush(QPalette::Active, QPalette::ButtonText).color(); + case CSSValueCaptiontext: + return pal.brush(QPalette::Active, QPalette::Text).color(); + default: + return RenderTheme::systemColor(cssValueId); + } +} + +int RenderThemeQt::minimumMenuListSize(RenderStyle*) const +{ + const QFontMetrics &fm = QApplication::fontMetrics(); + return 7 * fm.width(QLatin1Char('x')); +} + +void RenderThemeQt::computeSizeBasedOnStyle(RenderStyle* renderStyle) const +{ + QSize size(0, 0); + const QFontMetrics fm(renderStyle->font().font()); + QStyle* style = qStyle(); + + switch (renderStyle->appearance()) { + case TextAreaPart: + case TextFieldPart: { + int padding = findFrameLineWidth(style); + + renderStyle->setPaddingLeft(Length(padding, Fixed)); + renderStyle->setPaddingRight(Length(padding, Fixed)); + renderStyle->setPaddingTop(Length(padding, Fixed)); + renderStyle->setPaddingBottom(Length(padding, Fixed)); + break; + } + default: + break; + } + + // If the width and height are both specified, then we have nothing to do. + if (!renderStyle->width().isIntrinsicOrAuto() && !renderStyle->height().isAuto()) + return; + + switch (renderStyle->appearance()) { + case CheckboxPart: { + QStyleOption styleOption; + styleOption.state |= QStyle::State_Small; + int checkBoxWidth = style->pixelMetric(QStyle::PM_IndicatorWidth, &styleOption); + checkBoxWidth *= renderStyle->effectiveZoom(); + size = QSize(checkBoxWidth, checkBoxWidth); + break; + } + case RadioPart: { + QStyleOption styleOption; + styleOption.state |= QStyle::State_Small; + int radioWidth = style->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth, &styleOption); + radioWidth *= renderStyle->effectiveZoom(); + size = QSize(radioWidth, radioWidth); + break; + } + case PushButtonPart: + case ButtonPart: { + QStyleOptionButton styleOption; + styleOption.state |= QStyle::State_Small; + QSize contentSize = fm.size(Qt::TextShowMnemonic, QString::fromLatin1("X")); + QSize pushButtonSize = style->sizeFromContents(QStyle::CT_PushButton, + &styleOption, contentSize, 0); + styleOption.rect = QRect(0, 0, pushButtonSize.width(), pushButtonSize.height()); + QRect layoutRect = style->subElementRect(QStyle::SE_PushButtonLayoutItem, + &styleOption, 0); + + // If the style supports layout rects we use that, and compensate accordingly + // in paintButton() below. + if (!layoutRect.isNull()) + size.setHeight(layoutRect.height()); + else + size.setHeight(pushButtonSize.height()); + + break; + } + case MenulistPart: { + QStyleOptionComboBox styleOption; + styleOption.state |= QStyle::State_Small; + int contentHeight = qMax(fm.lineSpacing(), 14) + 2; + QSize menuListSize = style->sizeFromContents(QStyle::CT_ComboBox, + &styleOption, QSize(0, contentHeight), 0); + size.setHeight(menuListSize.height()); + break; + } + default: + break; + } + + // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. + if (renderStyle->width().isIntrinsicOrAuto() && size.width() > 0) + renderStyle->setWidth(Length(size.width(), Fixed)); + if (renderStyle->height().isAuto() && size.height() > 0) + renderStyle->setHeight(Length(size.height(), Fixed)); +} + +void RenderThemeQt::setCheckboxSize(RenderStyle* style) const +{ + computeSizeBasedOnStyle(style); +} + +bool RenderThemeQt::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + return paintButton(o, i, r); +} + +void RenderThemeQt::setRadioSize(RenderStyle* style) const +{ + computeSizeBasedOnStyle(style); +} + +bool RenderThemeQt::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + return paintButton(o, i, r); +} + +void RenderThemeQt::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element*) const +{ + // Ditch the border. + style->resetBorder(); + +#ifdef Q_WS_MAC + if (style->appearance() == PushButtonPart) { + // The Mac ports ignore the specified height for <input type="button"> elements + // unless a border and/or background CSS property is also specified. + style->setHeight(Length(Auto)); + } +#endif + + // White-space is locked to pre + style->setWhiteSpace(PRE); + + FontDescription fontDescription = style->fontDescription(); + fontDescription.setIsAbsoluteSize(true); + +#ifdef Q_WS_MAC // Use fixed font size and family on Mac (like Safari does) + fontDescription.setSpecifiedSize(m_buttonFontPixelSize); + fontDescription.setComputedSize(m_buttonFontPixelSize); +#else + fontDescription.setSpecifiedSize(style->fontSize()); + fontDescription.setComputedSize(style->fontSize()); +#endif + + FontFamily fontFamily; + fontFamily.setFamily(m_buttonFontFamily); + fontDescription.setFamily(fontFamily); + style->setFontDescription(fontDescription); + style->font().update(selector->fontSelector()); + style->setLineHeight(RenderStyle::initialLineHeight()); + + setButtonSize(style); + setButtonPadding(style); +} + +void RenderThemeQt::setButtonSize(RenderStyle* style) const +{ + computeSizeBasedOnStyle(style); +} + +void RenderThemeQt::setButtonPadding(RenderStyle* style) const +{ + QStyleOptionButton styleOption; + styleOption.state |= QStyle::State_Small; + + // Fake a button rect here, since we're just computing deltas + QRect originalRect = QRect(0, 0, 100, 30); + styleOption.rect = originalRect; + + // Default padding is based on the button margin pixel metric + int buttonMargin = qStyle()->pixelMetric(QStyle::PM_ButtonMargin, &styleOption, 0); + int paddingLeft = buttonMargin; + int paddingRight = buttonMargin; + int paddingTop = 1; + int paddingBottom = 0; + + // Then check if the style uses layout margins + QRect layoutRect = qStyle()->subElementRect(QStyle::SE_PushButtonLayoutItem, + &styleOption, 0); + if (!layoutRect.isNull()) { + QRect contentsRect = qStyle()->subElementRect(QStyle::SE_PushButtonContents, + &styleOption, 0); + paddingLeft = contentsRect.left() - layoutRect.left(); + paddingRight = layoutRect.right() - contentsRect.right(); + paddingTop = contentsRect.top() - layoutRect.top(); + + // Can't use this right now because we don't have the baseline to compensate + // paddingBottom = layoutRect.bottom() - contentsRect.bottom(); + } + + style->setPaddingLeft(Length(paddingLeft, Fixed)); + style->setPaddingRight(Length(paddingRight, Fixed)); + style->setPaddingTop(Length(paddingTop, Fixed)); + style->setPaddingBottom(Length(paddingBottom, Fixed)); +} + +bool RenderThemeQt::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + StylePainter p(this, i); + if (!p.isValid()) + return true; + + QStyleOptionButton option; + initStyleOption(p.widget, option); + option.rect = r; + option.state |= QStyle::State_Small; + + ControlPart appearance = initializeCommonQStyleOptions(option, o); + if (appearance == PushButtonPart || appearance == ButtonPart) { + option.rect = inflateButtonRect(option.rect, qStyle()); + p.drawControl(QStyle::CE_PushButton, option); + } else if (appearance == RadioPart) + p.drawControl(QStyle::CE_RadioButton, option); + else if (appearance == CheckboxPart) + p.drawControl(QStyle::CE_CheckBox, option); + + return false; +} + +void RenderThemeQt::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + style->setBackgroundColor(Color::transparent); + style->resetBorder(); + style->resetPadding(); + computeSizeBasedOnStyle(style); +} + +bool RenderThemeQt::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + StylePainter p(this, i); + if (!p.isValid()) + return true; + + QStyleOptionFrameV2 panel; + initStyleOption(p.widget, panel); + panel.rect = r; + panel.lineWidth = findFrameLineWidth(qStyle()); + panel.state |= QStyle::State_Sunken; + panel.features = QStyleOptionFrameV2::None; + + // Get the correct theme data for a text field + ControlPart appearance = initializeCommonQStyleOptions(panel, o); + if (appearance != TextFieldPart + && appearance != SearchFieldPart + && appearance != TextAreaPart + && appearance != ListboxPart) + return true; + + // Now paint the text field. + p.drawPrimitive(QStyle::PE_PanelLineEdit, panel); + + return false; +} + +void RenderThemeQt::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const +{ + adjustTextFieldStyle(selector, style, element); +} + +bool RenderThemeQt::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + return paintTextField(o, i, r); +} + +void RenderThemeQt::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + style->resetBorder(); + + // Height is locked to auto. + style->setHeight(Length(Auto)); + + // White-space is locked to pre + style->setWhiteSpace(PRE); + + computeSizeBasedOnStyle(style); + + // Add in the padding that we'd like to use. + setPopupPadding(style); +} + +void RenderThemeQt::setPopupPadding(RenderStyle* style) const +{ + const int padding = 8; + style->setPaddingLeft(Length(padding, Fixed)); + + QStyleOptionComboBox opt; + int w = qStyle()->pixelMetric(QStyle::PM_ButtonIconSize, &opt, 0); + style->setPaddingRight(Length(padding + w, Fixed)); + + style->setPaddingTop(Length(2, Fixed)); + style->setPaddingBottom(Length(0, Fixed)); +} + + +bool RenderThemeQt::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r) +{ + StylePainter p(this, i); + if (!p.isValid()) + return true; + + QtStyleOptionWebComboBox opt(o); + initStyleOption(p.widget, opt); + initializeCommonQStyleOptions(opt, o); + + const QPoint topLeft = r.topLeft(); + p.painter->translate(topLeft); + opt.rect.moveTo(QPoint(0, 0)); + opt.rect.setSize(r.size()); + + p.drawComplexControl(QStyle::CC_ComboBox, opt); + p.painter->translate(-topLeft); + return false; +} + +void RenderThemeQt::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ +#if USE(QT_MOBILE_THEME) + // Mobile theme uses border radius. +#else + // WORKAROUND because html.css specifies -webkit-border-radius for <select> so we override it here + // see also http://bugs.webkit.org/show_bug.cgi?id=18399 + style->resetBorderRadius(); +#endif + + // Height is locked to auto. + style->setHeight(Length(Auto)); + + // White-space is locked to pre + style->setWhiteSpace(PRE); + + computeSizeBasedOnStyle(style); + + // Add in the padding that we'd like to use. + setPopupPadding(style); +} + +bool RenderThemeQt::paintMenuListButton(RenderObject* o, const PaintInfo& i, + const IntRect& r) +{ + StylePainter p(this, i); + if (!p.isValid()) + return true; + + QtStyleOptionWebComboBox option(o); + initStyleOption(p.widget, option); + initializeCommonQStyleOptions(option, o); + option.rect = r; + + // for drawing the combo box arrow, rely only on the fallback style + p.style = fallbackStyle(); + option.subControls = QStyle::SC_ComboBoxArrow; + p.drawComplexControl(QStyle::CC_ComboBox, option); + + return false; +} + +#if ENABLE(PROGRESS_TAG) +double RenderThemeQt::animationRepeatIntervalForProgressBar(RenderProgress* renderProgress) const +{ + if (renderProgress->position() >= 0) + return 0; + + // FIXME: Use hard-coded value until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed. + // Use the value from windows style which is 10 fps. + return 0.1; +} + +double RenderThemeQt::animationDurationForProgressBar(RenderProgress* renderProgress) const +{ + if (renderProgress->position() >= 0) + return 0; + + QStyleOptionProgressBarV2 option; + option.rect.setSize(renderProgress->size()); + // FIXME: Until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed, + // we simulate one square animating across the progress bar. + return (option.rect.width() / qStyle()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &option)) * animationRepeatIntervalForProgressBar(renderProgress); +} + +void RenderThemeQt::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + style->setBoxShadow(0); +} + +bool RenderThemeQt::paintProgressBar(RenderObject* o, const PaintInfo& pi, const IntRect& r) +{ + if (!o->isProgress()) + return true; + + StylePainter p(this, pi); + if (!p.isValid()) + return true; + + QStyleOptionProgressBarV2 option; + initStyleOption(p.widget, option); + initializeCommonQStyleOptions(option, o); + + RenderProgress* renderProgress = toRenderProgress(o); + option.rect = r; + option.maximum = std::numeric_limits<int>::max(); + option.minimum = 0; + option.progress = (renderProgress->position() * std::numeric_limits<int>::max()); + + const QPoint topLeft = r.topLeft(); + p.painter->translate(topLeft); + option.rect.moveTo(QPoint(0, 0)); + option.rect.setSize(r.size()); + + if (option.progress < 0) { + // FIXME: Until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed, + // we simulate one square animating across the progress bar. + p.drawControl(QStyle::CE_ProgressBarGroove, option); + int chunkWidth = qStyle()->pixelMetric(QStyle::PM_ProgressBarChunkWidth, &option); + QColor color = (option.palette.highlight() == option.palette.background()) ? option.palette.color(QPalette::Active, QPalette::Highlight) : option.palette.color(QPalette::Highlight); + if (renderProgress->style()->direction() == RTL) + p.painter->fillRect(option.rect.right() - chunkWidth - renderProgress->animationProgress() * option.rect.width(), 0, chunkWidth, option.rect.height(), color); + else + p.painter->fillRect(renderProgress->animationProgress() * option.rect.width(), 0, chunkWidth, option.rect.height(), color); + } else + p.drawControl(QStyle::CE_ProgressBar, option); + + p.painter->translate(-topLeft); + + return false; +} +#endif + +bool RenderThemeQt::paintSliderTrack(RenderObject* o, const PaintInfo& pi, + const IntRect& r) +{ + StylePainter p(this, pi); + if (!p.isValid()) + return true; + + QStyleOptionSlider option; + initStyleOption(p.widget, option); + option.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle; + ControlPart appearance = initializeCommonQStyleOptions(option, o); + + RenderSlider* renderSlider = toRenderSlider(o); + IntRect thumbRect = renderSlider->thumbRect(); + + option.rect = r; + + int value; + if (appearance == SliderVerticalPart) { + option.maximum = r.height() - thumbRect.height(); + value = thumbRect.y(); + } else { + option.maximum = r.width() - thumbRect.width(); + value = thumbRect.x(); + } + + value = QStyle::sliderValueFromPosition(0, option.maximum, value, option.maximum); + + option.sliderValue = value; + option.sliderPosition = value; + if (appearance == SliderVerticalPart) + option.orientation = Qt::Vertical; + + if (renderSlider->inDragMode()) { + option.activeSubControls = QStyle::SC_SliderHandle; + option.state |= QStyle::State_Sunken; + } + + const QPoint topLeft = r.topLeft(); + p.painter->translate(topLeft); + option.rect.moveTo(QPoint(0, 0)); + option.rect.setSize(r.size()); + + p.drawComplexControl(QStyle::CC_Slider, option); + p.painter->translate(-topLeft); + + return false; +} + +void RenderThemeQt::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + style->setBoxShadow(0); +} + +bool RenderThemeQt::paintSliderThumb(RenderObject* o, const PaintInfo& pi, + const IntRect& r) +{ + // We've already painted it in paintSliderTrack(), no need to do anything here. + return false; +} + +void RenderThemeQt::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + style->setBoxShadow(0); +} + +bool RenderThemeQt::paintSearchField(RenderObject* o, const PaintInfo& pi, + const IntRect& r) +{ + return true; +} + +void RenderThemeQt::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, + Element* e) const +{ + notImplemented(); + RenderTheme::adjustSearchFieldStyle(selector, style, e); +} + +void RenderThemeQt::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, + Element* e) const +{ + notImplemented(); + RenderTheme::adjustSearchFieldCancelButtonStyle(selector, style, e); +} + +bool RenderThemeQt::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& pi, + const IntRect& r) +{ + notImplemented(); + return RenderTheme::paintSearchFieldCancelButton(o, pi, r); +} + +void RenderThemeQt::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, + Element* e) const +{ + notImplemented(); + RenderTheme::adjustSearchFieldDecorationStyle(selector, style, e); +} + +bool RenderThemeQt::paintSearchFieldDecoration(RenderObject* o, const PaintInfo& pi, + const IntRect& r) +{ + notImplemented(); + return RenderTheme::paintSearchFieldDecoration(o, pi, r); +} + +void RenderThemeQt::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, + Element* e) const +{ + notImplemented(); + RenderTheme::adjustSearchFieldResultsDecorationStyle(selector, style, e); +} + +bool RenderThemeQt::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& pi, + const IntRect& r) +{ + notImplemented(); + return RenderTheme::paintSearchFieldResultsDecoration(o, pi, r); +} + +bool RenderThemeQt::supportsFocus(ControlPart appearance) const +{ + switch (appearance) { + case PushButtonPart: + case ButtonPart: + case TextFieldPart: + case TextAreaPart: + case ListboxPart: + case MenulistPart: + case RadioPart: + case CheckboxPart: + case SliderHorizontalPart: + case SliderVerticalPart: + return true; + default: // No for all others... + return false; + } +} + +void RenderThemeQt::setPaletteFromPageClientIfExists(QPalette& palette) const +{ +#if USE(QT_MOBILE_THEME) + static QPalette lightGrayPalette(Qt::lightGray); + palette = lightGrayPalette; + return; +#endif + // If the webview has a custom palette, use it + if (!m_page) + return; + Chrome* chrome = m_page->chrome(); + if (!chrome) + return; + ChromeClient* chromeClient = chrome->client(); + if (!chromeClient) + return; + QWebPageClient* pageClient = chromeClient->platformPageClient(); + if (!pageClient) + return; + palette = pageClient->palette(); +} + +ControlPart RenderThemeQt::initializeCommonQStyleOptions(QStyleOption& option, RenderObject* o) const +{ + // Default bits: no focus, no mouse over + option.state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver); + + if (!isEnabled(o)) + option.state &= ~QStyle::State_Enabled; + + if (isReadOnlyControl(o)) + // Readonly is supported on textfields. + option.state |= QStyle::State_ReadOnly; + + option.direction = Qt::LeftToRight; + + if (isHovered(o)) + option.state |= QStyle::State_MouseOver; + + setPaletteFromPageClientIfExists(option.palette); + RenderStyle* style = o->style(); + if (!style) + return NoControlPart; + + ControlPart result = style->appearance(); + if (supportsFocus(result) && isFocused(o)) { + option.state |= QStyle::State_HasFocus; + option.state |= QStyle::State_KeyboardFocusChange; + } + + if (style->direction() == WebCore::RTL) + option.direction = Qt::RightToLeft; + + switch (result) { + case PushButtonPart: + case SquareButtonPart: + case ButtonPart: + case ButtonBevelPart: + case ListItemPart: + case MenulistButtonPart: + case SearchFieldResultsButtonPart: + case SearchFieldCancelButtonPart: { + if (isPressed(o)) + option.state |= QStyle::State_Sunken; + else if (result == PushButtonPart || result == ButtonPart) + option.state |= QStyle::State_Raised; + break; + } + case RadioPart: + case CheckboxPart: + option.state |= (isChecked(o) ? QStyle::State_On : QStyle::State_Off); + } + + return result; +} + +#if ENABLE(VIDEO) + +String RenderThemeQt::extraMediaControlsStyleSheet() +{ + return String(mediaControlsQtUserAgentStyleSheet, sizeof(mediaControlsQtUserAgentStyleSheet)); +} + +// Helper class to transform the painter's world matrix to the object's content area, scaled to 0,0,100,100 +class WorldMatrixTransformer { +public: + WorldMatrixTransformer(QPainter* painter, RenderObject* renderObject, const IntRect& r) : m_painter(painter) + { + RenderStyle* style = renderObject->style(); + m_originalTransform = m_painter->transform(); + m_painter->translate(r.x() + style->paddingLeft().value(), r.y() + style->paddingTop().value()); + m_painter->scale((r.width() - style->paddingLeft().value() - style->paddingRight().value()) / 100.0, + (r.height() - style->paddingTop().value() - style->paddingBottom().value()) / 100.0); + } + + ~WorldMatrixTransformer() { m_painter->setTransform(m_originalTransform); } + +private: + QPainter* m_painter; + QTransform m_originalTransform; +}; + +HTMLMediaElement* RenderThemeQt::getMediaElementFromRenderObject(RenderObject* o) const +{ + Node* node = o->node(); + Node* mediaNode = node ? node->shadowAncestorNode() : 0; + if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) + return 0; + + return static_cast<HTMLMediaElement*>(mediaNode); +} + +double RenderThemeQt::mediaControlsBaselineOpacity() const +{ + return 0.4; +} + +void RenderThemeQt::paintMediaBackground(QPainter* painter, const IntRect& r) const +{ + painter->setPen(Qt::NoPen); + static QColor transparentBlack(0, 0, 0, mediaControlsBaselineOpacity() * 255); + painter->setBrush(transparentBlack); + painter->drawRoundedRect(r.x(), r.y(), r.width(), r.height(), 5.0, 5.0); +} + +QColor RenderThemeQt::getMediaControlForegroundColor(RenderObject* o) const +{ + QColor fgColor = platformActiveSelectionBackgroundColor(); + if (o && o->node()->active()) + fgColor = fgColor.lighter(); + return fgColor; +} + +bool RenderThemeQt::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + return RenderTheme::paintMediaFullscreenButton(o, paintInfo, r); +} + +bool RenderThemeQt::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o); + if (!mediaElement) + return false; + + StylePainter p(this, paintInfo); + if (!p.isValid()) + return true; + + p.painter->setRenderHint(QPainter::Antialiasing, true); + + paintMediaBackground(p.painter, r); + + WorldMatrixTransformer transformer(p.painter, o, r); + const QPointF speakerPolygon[6] = { QPointF(20, 30), QPointF(50, 30), QPointF(80, 0), + QPointF(80, 100), QPointF(50, 70), QPointF(20, 70)}; + + p.painter->setBrush(mediaElement->muted() ? Qt::darkRed : getMediaControlForegroundColor(o)); + p.painter->drawPolygon(speakerPolygon, 6); + + return false; +} + +bool RenderThemeQt::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o); + if (!mediaElement) + return false; + + StylePainter p(this, paintInfo); + if (!p.isValid()) + return true; + + p.painter->setRenderHint(QPainter::Antialiasing, true); + + paintMediaBackground(p.painter, r); + + WorldMatrixTransformer transformer(p.painter, o, r); + p.painter->setBrush(getMediaControlForegroundColor(o)); + if (mediaElement->canPlay()) { + const QPointF playPolygon[3] = { QPointF(0, 0), QPointF(100, 50), QPointF(0, 100)}; + p.painter->drawPolygon(playPolygon, 3); + } else { + p.painter->drawRect(0, 0, 30, 100); + p.painter->drawRect(70, 0, 30, 100); + } + + return false; +} + +bool RenderThemeQt::paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&) +{ + // We don't want to paint this at the moment. + return false; +} + +bool RenderThemeQt::paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&) +{ + // We don't want to paint this at the moment. + return false; +} + +bool RenderThemeQt::paintMediaCurrentTime(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + StylePainter p(this, paintInfo); + if (!p.isValid()) + return true; + + p.painter->setRenderHint(QPainter::Antialiasing, true); + paintMediaBackground(p.painter, r); + + return false; +} + +String RenderThemeQt::formatMediaControlsCurrentTime(float currentTime, float duration) const +{ + return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration); +} + +String RenderThemeQt::formatMediaControlsRemainingTime(float currentTime, float duration) const +{ + return String(); +} + +bool RenderThemeQt::paintMediaVolumeSliderTrack(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r) +{ + StylePainter p(this, paintInfo); + if (!p.isValid()) + return true; + + p.painter->setRenderHint(QPainter::Antialiasing, true); + + paintMediaBackground(p.painter, r); + + if (!o->isSlider()) + return false; + + IntRect b = toRenderBox(o)->contentBoxRect(); + + // Position the outer rectangle + int top = r.y() + b.y(); + int left = r.x() + b.x(); + int width = b.width(); + int height = b.height(); + + // Get the scale color from the page client + QPalette pal = QApplication::palette(); + setPaletteFromPageClientIfExists(pal); + const QColor highlightText = pal.brush(QPalette::Active, QPalette::HighlightedText).color(); + const QColor scaleColor(highlightText.red(), highlightText.green(), highlightText.blue(), mediaControlsBaselineOpacity() * 255); + + // Draw the outer rectangle + p.painter->setBrush(scaleColor); + p.painter->drawRect(left, top, width, height); + + if (!o->node() || !o->node()->hasTagName(inputTag)) + return false; + + HTMLInputElement* slider = static_cast<HTMLInputElement*>(o->node()); + + // Position the inner rectangle + height = height * slider->valueAsNumber(); + top += b.height() - height; + + // Draw the inner rectangle + p.painter->setPen(Qt::NoPen); + p.painter->setBrush(getMediaControlForegroundColor(o)); + p.painter->drawRect(left, top, width, height); + + return false; +} + +bool RenderThemeQt::paintMediaVolumeSliderThumb(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r) +{ + StylePainter p(this, paintInfo); + if (!p.isValid()) + return true; + + // Nothing to draw here, this is all done in the track + return false; +} + +bool RenderThemeQt::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o); + if (!mediaElement) + return false; + + StylePainter p(this, paintInfo); + if (!p.isValid()) + return true; + + p.painter->setRenderHint(QPainter::Antialiasing, true); + + paintMediaBackground(p.painter, r); + +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + if (MediaPlayer* player = mediaElement->player()) { + // Get the buffered parts of the media + PassRefPtr<TimeRanges> buffered = player->buffered(); + if (buffered->length() > 0 && player->duration() < std::numeric_limits<float>::infinity()) { + // Set the transform and brush + WorldMatrixTransformer transformer(p.painter, o, r); + p.painter->setBrush(getMediaControlForegroundColor()); + + // Paint each buffered section + ExceptionCode ex; + for (int i = 0; i < buffered->length(); i++) { + float startX = (buffered->start(i, ex) / player->duration()) * 100; + float width = ((buffered->end(i, ex) / player->duration()) * 100) - startX; + p.painter->drawRect(startX, 37, width, 26); + } + } + } +#endif + + return false; +} + +bool RenderThemeQt::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + StylePainter p(this, paintInfo); + if (!p.isValid()) + return true; + + p.painter->setRenderHint(QPainter::Antialiasing, true); + + p.painter->setPen(Qt::NoPen); + p.painter->setBrush(getMediaControlForegroundColor(o)); + p.painter->drawRect(r.x(), r.y(), r.width(), r.height()); + + return false; +} +#endif + +void RenderThemeQt::adjustSliderThumbSize(RenderObject* o) const +{ + ControlPart part = o->style()->appearance(); + + if (part == MediaSliderThumbPart) { + RenderStyle* parentStyle = o->parent()->style(); + Q_ASSERT(parentStyle); + + int parentHeight = parentStyle->height().value(); + o->style()->setWidth(Length(parentHeight / 3, Fixed)); + o->style()->setHeight(Length(parentHeight, Fixed)); + } else if (part == MediaVolumeSliderThumbPart) { + RenderStyle* parentStyle = o->parent()->style(); + Q_ASSERT(parentStyle); + + int parentWidth = parentStyle->width().value(); + o->style()->setHeight(Length(parentWidth / 3, Fixed)); + o->style()->setWidth(Length(parentWidth, Fixed)); + } else if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) { + QStyleOptionSlider option; + if (part == SliderThumbVerticalPart) + option.orientation = Qt::Vertical; + + QStyle* style = qStyle(); + + int width = style->pixelMetric(QStyle::PM_SliderLength, &option); + int height = style->pixelMetric(QStyle::PM_SliderThickness, &option); + o->style()->setWidth(Length(width, Fixed)); + o->style()->setHeight(Length(height, Fixed)); + } +} + +double RenderThemeQt::caretBlinkInterval() const +{ + return QApplication::cursorFlashTime() / 1000.0 / 2.0; +} + +} + +// vim: ts=4 sw=4 et |