1/*
2 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20#include "config.h"
21#include "QtMobileWebStyle.h"
22
23#include "QtStyleOptionWebComboBox.h"
24
25#include <QPainter>
26#include <QPixmapCache>
27#include <QStyleOption>
28
29QtMobileWebStyle::QtMobileWebStyle()
30{
31}
32
33static inline void drawRectangularControlBackground(QPainter* painter, const QPen& pen, const QRect& rect, const QBrush& brush)
34{
35    QPen oldPen = painter->pen();
36    QBrush oldBrush = painter->brush();
37    painter->setRenderHint(QPainter::Antialiasing, true);
38    painter->setPen(pen);
39    painter->setBrush(brush);
40
41    int line = 1;
42    painter->drawRoundedRect(rect.adjusted(line, line, -line, -line),
43            /* xRadius */ 5.0, /* yRadious */ 5.0);
44    painter->setPen(oldPen);
45    painter->setBrush(oldBrush);
46}
47
48void QtMobileWebStyle::drawChecker(QPainter* painter, int size, QColor color) const
49{
50    int border = qMin(qMax(1, int(0.2 * size)), 6);
51    int checkerSize = qMax(size - 2 * border, 3);
52    int width = checkerSize / 3;
53    int middle = qMax(3 * checkerSize / 7, 3);
54    int x = ((size - checkerSize) >> 1);
55    int y = ((size - checkerSize) >> 1) + (checkerSize - width - middle);
56    QVector<QLineF> lines(checkerSize + 1);
57    painter->setPen(color);
58    for (int i = 0; i < middle; ++i) {
59        lines[i] = QLineF(x, y, x, y + width);
60        ++x;
61        ++y;
62    }
63    for (int i = middle; i <= checkerSize; ++i) {
64        lines[i] = QLineF(x, y, x, y + width);
65        ++x;
66        --y;
67    }
68    painter->drawLines(lines.constData(), lines.size());
69}
70
71QPixmap QtMobileWebStyle::findChecker(const QRect& rect, bool disabled) const
72{
73    int size = qMin(rect.width(), rect.height());
74    QPixmap result;
75    static const QString prefix = QLatin1String("$qt-maemo5-") + QLatin1String(metaObject()->className())+ QLatin1String("-checker-");
76    QString key = prefix + QString::number(size) + QLatin1String("-") + (disabled ? QLatin1String("disabled") : QLatin1String("enabled"));
77    if (!QPixmapCache::find(key, result)) {
78        result = QPixmap(size, size);
79        result.fill(Qt::transparent);
80        QPainter painter(&result);
81        drawChecker(&painter, size, disabled ? Qt::lightGray : Qt::darkGray);
82        QPixmapCache::insert(key, result);
83    }
84    return result;
85}
86
87void QtMobileWebStyle::drawRadio(QPainter* painter, const QSize& size, bool checked, QColor color) const
88{
89    painter->setRenderHint(QPainter::Antialiasing, true);
90
91    // get minor size to do not paint a wide elipse
92    qreal squareSize = qMin(size.width(), size.height());
93    // deflate one pixel
94    QRect rect = QRect(QPoint(1, 1), QSize(squareSize - 2, squareSize - 2));
95    const QPoint centerGradient(rect.bottomRight() * 0.7);
96
97    QRadialGradient radialGradient(centerGradient, centerGradient.x() - 1);
98    radialGradient.setColorAt(0.0, Qt::white);
99    radialGradient.setColorAt(0.6, Qt::white);
100    radialGradient.setColorAt(1.0, color);
101
102    painter->setPen(color);
103    painter->setBrush(color);
104    painter->drawEllipse(rect);
105    painter->setBrush(radialGradient);
106    painter->drawEllipse(rect);
107
108    int border = 0.1 * (rect.width() + rect.height());
109    border = qMin(qMax(2, border), 10);
110    rect.adjust(border, border, -border, -border);
111    if (checked) {
112        painter->setPen(Qt::NoPen);
113        painter->setBrush(color);
114        painter->drawEllipse(rect);
115    }
116}
117
118QPixmap QtMobileWebStyle::findRadio(const QSize& size, bool checked, bool disabled) const
119{
120    QPixmap result;
121    static const QString prefix = QLatin1String("$qt-maemo5-") + QLatin1String(metaObject()->className()) + QLatin1String("-radio-");
122    QString key = prefix + QString::number(size.width()) + QLatin1String("-") + QString::number(size.height()) + QLatin1String("-")
123        + (disabled ? QLatin1String("disabled") : QLatin1String("enabled")) + (checked ? QLatin1String("-checked") : QLatin1String(""));
124    if (!QPixmapCache::find(key, result)) {
125        result = QPixmap(size);
126        result.fill(Qt::transparent);
127        QPainter painter(&result);
128        drawRadio(&painter, size, checked, disabled ? Qt::lightGray : Qt::darkGray);
129        QPixmapCache::insert(key, result);
130    }
131    return result;
132}
133
134void QtMobileWebStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const
135{
136    switch (element) {
137    case CE_CheckBox: {
138        QRect rect = option->rect;
139        const bool disabled = !(option->state & State_Enabled);
140
141        QLinearGradient linearGradient(rect.topLeft(), rect.bottomLeft());
142        if (disabled) {
143            linearGradient.setColorAt(0.0, Qt::lightGray);
144            linearGradient.setColorAt(0.5, Qt::white);
145        } else {
146            linearGradient.setColorAt(0.0, Qt::darkGray);
147            linearGradient.setColorAt(0.5, Qt::white);
148        }
149
150        painter->setPen(QPen(disabled ? Qt::lightGray : Qt::darkGray));
151        painter->setBrush(linearGradient);
152        painter->drawRect(rect);
153        rect.adjust(1, 1, -1, -1);
154
155        if (option->state & State_Off)
156            break;
157
158        QPixmap checker = findChecker(rect, disabled);
159        if (checker.isNull())
160            break;
161
162        int x = (rect.width() - checker.width()) >> 1;
163        int y = (rect.height() - checker.height()) >> 1;
164        painter->drawPixmap(rect.x() + x, rect.y() + y, checker);
165        break;
166    }
167    case CE_RadioButton: {
168        const bool disabled = !(option->state & State_Enabled);
169        QPixmap radio = findRadio(option->rect.size(), option->state & State_On, disabled);
170        if (radio.isNull())
171            break;
172        painter->drawPixmap(option->rect.x(), option->rect.y(), radio);
173        break;
174    }
175    case CE_PushButton: {
176        const bool disabled = !(option->state & State_Enabled);
177        QRect rect = option->rect;
178        QPen pen(Qt::darkGray, 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
179        painter->setPen(pen);
180
181        const bool sunken = (option->state & State_Sunken);
182        if (sunken) {
183            drawRectangularControlBackground(painter, pen, rect, QBrush(Qt::darkGray));
184            break;
185        }
186
187        QLinearGradient linearGradient;
188        if (disabled) {
189            linearGradient.setStart(rect.bottomLeft());
190            linearGradient.setFinalStop(rect.topLeft());
191            linearGradient.setColorAt(0.0, Qt::gray);
192            linearGradient.setColorAt(1.0, Qt::white);
193        } else {
194            linearGradient.setStart(rect.bottomLeft());
195            linearGradient.setFinalStop(QPoint(rect.bottomLeft().x(),
196                        rect.bottomLeft().y() - /* offset limit for gradient */ 20));
197            linearGradient.setColorAt(0.0, Qt::gray);
198            linearGradient.setColorAt(0.4, Qt::white);
199        }
200
201        drawRectangularControlBackground(painter, pen, rect, linearGradient);
202        break;
203    }
204    default:
205        QWindowsStyle::drawControl(element, option, painter, widget);
206    }
207}
208void QtMobileWebStyle::drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const
209{
210    switch (element) {
211    case QStyle::PE_PanelLineEdit: {
212        const bool disabled = !(option->state & State_Enabled);
213        const bool sunken = (option->state & State_Sunken);
214        QRect rect = option->rect;
215        QPen pen(Qt::darkGray, 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
216        painter->setPen(pen);
217
218        if (sunken) {
219            drawRectangularControlBackground(painter, pen, rect, QBrush(Qt::darkGray));
220            break;
221        }
222
223        QLinearGradient linearGradient;
224        if (disabled) {
225            linearGradient.setStart(rect.topLeft());
226            linearGradient.setFinalStop(rect.bottomLeft());
227            linearGradient.setColorAt(0.0, Qt::lightGray);
228            linearGradient.setColorAt(1.0, Qt::white);
229        } else {
230            linearGradient.setStart(rect.topLeft());
231            linearGradient.setFinalStop(QPoint(rect.topLeft().x(),
232                        rect.topLeft().y() + /* offset limit for gradient */ 20));
233            linearGradient.setColorAt(0.0, Qt::darkGray);
234            linearGradient.setColorAt(0.35, Qt::white);
235        }
236
237        drawRectangularControlBackground(painter, pen, rect, linearGradient);
238        break;
239    }
240    default:
241        QWindowsStyle::drawPrimitive(element, option, painter, widget);
242    }
243}
244
245void QtMobileWebStyle::drawMultipleComboButton(QPainter* painter, const QSize& size, QColor color) const
246{
247    int rectWidth = size.width() - 1;
248    int width = qMax(2, rectWidth >> 3);
249    int distance = (rectWidth - 3 * width) >> 1;
250    int top = (size.height() - width) >> 1;
251
252    painter->setPen(color);
253    painter->setBrush(color);
254
255    painter->drawRect(0, top, width, width);
256    painter->drawRect(width + distance, top, width, width);
257    painter->drawRect(2 * (width + distance), top, width, width);
258}
259
260void QtMobileWebStyle::drawSimpleComboButton(QPainter* painter, const QSize& size, QColor color) const
261{
262    int width = size.width();
263    int midle = width >> 1;
264    QVector<QLine> lines(width + 1);
265
266    for (int x = 0, y = 0;  x < midle; x++, y++) {
267        lines[x] = QLine(x, y, x, y + 2);
268        lines[x + midle] = QLine(width - x - 1, y, width - x - 1, y + 2);
269    }
270    // Just to ensure the lines' intersection.
271    lines[width] = QLine(midle, midle, midle, midle + 2);
272
273    painter->setPen(color);
274    painter->setBrush(color);
275    painter->drawLines(lines);
276}
277
278QSize QtMobileWebStyle::getButtonImageSize(const QSize& buttonSize) const
279{
280    const int border = qMax(3, buttonSize.width() >> 3) << 1;
281
282    int width = buttonSize.width() - border;
283    int height = buttonSize.height() - border;
284
285    if (width < 0 || height < 0)
286        return QSize();
287
288    if (height >= (width >> 1))
289        width = width >> 1 << 1;
290    else
291        width = height << 1;
292
293    return QSize(width + 1, width);
294}
295
296QPixmap QtMobileWebStyle::findComboButton(const QSize& size, bool multiple, bool disabled) const
297{
298    QPixmap result;
299    QSize imageSize = getButtonImageSize(size);
300
301    if (imageSize.isNull())
302        return QPixmap();
303    static const QString prefix = QLatin1String("$qt-maemo5-") + QLatin1String(metaObject()->className()) + QLatin1String("-combo-");
304    QString key = prefix + (multiple ? QLatin1String("multiple-") : QLatin1String("simple-"))
305        + QString::number(imageSize.width()) + QLatin1String("-") + QString::number(imageSize.height())
306        + QLatin1String("-") + (disabled ? QLatin1String("disabled") : QLatin1String("enabled"));
307    if (!QPixmapCache::find(key, result)) {
308        result = QPixmap(imageSize);
309        result.fill(Qt::transparent);
310        QPainter painter(&result);
311        if (multiple)
312            drawMultipleComboButton(&painter, imageSize, disabled ? Qt::lightGray : Qt::darkGray);
313        else
314            drawSimpleComboButton(&painter, imageSize, disabled ? Qt::lightGray : Qt::darkGray);
315        QPixmapCache::insert(key, result);
316    }
317    return result;
318}
319
320void QtMobileWebStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const
321{
322    switch (control) {
323    case CC_ComboBox: {
324        bool multiple = false;
325        const bool disabled = !(option->state & State_Enabled);
326
327        const QStyleOptionComboBox* cmb = 0;
328        const WebCore::QtStyleOptionWebComboBox* webCombo = static_cast<const WebCore::QtStyleOptionWebComboBox*>(option);
329
330        if (webCombo) {
331            multiple = webCombo->multiple();
332            cmb = webCombo;
333        } else
334            cmb = qstyleoption_cast<const QStyleOptionComboBox*>(option);
335
336        if (!cmb) {
337            QWindowsStyle::drawComplexControl(control, option, painter, widget);
338            break;
339        }
340
341        if (!(cmb->subControls & SC_ComboBoxArrow))
342            break;
343
344        QRect rect = option->rect;
345        QPen pen(Qt::darkGray, 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
346        QLinearGradient linearGradient;
347        if (disabled) {
348            linearGradient.setStart(rect.bottomLeft());
349            linearGradient.setFinalStop(rect.topLeft());
350            linearGradient.setColorAt(0.0, Qt::gray);
351            linearGradient.setColorAt(1.0, Qt::white);
352        } else {
353            linearGradient.setStart(rect.bottomLeft());
354            linearGradient.setFinalStop(QPoint(rect.bottomLeft().x(),
355                        rect.bottomLeft().y() - /* offset limit for gradient */ 20));
356            linearGradient.setColorAt(0.0, Qt::gray);
357            linearGradient.setColorAt(0.4, Qt::white);
358        }
359
360        drawRectangularControlBackground(painter, pen, rect, linearGradient);
361
362        rect = subControlRect(CC_ComboBox, cmb, SC_ComboBoxArrow, widget);
363        QPixmap pic = findComboButton(rect.size(), multiple, disabled);
364
365        if (pic.isNull())
366            break;
367
368        int x = (rect.width() - pic.width()) >> 1;
369        int y = (rect.height() - pic.height()) >> 1;
370        painter->drawPixmap(rect.x() + x, rect.y() + y, pic);
371
372        painter->setPen(disabled ? Qt::gray : Qt::darkGray);
373        painter->drawLine(rect.left() - 2, rect.top() + 2, rect.left() - 2, rect.bottom() - 2);
374
375        break;
376    }
377    default:
378        QWindowsStyle::drawComplexControl(control, option, painter, widget);
379    }
380}
381
382