RenderThemeQt.cpp   [plain text]


/*
 * 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 "qwebpage.h"
#include "RenderThemeQt.h"
#include "ChromeClientQt.h"
#include "NotImplemented.h"

#include <QApplication>
#include <QColor>
#include <QDebug>
#include <QFile>
#include <QWidget>
#include <QPainter>
#include <QPushButton>
#include <QStyleFactory>
#include <QStyleOptionButton>
#include <QStyleOptionFrameV2>

#include "Color.h"
#include "CSSStyleSelector.h"
#include "CSSStyleSheet.h"
#include "FontSelector.h"
#include "Document.h"
#include "Page.h"
#include "Font.h"
#include "RenderTheme.h"
#include "GraphicsContext.h"
#include "HTMLMediaElement.h"
#include "HTMLNames.h"
#include "RenderBox.h"

namespace WebCore {

using namespace HTMLNames;


StylePainter::StylePainter(const RenderObject::PaintInfo& paintInfo)
{
    init(paintInfo.context ? paintInfo.context : 0);
}

StylePainter::StylePainter(GraphicsContext* context)
{
    init(context);
}

void StylePainter::init(GraphicsContext* context)
{
    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 = (widget ? widget->style() : QApplication::style());

    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);
    }
}

RenderTheme* theme()
{
    static RenderThemeQt rt;
    return &rt;
}

RenderThemeQt::RenderThemeQt()
    : RenderTheme()
{
    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

    m_fallbackStyle = 0;
}

RenderThemeQt::~RenderThemeQt()
{
    delete m_fallbackStyle;
}

// for some widget painting, we need to fallback to Windows style
QStyle* RenderThemeQt::fallbackStyle()
{
    if(!m_fallbackStyle)
        m_fallbackStyle = QStyleFactory::create(QLatin1String("windows"));

    if(!m_fallbackStyle)
        m_fallbackStyle = QApplication::style();

    return m_fallbackStyle;
}

bool RenderThemeQt::supportsHover(const RenderStyle*) const
{
    return true;
}

bool RenderThemeQt::supportsFocusRing(const RenderStyle* style) const
{
    return true; // Qt provides this through the style
}

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;
}

static QRect inflateButtonRect(const QRect& originalRect)
{
    QStyleOptionButton option;
    option.state |= QStyle::State_Small;
    option.rect = originalRect;

    QRect layoutRect = QApplication::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);
    } else {
        return originalRect;
    }
}

void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& r)
{
    switch (o->style()->appearance()) {
    case CheckboxPart: {
        break;
    }
    case RadioPart: {
        break;
    }
    case PushButtonPart:
    case ButtonPart: {
        QRect inflatedRect = inflateButtonRect(r);
        r = IntRect(inflatedRect.x(), inflatedRect.y(), inflatedRect.width(), inflatedRect.height());
        break;
    }
    case MenulistPart: {
        break;
    }
    default:
        break;
    }
}

bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border,
                                     const FillLayer& background, const Color& backgroundColor) const
{
    if (style->appearance() == TextFieldPart
            || style->appearance() == TextAreaPart
            || style->appearance() == ListboxPart) {
        return style->border() != border;
    }

    return RenderTheme::isControlStyled(style, border, background, backgroundColor);
}

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();
}

void RenderThemeQt::systemFont(int propId, FontDescription& fontDescription) const
{
    // no-op
}

int RenderThemeQt::minimumMenuListSize(RenderStyle*) const
{
    const QFontMetrics &fm = QApplication::fontMetrics();
    return 7 * fm.width(QLatin1Char('x'));
}

static void computeSizeBasedOnStyle(RenderStyle* renderStyle)
{
    // If the width and height are both specified, then we have nothing to do.
    if (!renderStyle->width().isIntrinsicOrAuto() && !renderStyle->height().isAuto())
        return;

    QSize size(0, 0);
    const QFontMetrics fm(renderStyle->font().font());
    QStyle* applicationStyle = QApplication::style();

    switch (renderStyle->appearance()) {
    case CheckboxPart: {
        QStyleOption styleOption;
        styleOption.state |= QStyle::State_Small;
        int checkBoxWidth = applicationStyle->pixelMetric(QStyle::PM_IndicatorWidth,
                                                          &styleOption);
        size = QSize(checkBoxWidth, checkBoxWidth);
        break;
    }
    case RadioPart: {
        QStyleOption styleOption;
        styleOption.state |= QStyle::State_Small;
        int radioWidth = applicationStyle->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth,
                                                       &styleOption);
        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 = applicationStyle->sizeFromContents(QStyle::CT_PushButton,
                                                                  &styleOption,
                                                                  contentSize,
                                                                  0);
        styleOption.rect = QRect(0, 0, pushButtonSize.width(), pushButtonSize.height());
        QRect layoutRect = applicationStyle->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 = applicationStyle->sizeFromContents(QStyle::CT_ComboBox,
                                                        &styleOption,
                                                        QSize(0, contentHeight),
                                                        0);
        size.setHeight(menuListSize.height());
        break;
    }
    case TextFieldPart: {
        const int verticalMargin = 1;
        const int horizontalMargin = 2;
        int h = qMax(fm.lineSpacing(), 14) + 2*verticalMargin;
        int w = fm.width(QLatin1Char('x')) * 17 + 2*horizontalMargin;
        QStyleOptionFrameV2 opt;
        opt.lineWidth = applicationStyle->pixelMetric(QStyle::PM_DefaultFrameWidth,
                                                           &opt, 0);
        QSize sz = applicationStyle->sizeFromContents(QStyle::CT_LineEdit,
                                                           &opt,
                                                           QSize(w, h).expandedTo(QApplication::globalStrut()),
                                                           0);
        size.setHeight(sz.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 RenderObject::PaintInfo& i, const IntRect& r)
{
    return paintButton(o, i, r);
}

void RenderThemeQt::setRadioSize(RenderStyle* style) const
{
    computeSizeBasedOnStyle(style);
}

bool RenderThemeQt::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
{
    return paintButton(o, i, r);
}

void RenderThemeQt::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
{
    // Ditch the border.
    style->resetBorder();

    // Height is locked to auto.
    style->setHeight(Length(Auto));

    // 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);

    style->setColor(QApplication::palette().text().color());
}

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 = QApplication::style()->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 = QApplication::style()->subElementRect(QStyle::SE_PushButtonLayoutItem,
                                                                  &styleOption, 0);
    if (!layoutRect.isNull()) {
        QRect contentsRect = QApplication::style()->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 RenderObject::PaintInfo& i, const IntRect& r)
{
    StylePainter p(i);
    if (!p.isValid())
       return true;

    QStyleOptionButton option;
    if (p.widget)
       option.initFrom(p.widget);

    option.rect = r;
    option.state |= QStyle::State_Small;

    ControlPart appearance = applyTheme(option, o);
    if(appearance == PushButtonPart || appearance == ButtonPart) {
        option.rect = inflateButtonRect(option.rect);
        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->setColor(QApplication::palette().text().color());
}

bool RenderThemeQt::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
{
    StylePainter p(i);
    if (!p.isValid())
        return true;

    QStyleOptionFrameV2 panel;
    if (p.widget)
        panel.initFrom(p.widget);

    panel.rect = r;
    panel.lineWidth = p.style->pixelMetric(QStyle::PM_DefaultFrameWidth, &panel, p.widget);
    panel.state |= QStyle::State_Sunken;
    panel.features = QStyleOptionFrameV2::None;

    // Get the correct theme data for a text field
    ControlPart appearance = applyTheme(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 RenderObject::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);

    style->setColor(QApplication::palette().text().color());
}

void RenderThemeQt::setPopupPadding(RenderStyle* style) const
{
    const int padding = 8;
    style->setPaddingLeft(Length(padding, Fixed));

    QStyleOptionComboBox opt;
    int w = QApplication::style()->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 RenderObject::PaintInfo& i, const IntRect& r)
{
    StylePainter p(i);
    if (!p.isValid())
        return true;

    QStyleOptionComboBox opt;
    if (p.widget)
        opt.initFrom(p.widget);
    ControlPart appearance = applyTheme(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* selector, RenderStyle* style,
                                              Element* e) const
{
    // WORKAROUND because html4.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();

    // 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);

    style->setColor(QApplication::palette().text().color());
}

bool RenderThemeQt::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i,
                                        const IntRect& r)
{
    StylePainter p(i);
    if (!p.isValid())
        return true;

    QStyleOptionComboBox option;
    if (p.widget)
        option.initFrom(p.widget);
    applyTheme(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;
}

bool RenderThemeQt::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& pi,
                                     const IntRect& r)
{
    notImplemented();
    return RenderTheme::paintSliderTrack(o, pi, r);
}

bool RenderThemeQt::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& pi,
                                     const IntRect& r)
{
    notImplemented();
    return RenderTheme::paintSliderThumb(o, pi, r);
}

bool RenderThemeQt::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& pi,
                                     const IntRect& r)
{
    paintTextField(o, pi, r);
    return false;
}

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 RenderObject::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 RenderObject::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 RenderObject::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:
            return true;
        default: // No for all others...
            return false;
    }
}

ControlPart RenderThemeQt::applyTheme(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;

    if (supportsFocus(o->style()->appearance()) && isFocused(o)) {
        option.state |= QStyle::State_HasFocus;
        option.state |= QStyle::State_KeyboardFocusChange;
    }

    if (isHovered(o))
        option.state |= QStyle::State_MouseOver;

    ControlPart result = o->style()->appearance();

    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)
                option.state |= QStyle::State_Raised;
            break;
        }
    }

    if(result == RadioPart || result == CheckboxPart)
        option.state |= (isChecked(o) ? QStyle::State_On : QStyle::State_Off);

    // If the webview has a custom palette, use it
    Page* page = o->document()->page();
    if (page) {
        QWidget* view = static_cast<ChromeClientQt*>(page->chrome()->client())->m_webPage->view();
        if (view)
            option.palette = view->palette();
    }

    return result;
}

#if ENABLE(VIDEO)

String RenderThemeQt::extraMediaControlsStyleSheet()
{
    QFile platformStyleSheet(QLatin1String(":/webcore/css/mediaControls-extras.css"));
    if (platformStyleSheet.open(QFile::ReadOnly)) {
        QByteArray sheetData = platformStyleSheet.readAll();
        return QString::fromUtf8(sheetData.constData(), sheetData.length());
    }

    return String();
}

// 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->element();
    Node* mediaNode = node ? node->shadowAncestorNode() : 0;
    if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
        return 0;

    return static_cast<HTMLMediaElement*>(mediaNode);
}

void RenderThemeQt::paintMediaBackground(QPainter* painter, const IntRect& r) const
{
    painter->setPen(Qt::NoPen);
    static QColor transparentBlack(0,0,0,100);
    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->element()->active())
        fgColor = fgColor.lighter();
    return fgColor;
}

bool RenderThemeQt::paintMediaFullscreenButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
{
   return RenderTheme::paintMediaFullscreenButton(o, paintInfo, r);
}

bool RenderThemeQt::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
{
    HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o);
    if (!mediaElement)
        return false;

    StylePainter p(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(getMediaControlForegroundColor(o));
    p.painter->drawPolygon(speakerPolygon, 6);

    if (mediaElement->muted()) {
        p.painter->setPen(Qt::red);
        p.painter->drawLine(0, 100, 100, 0);
    }

    return false;
}

bool RenderThemeQt::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
{
    HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o);
    if (!mediaElement)
        return false;

    StylePainter p(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* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
{
    // We don't want to paint this at the moment.
    return false;
}

bool RenderThemeQt::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
{
    // We don't want to paint this at the moment.
    return false;
}

bool RenderThemeQt::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
{
    HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o);
    if (!mediaElement)
        return false;

    StylePainter p(paintInfo);
    if (!p.isValid())
        return true;

    p.painter->setRenderHint(QPainter::Antialiasing, true);

    paintMediaBackground(p.painter, r);

    if (MediaPlayer* player = mediaElement->player()) {
        if (player->totalBytesKnown()) {
            float percentLoaded = static_cast<float>(player->bytesLoaded()) / player->totalBytes();

            WorldMatrixTransformer transformer(p.painter, o, r);
            p.painter->setBrush(getMediaControlForegroundColor());
            p.painter->drawRect(0, 37, 100 * percentLoaded, 26);
        }
    }

    return false;
}

bool RenderThemeQt::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
{
    HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o->parent());
    if (!mediaElement)
        return false;

    StylePainter p(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
{
    if (o->style()->appearance() == 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));
    }
}

double RenderThemeQt::caretBlinkInterval() const
{
    return  QApplication::cursorFlashTime() / 1000.0 / 2.0;
}

}

// vim: ts=4 sw=4 et