1/*
2 * This file is part of the WebKit project.
3 *
4 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5 *
6 * Copyright (C) 2006 Zack Rusin <zack@kde.org>
7 *               2006 Dirk Mueller <mueller@kde.org>
8 *               2006 Nikolas Zimmermann <zimmermann@kde.org>
9 * Copyright (C) 2008 Holger Hans Peter Freyther
10 *
11 * All rights reserved.
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 * Library General Public License for more details.
22 *
23 * You should have received a copy of the GNU Library General Public License
24 * along with this library; see the file COPYING.LIB.  If not, write to
25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
27 *
28 */
29
30#include "config.h"
31#include "RenderThemeQt.h"
32
33#include "CSSValueKeywords.h"
34#include "Chrome.h"
35#include "ChromeClient.h"
36#include "Color.h"
37#include "ExceptionCodePlaceholder.h"
38#include "FileList.h"
39#include "Font.h"
40#include "FontSelector.h"
41#include "GraphicsContext.h"
42#include "HTMLInputElement.h"
43#include "HTMLMediaElement.h"
44#include "HTMLNames.h"
45#include "LocalizedStrings.h"
46#if ENABLE(VIDEO)
47#include "MediaControlElements.h"
48#endif
49#include "NotImplemented.h"
50#include "Page.h"
51#include "PaintInfo.h"
52#include "RenderBox.h"
53#if ENABLE(PROGRESS_ELEMENT)
54#include "RenderProgress.h"
55#endif
56#include "RenderTheme.h"
57#include "RenderThemeQtMobile.h"
58#include "ScrollbarTheme.h"
59#include "StyleResolver.h"
60#include "TimeRanges.h"
61#include "UserAgentStyleSheets.h"
62#include <wtf/text/StringBuilder.h>
63
64#include <QColor>
65#include <QFile>
66#include <QFontMetrics>
67#include <QGuiApplication>
68
69#include <QStyleHints>
70
71namespace WebCore {
72
73using namespace HTMLNames;
74
75// These values all match Safari/Win/Chromium
76static const float defaultControlFontPixelSize = 13;
77static const float defaultCancelButtonSize = 9;
78static const float minCancelButtonSize = 5;
79static const float maxCancelButtonSize = 21;
80static const float defaultSearchFieldResultsDecorationSize = 13;
81static const float minSearchFieldResultsDecorationSize = 9;
82static const float maxSearchFieldResultsDecorationSize = 30;
83static const float defaultSearchFieldResultsButtonWidth = 18;
84
85static QtThemeFactoryFunction themeFactory;
86static ScrollbarTheme* scrollbarTheme;
87
88RenderThemeQt::RenderThemeQt(Page* page)
89    : RenderTheme()
90    , m_page(page)
91{
92    m_buttonFontFamily = QGuiApplication::font().family();
93}
94
95void RenderThemeQt::setCustomTheme(QtThemeFactoryFunction factory, ScrollbarTheme* customScrollbarTheme)
96{
97    themeFactory = factory;
98    scrollbarTheme = customScrollbarTheme;
99}
100
101ScrollbarTheme* RenderThemeQt::customScrollbarTheme()
102{
103    return scrollbarTheme;
104}
105
106static PassRefPtr<RenderTheme> createTheme(Page* page)
107{
108    if (themeFactory)
109        return themeFactory(page);
110    return RenderThemeQtMobile::create(page);
111}
112
113PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
114{
115    if (page)
116        return createTheme(page);
117    static RenderTheme* fallback = createTheme(0).leakRef();
118    return fallback;
119}
120
121// Remove this when SearchFieldPart is style-able in RenderTheme::isControlStyled()
122bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const
123{
124    switch (style->appearance()) {
125    case SearchFieldPart:
126        // Test the style to see if the UA border and background match.
127        return (style->border() != border
128                || *style->backgroundLayers() != fill
129                || style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor);
130    default:
131        return RenderTheme::isControlStyled(style, border, fill, backgroundColor);
132    }
133}
134
135String RenderThemeQt::extraDefaultStyleSheet()
136{
137    StringBuilder result;
138    result.append(RenderTheme::extraDefaultStyleSheet());
139    // When no theme factory is provided we default to using our platform independent "Mobile Qt" theme,
140    // which requires the following stylesheets.
141    if (!themeFactory) {
142        result.append(String(themeQtNoListboxesUserAgentStyleSheet, sizeof(themeQtNoListboxesUserAgentStyleSheet)));
143        result.append(String(mobileThemeQtUserAgentStyleSheet, sizeof(mobileThemeQtUserAgentStyleSheet)));
144    }
145    return result.toString();
146}
147
148bool RenderThemeQt::supportsHover(const RenderStyle*) const
149{
150    return true;
151}
152
153bool RenderThemeQt::supportsFocusRing(const RenderStyle* style) const
154{
155    switch (style->appearance()) {
156    case CheckboxPart:
157    case RadioPart:
158    case PushButtonPart:
159    case SquareButtonPart:
160    case ButtonPart:
161    case ButtonBevelPart:
162    case ListboxPart:
163    case ListItemPart:
164    case MenulistPart:
165    case MenulistButtonPart:
166    case SliderHorizontalPart:
167    case SliderVerticalPart:
168    case SliderThumbHorizontalPart:
169    case SliderThumbVerticalPart:
170    case SearchFieldPart:
171    case SearchFieldResultsButtonPart:
172    case SearchFieldCancelButtonPart:
173    case TextFieldPart:
174    case TextAreaPart:
175        return true;
176    default:
177        return false;
178    }
179}
180
181int RenderThemeQt::baselinePosition(const RenderObject* o) const
182{
183    if (!o->isBox())
184        return 0;
185
186    if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart)
187        return toRenderBox(o)->marginTop() + toRenderBox(o)->height() - 2; // Same as in old khtml
188    return RenderTheme::baselinePosition(o);
189}
190
191bool RenderThemeQt::controlSupportsTints(const RenderObject* o) const
192{
193    if (!isEnabled(o))
194        return false;
195
196    // Checkboxes only have tint when checked.
197    if (o->style()->appearance() == CheckboxPart)
198        return isChecked(o);
199
200    // For now assume other controls have tint if enabled.
201    return true;
202}
203
204bool RenderThemeQt::supportsControlTints() const
205{
206    return true;
207}
208
209QRect RenderThemeQt::inflateButtonRect(const QRect& originalRect) const
210{
211    return originalRect;
212}
213
214void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& rect)
215{
216    switch (o->style()->appearance()) {
217    case CheckboxPart:
218        break;
219    case RadioPart:
220        break;
221    case PushButtonPart:
222    case ButtonPart: {
223        QRect inflatedRect = inflateButtonRect(rect);
224        rect = IntRect(inflatedRect.x(), inflatedRect.y(), inflatedRect.width(), inflatedRect.height());
225        break;
226    }
227    case MenulistPart:
228        break;
229    default:
230        break;
231    }
232}
233
234Color RenderThemeQt::platformActiveSelectionBackgroundColor() const
235{
236    return colorPalette().brush(QPalette::Active, QPalette::Highlight).color();
237}
238
239Color RenderThemeQt::platformInactiveSelectionBackgroundColor() const
240{
241    return colorPalette().brush(QPalette::Inactive, QPalette::Highlight).color();
242}
243
244Color RenderThemeQt::platformActiveSelectionForegroundColor() const
245{
246    return colorPalette().brush(QPalette::Active, QPalette::HighlightedText).color();
247}
248
249Color RenderThemeQt::platformInactiveSelectionForegroundColor() const
250{
251    return colorPalette().brush(QPalette::Inactive, QPalette::HighlightedText).color();
252}
253
254Color RenderThemeQt::platformFocusRingColor() const
255{
256    return colorPalette().brush(QPalette::Active, QPalette::Highlight).color();
257}
258
259void RenderThemeQt::systemFont(int, FontDescription&) const
260{
261    // no-op
262}
263
264Color RenderThemeQt::systemColor(int cssValueId) const
265{
266    QPalette pal = colorPalette();
267    switch (cssValueId) {
268    case CSSValueButtontext:
269        return pal.brush(QPalette::Active, QPalette::ButtonText).color();
270    case CSSValueCaptiontext:
271        return pal.brush(QPalette::Active, QPalette::Text).color();
272    default:
273        return RenderTheme::systemColor(cssValueId);
274    }
275}
276
277int RenderThemeQt::minimumMenuListSize(RenderStyle*) const
278{
279    // FIXME: Later we need a way to query the UI process for the dpi
280    const QFontMetrics fm(QGuiApplication::font());
281    return fm.width(QLatin1Char('x'));
282}
283
284void RenderThemeQt::setCheckboxSize(RenderStyle* style) const
285{
286    computeSizeBasedOnStyle(style);
287}
288
289bool RenderThemeQt::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r)
290{
291    return paintButton(o, i, r);
292}
293
294void RenderThemeQt::setRadioSize(RenderStyle* style) const
295{
296    computeSizeBasedOnStyle(style);
297}
298
299bool RenderThemeQt::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r)
300{
301    return paintButton(o, i, r);
302}
303
304void RenderThemeQt::setButtonSize(RenderStyle* style) const
305{
306    computeSizeBasedOnStyle(style);
307}
308
309void RenderThemeQt::adjustTextFieldStyle(StyleResolver*, RenderStyle* style, Element*) const
310{
311    // Resetting the style like this leads to differences like:
312    // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)]
313    // + RenderTextControl {INPUT} at (2,2) size 166x26
314    // in layout tests when a CSS style is applied that doesn't affect background color, border or
315    // padding. Just worth keeping in mind!
316    style->setBackgroundColor(Color::transparent);
317    style->resetBorder();
318    style->resetPadding();
319    computeSizeBasedOnStyle(style);
320}
321
322void RenderThemeQt::adjustTextAreaStyle(StyleResolver* selector, RenderStyle* style, Element* element) const
323{
324    adjustTextFieldStyle(selector, style, element);
325}
326
327bool RenderThemeQt::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
328{
329    return paintTextField(o, i, r);
330}
331
332void RenderThemeQt::adjustMenuListStyle(StyleResolver*, RenderStyle* style, Element*) const
333{
334    style->resetBorder();
335
336    // Height is locked to auto.
337    style->setHeight(Length(Auto));
338
339    // White-space is locked to pre
340    style->setWhiteSpace(PRE);
341
342    computeSizeBasedOnStyle(style);
343
344    // Add in the padding that we'd like to use.
345    setPopupPadding(style);
346}
347
348void RenderThemeQt::adjustMenuListButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
349{
350    // Height is locked to auto.
351    style->setHeight(Length(Auto));
352
353    // White-space is locked to pre
354    style->setWhiteSpace(PRE);
355
356    computeSizeBasedOnStyle(style);
357
358    // Add in the padding that we'd like to use.
359    setPopupPadding(style);
360}
361
362#if ENABLE(PROGRESS_ELEMENT)
363double RenderThemeQt::animationRepeatIntervalForProgressBar(RenderProgress* renderProgress) const
364{
365    if (renderProgress->position() >= 0)
366        return 0;
367
368    // FIXME: Use hard-coded value until http://bugreports.qt.nokia.com/browse/QTBUG-9171 is fixed.
369    // Use the value from windows style which is 10 fps.
370    return 0.1;
371}
372
373void RenderThemeQt::adjustProgressBarStyle(StyleResolver*, RenderStyle* style, Element*) const
374{
375    style->setBoxShadow(nullptr);
376}
377#endif
378
379void RenderThemeQt::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element*) const
380{
381    style->setBoxShadow(nullptr);
382}
383
384void RenderThemeQt::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const
385{
386    RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
387    style->setBoxShadow(nullptr);
388}
389
390#if ENABLE(DATALIST_ELEMENT)
391IntSize RenderThemeQt::sliderTickSize() const
392{
393    // FIXME: We need to set this to the size of one tick mark.
394    return IntSize(0, 0);
395}
396
397int RenderThemeQt::sliderTickOffsetFromTrackCenter() const
398{
399    // FIXME: We need to set this to the position of the tick marks.
400    return 0;
401}
402#endif
403
404bool RenderThemeQt::paintSearchField(RenderObject* o, const PaintInfo& pi,
405                                     const IntRect& r)
406{
407    return paintTextField(o, pi, r);
408}
409
410void RenderThemeQt::adjustSearchFieldStyle(StyleResolver*, RenderStyle* style, Element*) const
411{
412    // Resetting the style like this leads to differences like:
413    // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)]
414    // + RenderTextControl {INPUT} at (2,2) size 166x26
415    // in layout tests when a CSS style is applied that doesn't affect background color, border or
416    // padding. Just worth keeping in mind!
417    style->setBackgroundColor(Color::transparent);
418    style->resetBorder();
419    style->resetPadding();
420    computeSizeBasedOnStyle(style);
421}
422
423void RenderThemeQt::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
424{
425    // Logic taken from RenderThemeChromium.cpp.
426    // Scale the button size based on the font size.
427    float fontScale = style->fontSize() / defaultControlFontPixelSize;
428    int cancelButtonSize = lroundf(qMin(qMax(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
429    style->setWidth(Length(cancelButtonSize, Fixed));
430    style->setHeight(Length(cancelButtonSize, Fixed));
431}
432
433// Function taken from RenderThemeChromium.cpp
434IntRect RenderThemeQt::convertToPaintingRect(RenderObject* inputRenderer, const RenderObject* partRenderer, IntRect partRect, const IntRect& localOffset) const
435{
436    // Compute an offset between the part renderer and the input renderer.
437    IntSize offsetFromInputRenderer = -roundedIntSize(partRenderer->offsetFromAncestorContainer(inputRenderer));
438    // Move the rect into partRenderer's coords.
439    partRect.move(offsetFromInputRenderer);
440    // Account for the local drawing offset.
441    partRect.move(localOffset.x(), localOffset.y());
442
443    return partRect;
444}
445
446QPalette RenderThemeQt::colorPalette() const
447{
448    return QGuiApplication::palette();
449}
450
451bool RenderThemeQt::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& pi,
452                                                 const IntRect& r)
453{
454    // Logic copied from RenderThemeChromium.cpp.
455
456    // Get the renderer of <input> element.
457    Node* input = o->node()->shadowHost();
458    if (!input)
459        input = o->node();
460    if (!input->renderer()->isBox())
461        return false;
462    RenderBox* inputRenderBox = toRenderBox(input->renderer());
463    IntRect inputContentBox = pixelSnappedIntRect(inputRenderBox->contentBoxRect());
464
465    // Make sure the scaled button stays square and will fit in its parent's box.
466    int cancelButtonSize = qMin(inputContentBox.width(), qMin(inputContentBox.height(), r.height()));
467    // Calculate cancel button's coordinates relative to the input element.
468    // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will
469    // be one pixel closer to the bottom of the field. This tends to look better with the text.
470    IntRect cancelButtonRect(o->offsetFromAncestorContainer(inputRenderBox).width(),
471                             inputContentBox.y() + (inputContentBox.height() - cancelButtonSize + 1) / 2,
472                             cancelButtonSize, cancelButtonSize);
473    IntRect paintingRect = convertToPaintingRect(inputRenderBox, o, cancelButtonRect, r);
474    static Image* cancelImage = Image::loadPlatformResource("searchCancelButton").leakRef();
475    static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelButtonPressed").leakRef();
476    pi.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage,
477                                 o->style()->colorSpace(), paintingRect);
478    return false;
479}
480
481void RenderThemeQt::adjustSearchFieldDecorationStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
482{
483    notImplemented();
484    RenderTheme::adjustSearchFieldDecorationStyle(styleResolver, style, e);
485}
486
487bool RenderThemeQt::paintSearchFieldDecoration(RenderObject* o, const PaintInfo& pi,
488                                               const IntRect& r)
489{
490    notImplemented();
491    return RenderTheme::paintSearchFieldDecoration(o, pi, r);
492}
493
494void RenderThemeQt::adjustSearchFieldResultsDecorationStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
495{
496    notImplemented();
497    RenderTheme::adjustSearchFieldResultsDecorationStyle(styleResolver, style, e);
498}
499
500bool RenderThemeQt::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& pi,
501                                                      const IntRect& r)
502{
503    notImplemented();
504    return RenderTheme::paintSearchFieldResultsDecoration(o, pi, r);
505}
506
507#ifndef QT_NO_SPINBOX
508void RenderThemeQt::adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
509{
510    // Use the same width as our native scrollbar
511    int width = ScrollbarTheme::theme()->scrollbarThickness();
512    style->setWidth(Length(width, Fixed));
513    style->setMinWidth(Length(width, Fixed));
514}
515#endif
516
517bool RenderThemeQt::supportsFocus(ControlPart appearance) const
518{
519    switch (appearance) {
520    case PushButtonPart:
521    case ButtonPart:
522    case TextFieldPart:
523    case TextAreaPart:
524    case ListboxPart:
525    case MenulistPart:
526    case RadioPart:
527    case CheckboxPart:
528    case SliderHorizontalPart:
529    case SliderVerticalPart:
530        return true;
531    default: // No for all others...
532        return false;
533    }
534}
535
536#if ENABLE(VIDEO)
537
538String RenderThemeQt::extraMediaControlsStyleSheet()
539{
540    String result = String(mediaControlsQtUserAgentStyleSheet, sizeof(mediaControlsQtUserAgentStyleSheet));
541
542    if (m_page && m_page->chrome().requiresFullscreenForVideoPlayback())
543        result.append(String(mediaControlsQtFullscreenUserAgentStyleSheet, sizeof(mediaControlsQtFullscreenUserAgentStyleSheet)));
544
545    return result;
546}
547
548// Helper class to transform the painter's world matrix to the object's content area, scaled to 0,0,100,100
549class WorldMatrixTransformer {
550public:
551    WorldMatrixTransformer(QPainter* painter, RenderObject* renderObject, const IntRect& r) : m_painter(painter)
552    {
553        RenderStyle* style = renderObject->style();
554        m_originalTransform = m_painter->transform();
555        m_painter->translate(r.x() + style->paddingLeft().value(), r.y() + style->paddingTop().value());
556        m_painter->scale((r.width() - style->paddingLeft().value() - style->paddingRight().value()) / 100.0,
557             (r.height() - style->paddingTop().value() - style->paddingBottom().value()) / 100.0);
558    }
559
560    ~WorldMatrixTransformer() { m_painter->setTransform(m_originalTransform); }
561
562private:
563    QPainter* m_painter;
564    QTransform m_originalTransform;
565};
566
567double RenderThemeQt::mediaControlsBaselineOpacity() const
568{
569    return 0.4;
570}
571
572void RenderThemeQt::paintMediaBackground(QPainter* painter, const IntRect& r) const
573{
574    painter->setPen(Qt::NoPen);
575    static QColor transparentBlack(0, 0, 0, mediaControlsBaselineOpacity() * 255);
576    painter->setBrush(transparentBlack);
577    painter->drawRoundedRect(r.x(), r.y(), r.width(), r.height(), 5.0, 5.0);
578}
579
580static bool mediaElementCanPlay(RenderObject* o)
581{
582    HTMLMediaElement* mediaElement = toParentMediaElement(o);
583    if (!mediaElement)
584        return false;
585
586    return mediaElement->readyState() > HTMLMediaElement::HAVE_METADATA
587           || (mediaElement->readyState() == HTMLMediaElement::HAVE_NOTHING
588               && o->style()->appearance() == MediaPlayButtonPart && mediaElement->preload() == "none");
589}
590
591QColor RenderThemeQt::getMediaControlForegroundColor(RenderObject* o) const
592{
593    QColor fgColor = platformActiveSelectionBackgroundColor();
594    if (!o)
595        return fgColor;
596
597    if (o->node() && o->node()->isElementNode() && toElement(o->node())->active())
598        fgColor = fgColor.lighter();
599
600    if (!mediaElementCanPlay(o))
601        fgColor = colorPalette().brush(QPalette::Disabled, QPalette::Text).color();
602
603    return fgColor;
604}
605
606bool RenderThemeQt::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
607{
608    HTMLMediaElement* mediaElement = toParentMediaElement(o);
609    if (!mediaElement)
610        return false;
611
612    QSharedPointer<StylePainter> p = getStylePainter(paintInfo);
613    if (p.isNull() || !p->isValid())
614        return true;
615
616    p->painter->setRenderHint(QPainter::Antialiasing, true);
617
618    paintMediaBackground(p->painter, r);
619
620    WorldMatrixTransformer transformer(p->painter, o, r);
621    const QPointF arrowPolygon[9] = { QPointF(20, 0), QPointF(100, 0), QPointF(100, 80),
622            QPointF(80, 80), QPointF(80, 30), QPointF(10, 100), QPointF(0, 90), QPointF(70, 20), QPointF(20, 20)};
623
624    p->painter->setBrush(getMediaControlForegroundColor(o));
625    p->painter->drawPolygon(arrowPolygon, 9);
626
627    return false;
628}
629
630bool RenderThemeQt::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
631{
632    HTMLMediaElement* mediaElement = toParentMediaElement(o);
633    if (!mediaElement)
634        return false;
635
636    QSharedPointer<StylePainter> p = getStylePainter(paintInfo);
637    if (p.isNull() || !p->isValid())
638        return true;
639
640    p->painter->setRenderHint(QPainter::Antialiasing, true);
641
642    paintMediaBackground(p->painter, r);
643
644    WorldMatrixTransformer transformer(p->painter, o, r);
645    const QPointF speakerPolygon[6] = { QPointF(20, 30), QPointF(50, 30), QPointF(80, 0),
646            QPointF(80, 100), QPointF(50, 70), QPointF(20, 70)};
647
648    p->painter->setBrush(mediaElement->muted() ? Qt::darkRed : getMediaControlForegroundColor(o));
649    p->painter->drawPolygon(speakerPolygon, 6);
650
651    return false;
652}
653
654bool RenderThemeQt::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
655{
656    HTMLMediaElement* mediaElement = toParentMediaElement(o);
657    if (!mediaElement)
658        return false;
659
660    QSharedPointer<StylePainter> p = getStylePainter(paintInfo);
661    if (p.isNull() || !p->isValid())
662        return true;
663
664    p->painter->setRenderHint(QPainter::Antialiasing, true);
665
666    paintMediaBackground(p->painter, r);
667
668    WorldMatrixTransformer transformer(p->painter, o, r);
669    p->painter->setBrush(getMediaControlForegroundColor(o));
670    if (mediaElement->canPlay()) {
671        const QPointF playPolygon[3] = { QPointF(0, 0), QPointF(100, 50), QPointF(0, 100)};
672        p->painter->drawPolygon(playPolygon, 3);
673    } else {
674        p->painter->drawRect(0, 0, 30, 100);
675        p->painter->drawRect(70, 0, 30, 100);
676    }
677
678    return false;
679}
680
681bool RenderThemeQt::paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&)
682{
683    // We don't want to paint this at the moment.
684    return false;
685}
686
687bool RenderThemeQt::paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&)
688{
689    // We don't want to paint this at the moment.
690    return false;
691}
692
693bool RenderThemeQt::paintMediaCurrentTime(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
694{
695    QSharedPointer<StylePainter> p = getStylePainter(paintInfo);
696    if (p.isNull() || !p->isValid())
697        return true;
698
699    p->painter->setRenderHint(QPainter::Antialiasing, true);
700    paintMediaBackground(p->painter, r);
701
702    return false;
703}
704
705String RenderThemeQt::formatMediaControlsCurrentTime(float currentTime, float duration) const
706{
707    return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
708}
709
710String RenderThemeQt::formatMediaControlsRemainingTime(float currentTime, float duration) const
711{
712    return String();
713}
714
715bool RenderThemeQt::paintMediaVolumeSliderTrack(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r)
716{
717    QSharedPointer<StylePainter> p = getStylePainter(paintInfo);
718    if (p.isNull() || !p->isValid())
719        return true;
720
721    p->painter->setRenderHint(QPainter::Antialiasing, true);
722
723    paintMediaBackground(p->painter, r);
724
725    if (!o->isSlider())
726        return false;
727
728    IntRect b = pixelSnappedIntRect(toRenderBox(o)->contentBoxRect());
729
730    // Position the outer rectangle
731    int top = r.y() + b.y();
732    int left = r.x() + b.x();
733    int width = b.width();
734    int height = b.height();
735
736    QPalette pal = colorPalette();
737    const QColor highlightText = pal.brush(QPalette::Active, QPalette::HighlightedText).color();
738    const QColor scaleColor(highlightText.red(), highlightText.green(), highlightText.blue(), mediaControlsBaselineOpacity() * 255);
739
740    // Draw the outer rectangle
741    p->painter->setBrush(scaleColor);
742    p->painter->drawRect(left, top, width, height);
743
744    if (!o->node() || !o->node()->hasTagName(inputTag))
745        return false;
746
747    HTMLInputElement* slider = static_cast<HTMLInputElement*>(o->node());
748
749    // Position the inner rectangle
750    height = height * slider->valueAsNumber();
751    top += b.height() - height;
752
753    // Draw the inner rectangle
754    p->painter->setPen(Qt::NoPen);
755    p->painter->setBrush(getMediaControlForegroundColor(o));
756    p->painter->drawRect(left, top, width, height);
757
758    return false;
759}
760
761bool RenderThemeQt::paintMediaVolumeSliderThumb(RenderObject *o, const PaintInfo &paintInfo, const IntRect &r)
762{
763    QSharedPointer<StylePainter> p = getStylePainter(paintInfo);
764    if (p.isNull() || !p->isValid())
765        return true;
766
767    // Nothing to draw here, this is all done in the track
768    return false;
769}
770
771bool RenderThemeQt::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
772{
773    HTMLMediaElement* mediaElement = toParentMediaElement(o);
774    if (!mediaElement)
775        return false;
776
777    QSharedPointer<StylePainter> p = getStylePainter(paintInfo);
778    if (p.isNull() || !p->isValid())
779        return true;
780
781    p->painter->setRenderHint(QPainter::Antialiasing, true);
782
783    paintMediaBackground(p->painter, r);
784
785    if (MediaPlayer* player = mediaElement->player()) {
786        // Get the buffered parts of the media
787        RefPtr<TimeRanges> buffered = player->buffered();
788        if (buffered->length() > 0 && player->duration() < std::numeric_limits<float>::infinity()) {
789            // Set the transform and brush
790            WorldMatrixTransformer transformer(p->painter, o, r);
791            p->painter->setBrush(getMediaControlForegroundColor());
792
793            // Paint each buffered section
794            for (int i = 0; i < buffered->length(); i++) {
795                float startX = (buffered->start(i, IGNORE_EXCEPTION) / player->duration()) * 100;
796                float width = ((buffered->end(i, IGNORE_EXCEPTION) / player->duration()) * 100) - startX;
797                p->painter->drawRect(startX, 37, width, 26);
798            }
799        }
800    }
801
802    return false;
803}
804
805bool RenderThemeQt::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
806{
807    ASSERT(o->node());
808    Node* hostNode = o->node()->shadowHost();
809    if (!hostNode)
810        hostNode = o->node();
811    HTMLMediaElement* mediaElement = toParentMediaElement(hostNode);
812    if (!mediaElement)
813        return false;
814
815    QSharedPointer<StylePainter> p = getStylePainter(paintInfo);
816    if (p.isNull() || !p->isValid())
817        return true;
818
819    p->painter->setRenderHint(QPainter::Antialiasing, true);
820
821    p->painter->setPen(Qt::NoPen);
822    p->painter->setBrush(getMediaControlForegroundColor(hostNode->renderer()));
823    p->painter->drawRect(r.x(), r.y(), r.width(), r.height());
824
825    return false;
826}
827#endif
828
829void RenderThemeQt::adjustSliderThumbSize(RenderStyle* style, Element*) const
830{
831    // timelineThumbHeight should match the height property of -webkit-media-controls-timeline in mediaControlsQt.css.
832    const int timelineThumbHeight = 12;
833    const int timelineThumbWidth = timelineThumbHeight / 3;
834    // volumeThumbWidth should match the width property of -webkit-media-controls-volume-slider in mediaControlsQt.css.
835    const int volumeThumbWidth = 12;
836    const int volumeThumbHeight = volumeThumbWidth / 3;
837    ControlPart part = style->appearance();
838
839    if (part == MediaSliderThumbPart) {
840        style->setWidth(Length(timelineThumbWidth, Fixed));
841        style->setHeight(Length(timelineThumbHeight, Fixed));
842    } else if (part == MediaVolumeSliderThumbPart) {
843        style->setHeight(Length(volumeThumbHeight, Fixed));
844        style->setWidth(Length(volumeThumbWidth, Fixed));
845    }
846}
847
848double RenderThemeQt::caretBlinkInterval() const
849{
850    return static_cast<QGuiApplication*>(qApp)->styleHints()->cursorFlashTime() / 1000.0 / 2.0;
851}
852
853String RenderThemeQt::fileListNameForWidth(const FileList* fileList, const Font& font, int width, bool multipleFilesAllowed) const
854{
855    UNUSED_PARAM(multipleFilesAllowed);
856    if (width <= 0)
857        return String();
858
859    String string;
860    if (fileList->isEmpty())
861        string = fileButtonNoFileSelectedLabel();
862    else if (fileList->length() == 1) {
863        String fname = fileList->item(0)->path();
864        QFontMetrics fm(font.syntheticFont());
865        string = fm.elidedText(fname, Qt::ElideLeft, width);
866    } else {
867        int n = fileList->length();
868        string = QCoreApplication::translate("QWebPage", "%n file(s)",
869                                             "number of chosen file",
870                                             n);
871    }
872
873    return string;
874}
875
876StylePainter::StylePainter(RenderThemeQt* theme, const PaintInfo& paintInfo)
877    : painter(0)
878{
879    Q_UNUSED(theme);
880    ASSERT(paintInfo.context);
881    init(paintInfo.context);
882}
883
884StylePainter::StylePainter()
885    : painter(0)
886{
887}
888
889void StylePainter::init(GraphicsContext* context)
890{
891    painter = static_cast<QPainter*>(context->platformContext());
892
893    if (painter) {
894        // the styles often assume being called with a pristine painter where no brush is set,
895        // so reset it manually
896        m_previousBrush = painter->brush();
897        painter->setBrush(Qt::NoBrush);
898
899        // painting the widget with anti-aliasing will make it blurry
900        // disable it here and restore it later
901        m_previousAntialiasing = painter->testRenderHint(QPainter::Antialiasing);
902        painter->setRenderHint(QPainter::Antialiasing, false);
903    }
904}
905
906StylePainter::~StylePainter()
907{
908    if (painter) {
909        painter->setBrush(m_previousBrush);
910        painter->setRenderHints(QPainter::Antialiasing, m_previousAntialiasing);
911    }
912}
913
914}
915
916// vim: ts=4 sw=4 et
917