702eda4cd09c763833fce8b60e8de2f4582dfe3e
[WebKit-https.git] / Source / WebCore / platform / qt / RenderThemeQtMobile.cpp
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
51 namespace WebCore {
52
53 using namespace HTMLNames;
54
55 // Constants used by the mobile theme
56 static const int arrowBoxWidth = 26;
57 static const int frameWidth = 2;
58 static const int checkBoxWidth = 21;
59 static const int radioWidth = 21;
60 static const int sliderSize = 20;
61 static const int buttonHeightRatio = 1.5;
62
63 static const float multipleComboDotsOffsetFactor = 1.8;
64 static const float buttonPaddingLeft = 18;
65 static const float buttonPaddingRight = 18;
66 static const float buttonPaddingTop = 2;
67 static const float buttonPaddingBottom = 3;
68 static const float menuListPadding = 9;
69 static const float textFieldPadding = 10;
70 static const float radiusFactor = 0.36;
71 static const float progressBarChunkPercentage = 0.2;
72 #if ENABLE(PROGRESS_ELEMENT)
73 static const int progressAnimationGranularity = 2;
74 #endif
75 static const float sliderGrooveBorderRatio = 0.2;
76 static const QColor darkColor(40, 40, 40);
77 static const QColor highlightColor(16, 128, 221);
78 static const QColor buttonGradientBottom(245, 245, 245);
79 static const QColor shadowColor(80, 80, 80, 160);
80
81 static QHash<KeyIdentifier, CacheKey> cacheKeys;
82
83 static 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
101 uint 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
129 static 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
149 static 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
189 static 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
195 static inline QPen borderPen(QPainter* painter = 0)
196 {
197     return QPen(darkColor, qMin(1.0, 0.4 * painterScale(painter)));
198 }
199
200 QSharedPointer<StylePainter> RenderThemeQtMobile::getStylePainter(const PaintInfo& pi)
201 {
202     return QSharedPointer<StylePainter>(new StylePainterMobile(this, pi));
203 }
204
205 QPalette RenderThemeQtMobile::colorPalette() const
206 {
207     static QPalette lightGrayPalette(Qt::lightGray);
208     return lightGrayPalette;
209 }
210
211 StylePainterMobile::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
219 StylePainterMobile::~StylePainterMobile()
220 {
221     painter->setRenderHints(QPainter::SmoothPixmapTransform, m_previousSmoothPixmapTransform);
222 }
223
224 bool 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
236 void 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
246 void 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
263 QSize 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
271 void 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
290 QPixmap 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
312 void 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
322 QPixmap 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
341 void 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
356 void 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
373 QSizeF 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
384 QPixmap 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
414 void 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
423 QPixmap 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
449 void 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
458 void 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
467 void 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
475 QPixmap 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
516 void 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
532 void 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
591 void 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
613 PassRefPtr<RenderTheme> RenderThemeQtMobile::create(Page* page)
614 {
615     return adoptRef(new RenderThemeQtMobile(page));
616 }
617
618 RenderThemeQtMobile::RenderThemeQtMobile(Page* page)
619     : RenderThemeQt(page)
620 {
621 }
622
623 RenderThemeQtMobile::~RenderThemeQtMobile()
624 {
625 }
626
627 bool 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
638 int RenderThemeQtMobile::popupInternalPaddingBottom(RenderStyle* style) const
639 {
640     return 1;
641 }
642
643 void 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
696 void 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
712 void 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
722 bool 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
739 void 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
758 bool 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
791 void RenderThemeQtMobile::adjustMenuListStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const
792 {
793     RenderThemeQt::adjustMenuListStyle(styleResolver, style, e);
794     style->setPaddingLeft(Length(menuListPadding, Fixed));
795 }
796
797 void 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
809 bool 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
819 bool 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)
832 double 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
841 bool 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
862 bool 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
890 bool 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
902 bool RenderThemeQtMobile::checkMultiple(RenderObject* o) const
903 {
904     HTMLSelectElement* select = o ? static_cast<HTMLSelectElement*>(o->node()) : 0;
905     return select ? select->multiple() : false;
906 }
907
908 void 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