1/*
2 * This file is part of the WebKit project.
3 *
4 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24#include "RenderThemeQtMobile.h"
25
26#include "CSSValueKeywords.h"
27#include "Color.h"
28#include "Document.h"
29#include "Font.h"
30#include "HTMLInputElement.h"
31#include "HTMLNames.h"
32#include "HTMLSelectElement.h"
33#include "LocalizedStrings.h"
34#include "NotImplemented.h"
35#include "Page.h"
36#include "PaintInfo.h"
37#include "QWebPageClient.h"
38#include "RenderBox.h"
39#if ENABLE(PROGRESS_ELEMENT)
40#include "RenderProgress.h"
41#endif
42#include "StyleResolver.h"
43
44#include <wtf/PassRefPtr.h>
45
46#include <QColor>
47#include <QFile>
48#include <QPainter>
49#include <QPixmapCache>
50
51namespace WebCore {
52
53using namespace HTMLNames;
54
55// Constants used by the mobile theme
56static const int arrowBoxWidth = 26;
57static const int frameWidth = 2;
58static const int checkBoxWidth = 21;
59static const int radioWidth = 21;
60static const int sliderSize = 20;
61static const int buttonHeightRatio = 1.5;
62
63static const float multipleComboDotsOffsetFactor = 1.8;
64static const float buttonPaddingLeft = 18;
65static const float buttonPaddingRight = 18;
66static const float buttonPaddingTop = 2;
67static const float buttonPaddingBottom = 3;
68static const float menuListPadding = 9;
69static const float textFieldPadding = 10;
70static const float radiusFactor = 0.36;
71static const float progressBarChunkPercentage = 0.2;
72#if ENABLE(PROGRESS_ELEMENT)
73static const int progressAnimationGranularity = 2;
74#endif
75static const float sliderGrooveBorderRatio = 0.2;
76static const QColor darkColor(40, 40, 40);
77static const QColor highlightColor(16, 128, 221);
78static const QColor buttonGradientBottom(245, 245, 245);
79static const QColor shadowColor(80, 80, 80, 160);
80
81static QHash<KeyIdentifier, CacheKey> cacheKeys;
82
83static qreal painterScale(QPainter* painter)
84{
85    if (!painter)
86        return 1;
87
88    const QTransform& transform = painter->transform();
89    qreal scale = 1;
90
91    if (transform.type() == QTransform::TxScale)
92        scale = qAbs(transform.m11());
93    else if (transform.type() >= QTransform::TxRotate) {
94        const QLineF l1(0, 0, 1, 0);
95        const QLineF l2 = transform.map(l1);
96        scale = qAbs(l2.length() / l1.length());
97    }
98    return scale;
99}
100
101uint qHash(const KeyIdentifier& id)
102{
103    const quint32 value = id.trait1 + (id.trait2 << 1) + (uint(id.type) << 2) + (id.height << 5) + (id.width << 14) + (id.trait3 << 25);
104    const unsigned char* p = reinterpret_cast<const unsigned char*>(&value);
105    uint hash = 0;
106    for (int i = 0; i < 4; ++i)
107        hash ^= (hash << 5) + (hash >> 2) + p[i];
108    return hash;
109}
110
111/*
112 * The octants' indices are identified below, for each point (x,y)
113 * in the first octant, we can populate the 7 others with the corresponding
114 * point.
115 *
116 *                                       index |   xpos   |   ypos
117 *                xd                    ---------------------------
118 *      4      |<--->| 3                    0  |  xd + x  |    y
119 *     __________________                   1  |  xd + y  |    x
120 *    /                  \                  2  |  xd + y  |   -x
121 * 5 |         .(c)       |  2              3  |  xd + x  |   -y
122 * 6 |                    |  1              4  | -xd - x  |   -y
123 *    \__________________/                  5  | -xd - y  |   -x
124 *                                          6  | -xd - y  |    x
125 *      7              0                    7  | -xd - x  |    y
126 *
127 **/
128
129static void addPointToOctants(QVector<QPainterPath>& octants, const QPointF& center, qreal x, qreal y , int xDelta = 0)
130{
131    ASSERT(octants.count() == 8);
132
133    for (short i = 0; i < 8; ++i) {
134        QPainterPath& octant = octants[i];
135        QPointF pos(center);
136        // The Gray code corresponding to the octant's index helps doing the math in a more generic way.
137        const short gray = (i >> 1) ^ i;
138        const qreal xOffset = xDelta + ((gray & 1) ? y : x);
139        pos.ry() += ((gray & 2)? -1 : 1) * ((gray & 1) ? x : y);
140        pos.rx() += (i < 4) ? xOffset : -xOffset;
141
142        if (octant.elementCount())
143            octant.lineTo(pos);
144        else // The path is empty. Initialize the start point.
145            octant.moveTo(pos);
146    }
147}
148
149static void drawControlBackground(QPainter* painter, const QPen& pen, const QRect& rect, const QBrush& brush)
150{
151    QPen oldPen = painter->pen();
152    QBrush oldBrush = painter->brush();
153    painter->setRenderHint(QPainter::Antialiasing, true);
154    painter->setPen(pen);
155    painter->setBrush(brush);
156
157    static const qreal line = 1.5;
158    const QRectF paddedRect = rect.adjusted(line, line, -line, -line);
159
160    static const int n = 3;
161    const qreal invPow = 1 / double(n);
162    ASSERT(paddedRect.width() >= paddedRect.height());
163    const int radius = paddedRect.height() / 2;
164    const int xDelta = paddedRect.width() / 2 - radius;
165    const QPointF center = paddedRect.center();
166    qreal x = 0;
167    qreal y;
168    QVector<QPainterPath> octants(8);
169    // Stay within reasonable distance from edge values, which can cause artifacts at certain zoom levels.
170    static const float epsilon = 0.02;
171    for (y = radius - epsilon; y - epsilon > x; y -= 0.5) {
172        x = radius * pow(1 - pow(qAbs(y) / radius , n), invPow);
173        addPointToOctants(octants, center, x, y, xDelta);
174    }
175
176    QPainterPath path = octants.first();
177    for (int i = 1; i < 8; ++i) {
178        // Due to the orientation of the arcs, we need to reverse the paths with odd indices.
179        QPainterPath subPath = (i % 2) ?  octants.at(i).toReversed() : octants.at(i);
180        path.connectPath(subPath);
181    }
182    path.closeSubpath();
183
184    painter->drawPath(path);
185    painter->setPen(oldPen);
186    painter->setBrush(oldBrush);
187}
188
189static inline QRect shrinkRectToSquare(const QRect& rect)
190{
191    const int side = qMin(rect.height(), rect.width());
192    return QRect(rect.topLeft(), QSize(side, side));
193}
194
195static inline QPen borderPen(QPainter* painter = 0)
196{
197    return QPen(darkColor, qMin(1.0, 0.4 * painterScale(painter)));
198}
199
200QSharedPointer<StylePainter> RenderThemeQtMobile::getStylePainter(const PaintInfo& pi)
201{
202    return QSharedPointer<StylePainter>(new StylePainterMobile(this, pi));
203}
204
205QPalette RenderThemeQtMobile::colorPalette() const
206{
207    static QPalette lightGrayPalette(Qt::lightGray);
208    return lightGrayPalette;
209}
210
211StylePainterMobile::StylePainterMobile(RenderThemeQtMobile* theme, const PaintInfo& paintInfo)
212    : StylePainter(theme, paintInfo)
213{
214    m_previousSmoothPixmapTransform = painter->testRenderHint(QPainter::SmoothPixmapTransform);
215    if (!m_previousSmoothPixmapTransform)
216        painter->setRenderHint(QPainter::SmoothPixmapTransform);
217}
218
219StylePainterMobile::~StylePainterMobile()
220{
221    painter->setRenderHints(QPainter::SmoothPixmapTransform, m_previousSmoothPixmapTransform);
222}
223
224bool StylePainterMobile::findCachedControl(const KeyIdentifier& keyId, QPixmap* result)
225{
226    static CacheKey emptyKey;
227    CacheKey key = cacheKeys.value(keyId, emptyKey);
228    if (key == emptyKey)
229        return false;
230    const bool ret = QPixmapCache::find(key, result);
231    if (!ret)
232        cacheKeys.remove(keyId);
233    return ret;
234}
235
236void StylePainterMobile::insertIntoCache(const KeyIdentifier& keyId, const QPixmap& pixmap)
237{
238    ASSERT(keyId.type);
239    const int sizeInKiloBytes = pixmap.width() * pixmap.height() * pixmap.depth() / (8 * 1024);
240    // Don't cache pixmaps over 512 KB;
241    if (sizeInKiloBytes > 512)
242        return;
243    cacheKeys.insert(keyId, QPixmapCache::insert(pixmap));
244}
245
246void StylePainterMobile::drawCheckableBackground(QPainter* painter, const QRect& rect, bool checked, bool enabled) const
247{
248    QBrush brush;
249    QColor color = Qt::gray;
250    if (checked && enabled)
251        color = highlightColor;
252
253    QLinearGradient gradient;
254    gradient.setStart(rect.topLeft());
255    gradient.setFinalStop(rect.bottomLeft());
256    gradient.setColorAt(0.0, color);
257    gradient.setColorAt(1.0, color.lighter(130));
258    brush = gradient;
259
260    drawControlBackground(painter, borderPen(painter), rect, brush);
261}
262
263QSize StylePainterMobile::sizeForPainterScale(const QRect& rect) const
264{
265    qreal scale = painterScale(painter);
266    QTransform scaleTransform = QTransform::fromScale(scale, scale);
267
268    return scaleTransform.mapRect(rect).size();
269}
270
271void StylePainterMobile::drawChecker(QPainter* painter, const QRect& rect, const QColor& color) const
272{
273    painter->setRenderHint(QPainter::Antialiasing, true);
274    QPen pen(Qt::darkGray);
275    pen.setCosmetic(true);
276    painter->setPen(pen);
277    painter->scale(rect.width(), rect.height());
278    QPainterPath path;
279    path.moveTo(0.18, 0.47);
280    path.lineTo(0.25, 0.4);
281    path.lineTo(0.4, 0.55);
282    path.quadTo(0.64, 0.29, 0.78, 0.2);
283    path.lineTo(0.8, 0.25);
284    path.quadTo(0.53, 0.55, 0.45, 0.75);
285    path.closeSubpath();
286    painter->setBrush(color);
287    painter->drawPath(path);
288}
289
290QPixmap StylePainterMobile::findCheckBox(const QSize& size, bool checked, bool enabled) const
291{
292    ASSERT(size.width() == size.height());
293    QPixmap result;
294    KeyIdentifier id;
295    id.type = KeyIdentifier::CheckBox;
296    id.height = size.height();
297    id.trait1 = enabled;
298    id.trait2 = checked;
299    if (!findCachedControl(id, &result)) {
300        result = QPixmap(size);
301        result.fill(Qt::transparent);
302        QPainter cachePainter(&result);
303        QRect rect(QPoint(0, 0), size);
304        drawCheckableBackground(&cachePainter, rect, checked, enabled);
305        if (checked || !enabled)
306            drawChecker(&cachePainter, rect, enabled ? Qt::white : Qt::gray);
307        insertIntoCache(id, result);
308    }
309    return result;
310}
311
312void StylePainterMobile::drawRadio(QPainter* painter, const QSize& size, bool checked, bool enabled) const
313{
314    QRect rect(QPoint(0, 0), size);
315
316    drawCheckableBackground(painter, rect, checked, enabled);
317    const int border = size.width() / 4;
318    rect.adjust(border, border, -border, -border);
319    drawControlBackground(painter, borderPen(), rect, enabled ? Qt::white : Qt::gray);
320}
321
322QPixmap StylePainterMobile::findRadio(const QSize& size, bool checked, bool enabled) const
323{
324    ASSERT(size.width() == size.height());
325    QPixmap result;
326    KeyIdentifier id;
327    id.type = KeyIdentifier::Radio;
328    id.height = size.height();
329    id.trait1 = enabled;
330    id.trait2 = checked;
331    if (!findCachedControl(id, &result)) {
332        result = QPixmap(size);
333        result.fill(Qt::transparent);
334        QPainter cachePainter(&result);
335        drawRadio(&cachePainter, size, checked, enabled);
336        insertIntoCache(id, result);
337    }
338    return result;
339}
340
341void StylePainterMobile::drawMultipleComboButton(QPainter* painter, const QSizeF& size, const QColor& color) const
342{
343    const qreal dotDiameter = size.height();
344    const qreal dotRadii = dotDiameter / 2;
345
346    painter->setRenderHint(QPainter::Antialiasing, true);
347    painter->setPen(color);
348    painter->setBrush(color);
349
350    for (int i = 0; i < 3; ++i) {
351        QPointF center(dotRadii + i * multipleComboDotsOffsetFactor * dotDiameter, dotRadii);
352        painter->drawEllipse(center, dotRadii, dotRadii);
353    }
354}
355
356void StylePainterMobile::drawSimpleComboButton(QPainter* painter, const QSizeF& size, const QColor& color) const
357{
358    const qreal gap = size.height() / 5.0;
359    const qreal arrowHeight = (size.height() - gap) / 2.0;
360    const qreal right = arrowHeight * 2;
361    const qreal bottomBaseline = size.height() - arrowHeight;
362    QPolygonF upArrow, downArrow;
363    upArrow << QPointF(0, arrowHeight) << QPointF(arrowHeight, 0) << QPointF(right, arrowHeight);
364    downArrow << QPointF(0, bottomBaseline) << QPointF(arrowHeight, bottomBaseline + arrowHeight)
365              << QPointF(right, bottomBaseline);
366
367    painter->setPen(Qt::NoPen);
368    painter->setBrush(color);
369    painter->drawPolygon(upArrow);
370    painter->drawPolygon(downArrow);
371}
372
373QSizeF StylePainterMobile::getButtonImageSize(int buttonHeight, bool multiple) const
374{
375    if (multiple)
376        return QSizeF(qreal(2 + buttonHeight * 3 * multipleComboDotsOffsetFactor/ 10.0)
377                      , qreal(2 + buttonHeight / 10.0));
378
379    const qreal height = buttonHeight / 2.5;
380    const qreal width = 4 * height / 5.0;
381    return QSizeF(2 + width, 2 + height);
382}
383
384QPixmap StylePainterMobile::findComboButton(const QSize& size, bool multiple, bool enabled) const
385{
386    if (size.isNull())
387        return QPixmap();
388    QPixmap result;
389    KeyIdentifier id;
390    id.type = KeyIdentifier::ComboButton;
391    id.width = size.width();
392    id.height = size.height();
393    id.trait1 = multiple;
394    id.trait2 = enabled;
395
396    if (!findCachedControl(id, &result)) {
397        result = QPixmap(size);
398        const qreal border = painterScale(painter);
399        const QSizeF padding(2 * border, 2 * border);
400        const QSizeF innerSize = size - padding;
401        ASSERT(innerSize.isValid());
402        result.fill(Qt::transparent);
403        QPainter cachePainter(&result);
404        cachePainter.translate(border, border);
405        if (multiple)
406            drawMultipleComboButton(&cachePainter, innerSize, enabled ? darkColor : Qt::lightGray);
407        else
408            drawSimpleComboButton(&cachePainter, innerSize, enabled ? darkColor : Qt::lightGray);
409        insertIntoCache(id, result);
410    }
411    return result;
412}
413
414void StylePainterMobile::drawLineEdit(const QRect& rect, bool focused, bool enabled)
415{
416    Q_UNUSED(enabled);
417    QPixmap lineEdit = findLineEdit(sizeForPainterScale(rect), focused);
418    if (lineEdit.isNull())
419        return;
420    painter->drawPixmap(rect, lineEdit);
421}
422
423QPixmap StylePainterMobile::findLineEdit(const QSize & size, bool focused) const
424{
425    QPixmap result;
426    KeyIdentifier id;
427    id.type = KeyIdentifier::LineEdit;
428    id.width = size.width();
429    id.height = size.height();
430    id.trait1 = focused;
431
432    if (!findCachedControl(id, &result)) {
433        const int focusFrame = painterScale(painter);
434        result = QPixmap(size);
435        result.fill(Qt::transparent);
436        const QRect rect = result.rect().adjusted(focusFrame, focusFrame, -focusFrame, -focusFrame);
437        QPainter cachePainter(&result);
438        drawControlBackground(&cachePainter, borderPen(painter), rect, Qt::white);
439
440        if (focused) {
441            QPen focusPen(highlightColor, 1.2 * painterScale(painter), Qt::SolidLine);
442            drawControlBackground(&cachePainter, focusPen, rect, Qt::NoBrush);
443        }
444        insertIntoCache(id, result);
445    }
446    return result;
447}
448
449void StylePainterMobile::drawCheckBox(const QRect& rect, bool checked, bool enabled)
450{
451    const QRect square = shrinkRectToSquare(rect);
452    QPixmap checkBox = findCheckBox(sizeForPainterScale(square), checked, enabled);
453    if (checkBox.isNull())
454        return;
455    painter->drawPixmap(square, checkBox);
456}
457
458void StylePainterMobile::drawRadioButton(const QRect& rect, bool checked, bool enabled)
459{
460    const QRect square = shrinkRectToSquare(rect);
461    QPixmap radio = findRadio(sizeForPainterScale(square), checked, enabled);
462    if (radio.isNull())
463        return;
464    painter->drawPixmap(square, radio);
465}
466
467void StylePainterMobile::drawPushButton(const QRect& rect, bool sunken, bool enabled)
468{
469    QPixmap pushButton = findPushButton(sizeForPainterScale(rect), sunken, enabled);
470    if (pushButton.isNull())
471        return;
472    painter->drawPixmap(rect, pushButton);
473}
474
475QPixmap StylePainterMobile::findPushButton(const QSize& size, bool sunken, bool enabled) const
476{
477    QPixmap result;
478    KeyIdentifier id;
479    id.type = KeyIdentifier::PushButton;
480    id.width = size.width();
481    id.height = size.height();
482    id.trait1 = sunken;
483    id.trait2 = enabled;
484    if (!findCachedControl(id, &result)) {
485        const qreal dropShadowSize = painterScale(painter);
486        result = QPixmap(size);
487        result.fill(Qt::transparent);
488        const QRect rect = QRect(0, 0, size.width(), size.height() - dropShadowSize);
489        QPainter cachePainter(&result);
490        drawControlBackground(&cachePainter, Qt::NoPen, rect.adjusted(0, dropShadowSize, 0, dropShadowSize), shadowColor);
491
492        QBrush brush;
493        if (enabled && !sunken) {
494            QLinearGradient linearGradient;
495            linearGradient.setStart(rect.bottomLeft());
496            linearGradient.setFinalStop(rect.topLeft());
497            linearGradient.setColorAt(0.0, buttonGradientBottom);
498            linearGradient.setColorAt(1.0, Qt::white);
499            brush = linearGradient;
500        } else if (!enabled)
501            brush = QColor(241, 242, 243);
502        else { // sunken
503            QLinearGradient linearGradient;
504            linearGradient.setStart(rect.bottomLeft());
505            linearGradient.setFinalStop(rect.topLeft());
506            linearGradient.setColorAt(0.0, highlightColor);
507            linearGradient.setColorAt(1.0, highlightColor.lighter());
508            brush = linearGradient;
509        }
510        drawControlBackground(&cachePainter, borderPen(painter), rect, brush);
511        insertIntoCache(id, result);
512    }
513    return result;
514}
515
516void StylePainterMobile::drawComboBox(const QRect& rect, bool multiple, bool enabled)
517{
518    QPixmap pushButton = findPushButton(sizeForPainterScale(rect), /*sunken = */false, enabled);
519    if (pushButton.isNull())
520        return;
521    painter->drawPixmap(rect, pushButton);
522    QRectF targetRect(QPointF(0, 0), getButtonImageSize(rect.height() - 1, multiple));
523    const QPointF buttonCenter(rect.right() - arrowBoxWidth / 2, rect.top() + (rect.height() - 1) / 2);
524    targetRect.moveCenter(buttonCenter);
525    QPixmap pic = findComboButton(sizeForPainterScale(targetRect.toRect()), multiple, enabled);
526    if (pic.isNull())
527        return;
528
529    painter->drawPixmap(targetRect.toRect(), pic);
530}
531
532void StylePainterMobile::drawProgress(const QRect& rect, double progress, bool leftToRight, bool animated, bool vertical) const
533{
534    const int horizontalBorder = (vertical ? rect.width() / 4 : 0);
535    const int verticalBorder = (vertical ? 0 : rect.height() / 4);
536    const QRect targetRect = rect.adjusted(horizontalBorder, verticalBorder, -horizontalBorder, -verticalBorder);
537
538    QPixmap result;
539    QSize imageSize = sizeForPainterScale(targetRect);
540    if (vertical)
541        qSwap(imageSize.rheight(), imageSize.rwidth());
542    KeyIdentifier id;
543    id.type = KeyIdentifier::Progress;
544    id.width = imageSize.width();
545    id.height = imageSize.height();
546    id.trait1 = animated;
547    id.trait2 = (!animated && !leftToRight);
548    id.trait3 = progress * 100;
549    if (!findCachedControl(id, &result)) {
550        if (imageSize.isNull())
551            return;
552        result = QPixmap(imageSize);
553        result.fill(Qt::transparent);
554        QPainter painter(&result);
555        painter.setRenderHint(QPainter::Antialiasing);
556        QRect progressRect(QPoint(0, 0), imageSize);
557        qreal radius = radiusFactor * progressRect.height();
558        painter.setBrush(Qt::NoBrush);
559        painter.setPen(borderPen());
560        progressRect.adjust(1, 1, -1, -1);
561        painter.drawRoundedRect(progressRect, radius, radius);
562        progressRect.adjust(1, 1, -1, -1);
563        if (animated) {
564            const int right = progressRect.right();
565            const int startPos = right * (1 - progressBarChunkPercentage) * 2 * fabs(progress - 0.5);
566            progressRect.setWidth(progressBarChunkPercentage * right);
567            progressRect.moveLeft(startPos);
568        } else {
569            progressRect.setWidth(progress * progressRect.width());
570            if (!leftToRight)
571                progressRect.moveRight(imageSize.width() - 2);
572        }
573        if (progressRect.width() > 0) {
574            QLinearGradient gradient;
575            gradient.setStart(progressRect.bottomLeft());
576            gradient.setFinalStop(progressRect.topLeft());
577            gradient.setColorAt(0.0, highlightColor);
578            gradient.setColorAt(1.0, highlightColor.lighter());
579            painter.setBrush(gradient);
580            painter.setPen(Qt::NoPen);
581            radius = radiusFactor * progressRect.height();
582            painter.drawRoundedRect(progressRect, radius, radius);
583        }
584        insertIntoCache(id, result);
585    }
586    QTransform transform;
587    transform.rotate(-90);
588    painter->drawPixmap(targetRect, vertical ? result.transformed(transform) : result);
589}
590
591void StylePainterMobile::drawSliderThumb(const QRect & rect, bool pressed) const
592{
593    QPixmap result;
594    const QSize size = sizeForPainterScale(rect);
595    KeyIdentifier id;
596    id.type = KeyIdentifier::SliderThumb;
597    id.width = size.width();
598    id.height = size.height();
599    id.trait1 = pressed;
600    if (!findCachedControl(id, &result)) {
601        if (size.isNull())
602            return;
603        result = QPixmap(size);
604        result.fill(Qt::transparent);
605        QPainter cachePainter(&result);
606        drawControlBackground(&cachePainter, borderPen(painter), QRect(QPoint(0, 0), size), pressed? Qt::lightGray : buttonGradientBottom);
607        insertIntoCache(id, result);
608    }
609    painter->drawPixmap(rect, result);
610}
611
612
613PassRefPtr<RenderTheme> RenderThemeQtMobile::create(Page* page)
614{
615    return adoptRef(new RenderThemeQtMobile(page));
616}
617
618RenderThemeQtMobile::RenderThemeQtMobile(Page* page)
619    : RenderThemeQt(page)
620{
621}
622
623RenderThemeQtMobile::~RenderThemeQtMobile()
624{
625}
626
627bool RenderThemeQtMobile::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& fill, const Color& backgroundColor) const
628{
629    switch (style->appearance()) {
630    case CheckboxPart:
631    case RadioPart:
632        return false;
633    default:
634        return RenderThemeQt::isControlStyled(style, border, fill, backgroundColor);
635    }
636}
637
638int RenderThemeQtMobile::popupInternalPaddingBottom(RenderStyle* style) const
639{
640    return 1;
641}
642
643void RenderThemeQtMobile::computeSizeBasedOnStyle(RenderStyle* renderStyle) const
644{
645    QSize size(0, 0);
646
647    switch (renderStyle->appearance()) {
648    case TextAreaPart:
649    case SearchFieldPart:
650    case TextFieldPart: {
651        int padding = frameWidth;
652        renderStyle->setPaddingLeft(Length(padding, Fixed));
653        renderStyle->setPaddingRight(Length(padding, Fixed));
654        renderStyle->setPaddingTop(Length(padding, Fixed));
655        renderStyle->setPaddingBottom(Length(padding, Fixed));
656        break;
657    }
658    default:
659        break;
660    }
661    // If the width and height are both specified, then we have nothing to do.
662    if (!renderStyle->width().isIntrinsicOrAuto() && !renderStyle->height().isAuto())
663        return;
664
665    switch (renderStyle->appearance()) {
666    case CheckboxPart: {
667        const int w = checkBoxWidth * renderStyle->effectiveZoom();
668        size = QSize(w, w);
669        break;
670    }
671    case RadioPart: {
672        const int w = radioWidth * renderStyle->effectiveZoom();
673        size = QSize(w, w);
674        break;
675    }
676    case PushButtonPart:
677    case SquareButtonPart:
678    case DefaultButtonPart:
679    case ButtonPart:
680    case MenulistPart: {
681        const int height = renderStyle->fontMetrics().height() * buttonHeightRatio * renderStyle->effectiveZoom();
682        size = QSize(renderStyle->width().value(), height);
683        break;
684    }
685    default:
686        break;
687    }
688
689    // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
690    if (renderStyle->width().isIntrinsicOrAuto() && size.width() > 0)
691        renderStyle->setMinWidth(Length(size.width(), Fixed));
692    if (renderStyle->height().isAuto() && size.height() > 0)
693        renderStyle->setMinHeight(Length(size.height(), Fixed));
694}
695
696void RenderThemeQtMobile::adjustButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
697{
698    // Ditch the border.
699    style->resetBorder();
700
701    FontDescription fontDescription = style->fontDescription();
702    fontDescription.setIsAbsoluteSize(true);
703
704    fontDescription.setSpecifiedSize(style->fontSize());
705    fontDescription.setComputedSize(style->fontSize());
706
707    style->setLineHeight(RenderStyle::initialLineHeight());
708    setButtonSize(style);
709    setButtonPadding(style);
710}
711
712void RenderThemeQtMobile::setButtonPadding(RenderStyle* style) const
713{
714    if (!style)
715        return;
716    style->setPaddingLeft(Length(buttonPaddingLeft, Fixed));
717    style->setPaddingRight(Length(buttonPaddingRight, Fixed));
718    style->setPaddingTop(Length(buttonPaddingTop, Fixed));
719    style->setPaddingBottom(Length(buttonPaddingBottom, Fixed));
720}
721
722bool RenderThemeQtMobile::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
723{
724    StylePainterMobile p(this, i);
725    if (!p.isValid())
726       return true;
727
728    ControlPart appearance = o->style()->appearance();
729    if (appearance == PushButtonPart || appearance == ButtonPart) {
730        p.drawPushButton(r, isPressed(o), isEnabled(o));
731    } else if (appearance == RadioPart)
732       p.drawRadioButton(r, isChecked(o), isEnabled(o));
733    else if (appearance == CheckboxPart)
734       p.drawCheckBox(r, isChecked(o), isEnabled(o));
735
736    return false;
737}
738
739void RenderThemeQtMobile::adjustTextFieldStyle(StyleResolver*, RenderStyle* style, Element*) const
740{
741    // Resetting the style like this leads to differences like:
742    // - RenderTextControl {INPUT} at (2,2) size 168x25 [bgcolor=#FFFFFF] border: (2px inset #000000)]
743    // + RenderTextControl {INPUT} at (2,2) size 166x26
744    // in layout tests when a CSS style is applied that doesn't affect background color, border or
745    // padding. Just worth keeping in mind!
746    style->setBackgroundColor(Color::transparent);
747    style->resetBorder();
748    style->setBorderTopWidth(frameWidth);
749    style->setBorderRightWidth(frameWidth);
750    style->setBorderBottomWidth(frameWidth);
751    style->setBorderLeftWidth(frameWidth);
752    style->resetPadding();
753    computeSizeBasedOnStyle(style);
754    style->setPaddingLeft(Length(textFieldPadding, Fixed));
755    style->setPaddingRight(Length(textFieldPadding, Fixed));
756}
757
758bool RenderThemeQtMobile::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r)
759{
760    StylePainterMobile p(this, i);
761    if (!p.isValid())
762        return true;
763
764    ControlPart appearance = o->style()->appearance();
765    if (appearance != TextFieldPart
766        && appearance != SearchFieldPart
767        && appearance != TextAreaPart)
768        return true;
769
770    // Now paint the text field.
771    if (appearance == TextAreaPart) {
772        const bool previousAntialiasing = p.painter->testRenderHint(QPainter::Antialiasing);
773        p.painter->setRenderHint(QPainter::Antialiasing);
774        p.painter->setPen(borderPen());
775        p.painter->setBrush(Qt::white);
776        const int radius = checkBoxWidth * radiusFactor;
777        p.painter->drawRoundedRect(r, radius, radius);
778
779        if (isFocused(o)) {
780            QPen focusPen(highlightColor, 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
781            p.painter->setPen(focusPen);
782            p.painter->setBrush(Qt::NoBrush);
783            p.painter->drawRoundedRect(r, radius, radius);
784        }
785        p.painter->setRenderHint(QPainter::Antialiasing, previousAntialiasing);
786    } else
787        p.drawLineEdit(r, isFocused(o), isEnabled(o));
788    return false;
789}
790
791void RenderThemeQtMobile::adjustMenuListStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
792{
793    RenderThemeQt::adjustMenuListStyle(styleResolver, style, e);
794    style->setPaddingLeft(Length(menuListPadding, Fixed));
795}
796
797void RenderThemeQtMobile::setPopupPadding(RenderStyle* style) const
798{
799    const int paddingLeft = 4;
800    const int paddingRight = style->width().isFixed() || style->width().isPercent() ? 5 : 8;
801
802    style->setPaddingLeft(Length(paddingLeft, Fixed));
803    style->setPaddingRight(Length(paddingRight + arrowBoxWidth, Fixed));
804
805    style->setPaddingTop(Length(2, Fixed));
806    style->setPaddingBottom(Length(2, Fixed));
807}
808
809bool RenderThemeQtMobile::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r)
810{
811    StylePainterMobile p(this, i);
812    if (!p.isValid())
813        return true;
814
815    p.drawComboBox(r, checkMultiple(o), isEnabled(o));
816    return false;
817}
818
819bool RenderThemeQtMobile::paintMenuListButton(RenderObject* o, const PaintInfo& i,
820                                        const IntRect& r)
821{
822    StylePainterMobile p(this, i);
823    if (!p.isValid())
824        return true;
825
826    p.drawComboBox(r, checkMultiple(o), isEnabled(o));
827
828    return false;
829}
830
831#if ENABLE(PROGRESS_ELEMENT)
832double RenderThemeQtMobile::animationDurationForProgressBar(RenderProgress* renderProgress) const
833{
834    if (renderProgress->isDeterminate())
835        return 0;
836    // Our animation goes back and forth so we need to make it last twice as long
837    // and we need the numerator to be an odd number to ensure we get a progress value of 0.5.
838    return (2 * progressAnimationGranularity +1) / progressBarChunkPercentage * animationRepeatIntervalForProgressBar(renderProgress);
839}
840
841bool RenderThemeQtMobile::paintProgressBar(RenderObject* o, const PaintInfo& pi, const IntRect& r)
842{
843    if (!o->isProgress())
844        return true;
845
846    StylePainterMobile p(this, pi);
847    if (!p.isValid())
848        return true;
849
850    RenderProgress* renderProgress = toRenderProgress(o);
851    const bool isRTL = (renderProgress->style()->direction() == RTL);
852
853    if (renderProgress->isDeterminate())
854        p.drawProgress(r, renderProgress->position(), !isRTL);
855    else
856        p.drawProgress(r, renderProgress->animationProgress(), !isRTL, true);
857
858    return false;
859}
860#endif
861
862bool RenderThemeQtMobile::paintSliderTrack(RenderObject* o, const PaintInfo& pi,
863                                     const IntRect& r)
864{
865    StylePainterMobile p(this, pi);
866    if (!p.isValid())
867        return true;
868
869    HTMLInputElement* slider = static_cast<HTMLInputElement*>(o->node());
870
871    const double min = slider->minimum();
872    const double max = slider->maximum();
873    const double progress = (max - min > 0) ? (slider->valueAsNumber() - min) / (max - min) : 0;
874
875    QRect rect(r);
876    const bool vertical = (o->style()->appearance() == SliderVerticalPart);
877    const int groovePadding = vertical ? r.width() * sliderGrooveBorderRatio : r.height() * sliderGrooveBorderRatio;
878    if (vertical) {
879        rect.adjust(groovePadding, 0, -groovePadding, 0);
880        // Direction is ignored on vertical sliders and we assume LTR.
881        p.drawProgress(rect, progress, true, /*animated = */ false, vertical);
882    } else {
883        rect.adjust(0, groovePadding, 0, -groovePadding);
884        p.drawProgress(rect, progress, o->style()->isLeftToRightDirection(), /*animated = */ false, vertical);
885    }
886
887    return false;
888}
889
890bool RenderThemeQtMobile::paintSliderThumb(RenderObject* o, const PaintInfo& pi,
891                                     const IntRect& r)
892{
893    StylePainterMobile p(this, pi);
894    if (!p.isValid())
895        return true;
896
897    p.drawSliderThumb(r, isPressed(o));
898
899    return false;
900}
901
902bool RenderThemeQtMobile::checkMultiple(RenderObject* o) const
903{
904    HTMLSelectElement* select = o ? toHTMLSelectElement(o->node()) : 0;
905    return select ? select->multiple() : false;
906}
907
908void RenderThemeQtMobile::adjustSliderThumbSize(RenderStyle* style, Element* element) const
909{
910    const ControlPart part = style->appearance();
911    if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
912        const int size = sliderSize * style->effectiveZoom();
913        style->setWidth(Length(size, Fixed));
914        style->setHeight(Length(size, Fixed));
915    } else
916        RenderThemeQt::adjustSliderThumbSize(style, element);
917}
918
919}
920
921// vim: ts=4 sw=4 et
922