1
/****************************************************************************
2
**
3
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtGui module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** No Commercial Usage
11
** This file contains pre-release code and may not be distributed.
12
** You may use this file in accordance with the terms and conditions
13
** contained in the Technology Preview License Agreement accompanying
14
** this package.
15
**
16
** GNU Lesser General Public License Usage
17
** Alternatively, this file may be used under the terms of the GNU Lesser
18
** General Public License version 2.1 as published by the Free Software
19
** Foundation and appearing in the file LICENSE.LGPL included in the
20
** packaging of this file.  Please review the following information to
21
** ensure the GNU Lesser General Public License version 2.1 requirements
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23
**
24
** In addition, as a special exception, Nokia gives you certain additional
25
** rights.  These rights are described in the Nokia Qt LGPL Exception
26
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27
**
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
30
**
31
**
32
**
33
**
34
**
35
**
36
**
37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include <qgtkstyle.h>
43
#include <qmaemo5style.h>
44
#include <private/qmaemo5style_p.h>
45
46
#include <private/qapplication_p.h>
47
48
#include <QtCore/QLibrary>
49
#include <QtGui/QStyleOption>
50
#include <QtGui/QLineEdit>
51
#include <QtGui/QDialogButtonBox>
52
#include <QtGui/QCommandLinkButton>
53
#include <QtGui/QAbstractScrollArea>
54
#include <QtGui/QAbstractItemView>
55
#include <QtGui/QScrollBar>
56
#include <QtGui/QPlainTextEdit>
57
#include <QtGui/QTextEdit>
58
#include <QtGui/QTextBrowser>
59
#include <QtGui/QButtonGroup>
60
#include <QtGui/QPushButton>
61
#include <QtGui/QRadioButton>
62
#include <QtGui/QAbstractSpinBox>
63
#include <QtGui/QX11Info>
64
#include <QtGui/QHBoxLayout>
65
#include <QtCore/QTimer>
66
#include <QtCore/QTimeLine>
67
68
#include <QtDebug>
69
70
#include "qpixmapcache.h"
71
#undef signals // Collides with GTK stymbols
72
#include "qgtkpainter_p.h"
73
74
#include <private/qt_x11_p.h>
75
76
#if defined(Q_WS_MAEMO_5)
77
78
QT_BEGIN_NAMESPACE
79
80
Ptr_gtk_widget_show QMaemo5StylePrivate::gtk_widget_show = 0;
81
Ptr_gtk_widget_set_name QMaemo5StylePrivate::gtk_widget_set_name = 0;
82
Ptr_gtk_text_view_new QMaemo5StylePrivate::gtk_text_view_new = 0;
83
Ptr_gtk_widget_get_size_request QMaemo5StylePrivate::gtk_widget_get_size_request = 0;
84
Ptr_gtk_button_set_label QMaemo5StylePrivate::gtk_button_set_label = 0;
85
Ptr_gtk_style_lookup_color QMaemo5StylePrivate::gtk_style_lookup_color = 0;
86
Ptr_gtk_hbox_new QMaemo5StylePrivate::gtk_hbox_new = 0;
87
Ptr_gtk_box_pack_start QMaemo5StylePrivate::gtk_box_pack_start = 0;
88
Ptr_gtk_box_pack_end QMaemo5StylePrivate::gtk_box_pack_end = 0;
89
Ptr_gtk_widget_set_size_request QMaemo5StylePrivate::gtk_widget_set_size_request = 0;
90
91
Ptr_hildon_init QMaemo5StylePrivate::hildon_init = 0;
92
Ptr_hildon_entry_new QMaemo5StylePrivate::hildon_entry_new = 0;
93
Ptr_hildon_number_editor_new QMaemo5StylePrivate::hildon_number_editor_new = 0;
94
Ptr_hildon_app_menu_new QMaemo5StylePrivate::hildon_app_menu_new = 0;
95
Ptr_hildon_app_menu_add_filter QMaemo5StylePrivate::hildon_app_menu_add_filter = 0;
96
Ptr_hildon_edit_toolbar_new_with_text QMaemo5StylePrivate::hildon_edit_toolbar_new_with_text = 0;
97
Ptr_hildon_button_new QMaemo5StylePrivate::hildon_button_new = 0;
98
Ptr_hildon_check_button_new QMaemo5StylePrivate::hildon_check_button_new = 0;
99
Ptr_hildon_pannable_area_new QMaemo5StylePrivate::hildon_pannable_area_new = 0;
100
Ptr_hildon_gtk_widget_set_theme_size QMaemo5StylePrivate::hildon_gtk_widget_set_theme_size = 0;
101
Ptr_hildon_dialog_new_with_buttons QMaemo5StylePrivate::hildon_dialog_new_with_buttons = 0;
102
Ptr_hildon_note_new_information QMaemo5StylePrivate::hildon_note_new_information = 0;
103
104
Ptr_hildon_file_chooser_dialog_new QMaemo5StylePrivate::hildon_file_chooser_dialog_new = 0;
105
Ptr_hildon_file_chooser_dialog_set_extension QMaemo5StylePrivate::hildon_file_chooser_dialog_set_extension = 0;
106
Ptr_hildon_file_chooser_dialog_add_extensions_combo QMaemo5StylePrivate::hildon_file_chooser_dialog_add_extensions_combo = 0;
107
108
QMap<QAbstractScrollArea *, ScrollBarFader *> QMaemo5StylePrivate::scrollBarFaders;
109
int QMaemo5StylePrivate::scrollBarFadeDelay = 3000;         // [ms] hardcoded in hildon pannable area
110
int QMaemo5StylePrivate::scrollBarFadeDuration = 400;       // [ms] hardcoded in hildon pannable area
111
int QMaemo5StylePrivate::scrollBarFadeUpdateInterval = 100; // [ms] hardcoded in hildon pannable area
112
GtkWidget *QMaemo5StylePrivate::radioButtonLeft = 0;
113
GtkWidget *QMaemo5StylePrivate::radioButtonMiddle = 0;
114
GtkWidget *QMaemo5StylePrivate::radioButtonRight = 0;
115
116
117
/*!
118
    \internal
119
*/
120
void QMaemo5StylePrivate::resolveGtk() const
121
{
122
    QGtkStylePrivate::resolveGtk();
123
124
    QLibrary libgtk(QLS("gtk-x11-2.0"), 0, 0);
125
    QLibrary libhildon(QLS("hildon-1"), 0, 0);
126
    QLibrary libhildonfm(QLS("hildonfm"), 2, 0);
127
128
    gtk_widget_show = (Ptr_gtk_widget_show)libgtk.resolve("gtk_widget_show");
129
    gtk_widget_set_name = (Ptr_gtk_widget_set_name)libgtk.resolve("gtk_widget_set_name");
130
    gtk_text_view_new = (Ptr_gtk_text_view_new)libgtk.resolve("gtk_text_view_new");
131
    gtk_widget_get_size_request = (Ptr_gtk_widget_get_size_request)libgtk.resolve("gtk_widget_get_size_request");
132
    gtk_button_set_label = (Ptr_gtk_button_set_label)libgtk.resolve("gtk_button_set_label");
133
    gtk_style_lookup_color = (Ptr_gtk_style_lookup_color)libgtk.resolve("gtk_style_lookup_color");
134
    gtk_hbox_new = (Ptr_gtk_hbox_new)libgtk.resolve("gtk_hbox_new");
135
    gtk_box_pack_start = (Ptr_gtk_box_pack_start)libgtk.resolve("gtk_box_pack_start");
136
    gtk_box_pack_end = (Ptr_gtk_box_pack_end)libgtk.resolve("gtk_box_pack_end");
137
    gtk_widget_set_size_request = (Ptr_gtk_widget_set_size_request)libgtk.resolve("gtk_widget_set_size_request");
138
139
    hildon_init = (Ptr_hildon_init)libhildon.resolve("hildon_init");
140
    hildon_entry_new = (Ptr_hildon_entry_new)libhildon.resolve("hildon_entry_new");
141
    hildon_number_editor_new = (Ptr_hildon_number_editor_new)libhildon.resolve("hildon_number_editor_new");
142
    hildon_app_menu_new = (Ptr_hildon_app_menu_new)libhildon.resolve("hildon_app_menu_new");
143
    hildon_app_menu_add_filter = (Ptr_hildon_app_menu_add_filter)libhildon.resolve("hildon_app_menu_add_filter");
144
    hildon_edit_toolbar_new_with_text = (Ptr_hildon_edit_toolbar_new_with_text)libhildon.resolve("hildon_edit_toolbar_new_with_text");
145
    hildon_button_new = (Ptr_hildon_button_new)libhildon.resolve("hildon_button_new");
146
    hildon_check_button_new = (Ptr_hildon_check_button_new)libhildon.resolve("hildon_check_button_new");
147
    hildon_pannable_area_new = (Ptr_hildon_pannable_area_new)libhildon.resolve("hildon_pannable_area_new");
148
    hildon_gtk_widget_set_theme_size = (Ptr_hildon_gtk_widget_set_theme_size)libhildon.resolve("hildon_gtk_widget_set_theme_size");
149
    hildon_dialog_new_with_buttons = (Ptr_hildon_dialog_new_with_buttons)libhildon.resolve("hildon_dialog_new_with_buttons");
150
    hildon_note_new_information = (Ptr_hildon_note_new_information)libhildon.resolve("hildon_note_new_information");
151
152
    hildon_file_chooser_dialog_new = (Ptr_hildon_file_chooser_dialog_new)libhildonfm.resolve("hildon_file_chooser_dialog_new");
153
    hildon_file_chooser_dialog_set_extension = (Ptr_hildon_file_chooser_dialog_set_extension)libhildonfm.resolve("hildon_file_chooser_dialog_set_extension");
154
    hildon_file_chooser_dialog_add_extensions_combo = (Ptr_hildon_file_chooser_dialog_add_extensions_combo)libhildonfm.resolve("hildon_file_chooser_dialog_add_extensions_combo");
155
156
}
157
158
/*!
159
  \internal
160
  NOTE: Reverse color in fremantle themes are set only for
161
  GtkTextView widgets. GtkEntry returns a dark base color.
162
*/
163
GtkWidget* QMaemo5StylePrivate::getTextColorWidget() const
164
{
165
    return  gtkWidget("GtkTextView");
166
}
167
168
/*!
169
    \internal
170
    \reimp
171
*/
172
void QMaemo5StylePrivate::initGtkMenu() const
173
{
174
    QGtkStylePrivate::initGtkMenu();
175
176
    // --- create widgets for the context sensitive menu
177
    GtkWidget *gtkMenu = gtk_menu_new();
178
    gtk_widget_set_name(gtkMenu, "hildon-context-sensitive-menu");
179
    gtk_widget_realize(gtkMenu);
180
181
    GtkWidget *gtkMenuItem = QGtkStylePrivate::gtk_menu_item_new_with_label("X");
182
    gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuItem);
183
    gtk_widget_realize(gtkMenuItem);
184
185
    GtkWidget *gtkCheckMenuItem = QGtkStylePrivate::gtk_check_menu_item_new_with_label("X");
186
    gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkCheckMenuItem);
187
    gtk_widget_realize(gtkCheckMenuItem);
188
189
    GtkWidget *gtkMenuSeparator = QGtkStylePrivate::gtk_separator_menu_item_new();
190
    gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuSeparator);
191
192
    addAllSubWidgets(gtkMenu);
193
194
    // --- and now for the "normal" app menu
195
    GtkWidget *gtkMenu2 = hildon_app_menu_new();
196
197
    // -- three menu buttons. Sapwood will use different background images depending on their position.
198
    radioButtonLeft = gtk_radio_button_new(NULL);
199
    gtk_widget_show(radioButtonLeft); // only a visible button is really added.
200
    hildon_app_menu_add_filter( (HildonAppMenu*)gtkMenu2, (GtkButton*)radioButtonLeft);
201
202
    radioButtonMiddle = gtk_radio_button_new(NULL);
203
    gtk_widget_show(radioButtonMiddle); // only a visible button is really added.
204
    hildon_app_menu_add_filter( (HildonAppMenu*)gtkMenu2, (GtkButton*)radioButtonMiddle);
205
206
    radioButtonRight = gtk_radio_button_new(NULL);
207
    gtk_widget_show(radioButtonRight); // only a visible button is really added.
208
    hildon_app_menu_add_filter( (HildonAppMenu*)gtkMenu2, (GtkButton*)radioButtonRight);
209
210
    addAllSubWidgets(gtkMenu2);
211
212
    // and now a hack for the sapwood engine
213
    radioButtonLeft->allocation.x = 0;
214
    radioButtonLeft->allocation.y = 0;
215
    GTK_WIDGET_FLAGS(radioButtonLeft) |= GTK_MAPPED;
216
    radioButtonMiddle->allocation.x = 1;
217
    radioButtonMiddle->allocation.y = 0;
218
    GTK_WIDGET_FLAGS(radioButtonMiddle) |= GTK_MAPPED;
219
    radioButtonRight->allocation.x = 2;
220
    radioButtonRight->allocation.y = 0;
221
    GTK_WIDGET_FLAGS(radioButtonRight) |= GTK_MAPPED;
222
}
223
224
void QMaemo5StylePrivate::initGtkWidgets() const
225
{
226
    QGtkStylePrivate::initGtkWidgets();
227
228
    if (gtk_init && hildon_init) {
229
        hildon_init();
230
231
        if (!gtkWidgetMap()->contains("GtkWindow"))
232
            return; // without window all other widgets won't be created cleanly
233
234
        // ComboBoxes buttons
235
        addWidget(hildon_number_editor_new(0,1));
236
        addWidget(hildon_entry_new(HILDON_SIZE_FINGER_HEIGHT));
237
        addWidget(gtk_text_view_new());
238
        addWidget(hildon_dialog_new_with_buttons("", NULL, GTK_DIALOG_MODAL, "", 0, NULL));
239
        addWidget(hildon_pannable_area_new());
240
        GtkWidget *note = hildon_note_new_information(NULL, "");
241
        gtk_widget_set_name(note, "HildonNote-InformationTheme");
242
        addWidget(note);
243
244
        // I need a tree view inside a pannable area
245
        GtkWidget *hildonPan = gtkWidget("HildonPannableArea");
246
        GtkWidget *gtkTreeView = gtk_tree_view_new();
247
        QGtkStylePrivate::gtk_container_add((GtkContainer*)hildonPan, gtkTreeView);
248
        addWidget(gtkTreeView);
249
250
        // Gtk bug - a GtkButton without a label won't even create it's child widgets
251
        GtkWidget *button = hildon_button_new(HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
252
        gtk_button_set_label(reinterpret_cast<GtkButton *>(button), "");
253
        addWidget(button);
254
        button = hildon_check_button_new(HILDON_SIZE_FINGER_HEIGHT);
255
        gtk_button_set_label(reinterpret_cast<GtkButton*>(button), "");
256
        addWidget(button);
257
258
        addWidget(hildon_edit_toolbar_new_with_text("", ""));
259
260
        // now for a special combobox trick...
261
        for (int i = 0; i < 3; i++) {
262
            const char *names[] = { "hildon-combobox-disabled-button", "hildon-combobox-active-button", "hildon-combobox-button" };
263
264
            GtkWidget *popup_button = hildon_button_new(HILDON_SIZE_FINGER_HEIGHT, HILDON_BUTTON_ARRANGEMENT_VERTICAL);
265
            gtk_widget_set_name(popup_button, names[i]);
266
            gtk_widget_set_size_request (popup_button, 60, -1);
267
268
            GtkWidget *address_entry = hildon_entry_new(HILDON_SIZE_FINGER_HEIGHT);
269
            GtkWidget *address_container_box = gtk_hbox_new(FALSE, 0);
270
            gtk_widget_set_name(address_container_box, "hildon-combobox");
271
272
            gtk_box_pack_start((GtkBox *)address_container_box, address_entry, TRUE, TRUE, 0);
273
            gtk_box_pack_end((GtkBox *)address_container_box, popup_button, FALSE, FALSE, 0);
274
            gtk_widget_show (address_entry);
275
            gtk_widget_show (popup_button);
276
            // gtk_widget_set_sensitive(popup_button, FALSE);
277
278
            addWidget(address_container_box);
279
        }
280
    }
281
}
282
283
extern QStringList qt_make_filter_list(const QString &filter);
284
285
void QMaemo5StylePrivate::setupGtkFileChooser(GtkWidget* gtkFileChooser, QWidget *parent,
286
                                const QString &dir, const QString &filter, QString *,
287
                                QFileDialog::Options options, bool isSaveDialog,
288
                                QMap<GtkFileFilter *, QString> *)
289
{
290
    g_object_set(gtkFileChooser, "do-overwrite-confirmation", gboolean(!(options & QFileDialog::DontConfirmOverwrite)), NULL);
291
    g_object_set(gtkFileChooser, "local_only", gboolean(true), NULL);
292
    if (!filter.isEmpty()) {
293
        QStringList filters = qt_make_filter_list(filter);
294
295
        if (isSaveDialog) {
296
            const int Nfilters = filters.count();
297
            char *ext_names[Nfilters];
298
            char *extensions[Nfilters];
299
300
            for (int i = 0; i < Nfilters; i++) {
301
                const QString &rawfilter = filters[i];
302
                QString name = rawfilter.left(rawfilter.indexOf(QLatin1Char('(')));
303
                QString extension = extract_filter(rawfilter)[0].remove(QLatin1String("*."));
304
                //Doesn't make sense adding '*' extension in a save dialog;
305
                if (extension.compare(QLatin1String("*")) == 0)
306
                    qWarning("'*' is not a valid extension for a save dialog");
307
                ext_names[i]= (char*) malloc(name.count()+1);
308
                memcpy(ext_names[i], qPrintable(name), name.count());
309
                extensions[i]= (char*) malloc(extension.count()+1);
310
                memcpy(extensions[i], qPrintable(extension), extension.count());
311
            }
312
            ext_names[Nfilters] = NULL;
313
            extensions[Nfilters] = NULL;
314
315
            GtkWidget* gtkExtensionCombo;
316
            gtkExtensionCombo = hildon_file_chooser_dialog_add_extensions_combo((HildonFileChooserDialog*)(gtkFileChooser),
317
                    extensions,ext_names);
318
            //gtk_signal_connect (GTK_OBJECT(gtkExtensionCombo), "changed",
319
            //                    GTK_SIGNAL_FUNC (qt_update_file_filter), NULL);
320
        } else {
321
            //Set "*.ext" filters in the open file dialogs. There we don't have a combobox to select a filter. :(
322
            //TODO the combobox in the HildonFileChooserDialog doesn't change the filter automatically. We have to set it
323
            //in Qt..
324
            GtkFileFilter *gtkFilter = gtk_file_filter_new ();
325
            foreach (const QString &rawfilter, filters) {
326
                QStringList extensions = extract_filter(rawfilter);
327
                foreach (const QString &fileExtension, extensions) {
328
                    gtk_file_filter_add_pattern (gtkFilter, qPrintable(fileExtension));
329
                }
330
            }
331
            g_object_set(gtkFileChooser, "filter", gtkFilter, NULL);
332
        }
333
    }
334
335
    // Using the currently active window is not entirely correct, however
336
    // it gives more sensible behavior for applications that do not provide a
337
    // parent
338
    QWidget *modalFor = parent ? parent->window() : qApp->activeWindow();
339
    if (modalFor) {
340
        gtk_widget_realize(gtkFileChooser); // Creates X window
341
        XSetTransientForHint(gdk_x11_drawable_get_xdisplay(gtkFileChooser->window),
342
                             gdk_x11_drawable_get_xid(gtkFileChooser->window),
343
                             modalFor->winId());
344
        gdk_x11_window_set_user_time (gtkFileChooser->window, QX11Info::appUserTime());
345
    }
346
347
    QFileInfo fileinfo(dir);
348
    if (dir.isEmpty())
349
        fileinfo.setFile(QDir::currentPath());
350
    fileinfo.makeAbsolute();
351
    if (fileinfo.isDir()) {
352
        gtk_file_chooser_set_current_folder((GtkFileChooser*)gtkFileChooser, qPrintable(dir));
353
    } else if (isSaveDialog) {
354
        gtk_file_chooser_set_current_folder((GtkFileChooser*)gtkFileChooser, qPrintable(fileinfo.absolutePath()));
355
        gtk_file_chooser_set_current_name((GtkFileChooser*)gtkFileChooser, qPrintable(fileinfo.fileName()));
356
    } else {
357
        gtk_file_chooser_set_filename((GtkFileChooser*)gtkFileChooser, qPrintable(dir));
358
    }
359
}
360
361
QString QMaemo5StylePrivate::openFilename(QWidget *parent, const QString &, const QString &dir, const QString &filter,
362
        QString *selectedFilter, QFileDialog::Options options)
363
{
364
    QMap<GtkFileFilter *, QString> filterMap;
365
    GtkWidget *gtkFileChooser = hildon_file_chooser_dialog_new (NULL, GTK_FILE_CHOOSER_ACTION_OPEN);
366
367
    setupGtkFileChooser(gtkFileChooser, parent, dir, filter, selectedFilter, options, false, &filterMap);
368
369
    QWidget modal_widget;
370
    modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
371
    modal_widget.setParent(parent, Qt::Window);
372
    QApplicationPrivate::enterModal(&modal_widget);
373
374
    QString filename;
375
    if (gtk_dialog_run ((GtkDialog*)gtkFileChooser) == GTK_RESPONSE_OK) {
376
        char *gtk_filename = gtk_file_chooser_get_filename ((GtkFileChooser*)gtkFileChooser);
377
        filename = QString::fromUtf8(gtk_filename);
378
        g_free (gtk_filename);
379
        if (selectedFilter) {
380
            GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter ((GtkFileChooser*)gtkFileChooser);
381
            *selectedFilter = filterMap.value(gtkFilter);
382
        }
383
    }
384
385
    QApplicationPrivate::leaveModal(&modal_widget);
386
    gtk_widget_destroy (gtkFileChooser);
387
    return filename;
388
}
389
390
391
QString QMaemo5StylePrivate::openDirectory(QWidget *parent, const QString &, const QString &dir, QFileDialog::Options options)
392
{
393
    QMap<GtkFileFilter *, QString> filterMap;
394
    GtkWidget *gtkFileChooser = hildon_file_chooser_dialog_new (NULL, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
395
396
    setupGtkFileChooser(gtkFileChooser, parent, dir, QString(), 0, options);
397
    QWidget modal_widget;
398
    modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
399
    modal_widget.setParent(parent, Qt::Window);
400
    QApplicationPrivate::enterModal(&modal_widget);
401
402
    QString filename;
403
    if (gtk_dialog_run ((GtkDialog*)gtkFileChooser) == GTK_RESPONSE_OK) {
404
        char *gtk_filename = gtk_file_chooser_get_filename ((GtkFileChooser*)gtkFileChooser);
405
        filename = QString::fromUtf8(gtk_filename);
406
        g_free (gtk_filename);
407
    }
408
409
    QApplicationPrivate::leaveModal(&modal_widget);
410
    gtk_widget_destroy (gtkFileChooser);
411
    return filename;
412
}
413
414
QStringList QMaemo5StylePrivate::openFilenames(QWidget *parent, const QString &, const QString &dir, const QString &filter,
415
                                 QString *selectedFilter, QFileDialog::Options options)
416
{
417
    QStringList filenames;
418
    QMap<GtkFileFilter *, QString> filterMap;
419
    GtkWidget *gtkFileChooser = hildon_file_chooser_dialog_new (NULL, GTK_FILE_CHOOSER_ACTION_OPEN);
420
421
    setupGtkFileChooser(gtkFileChooser, parent, dir, filter, selectedFilter, options, false, &filterMap);
422
    g_object_set(gtkFileChooser, "select-multiple", gboolean(true), NULL);
423
424
    QWidget modal_widget;
425
    modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
426
    modal_widget.setParent(parent, Qt::Window);
427
    QApplicationPrivate::enterModal(&modal_widget);
428
429
    if (gtk_dialog_run ((GtkDialog*)gtkFileChooser) == GTK_RESPONSE_OK) {
430
        GSList *gtk_file_names = gtk_file_chooser_get_filenames((GtkFileChooser*)gtkFileChooser);
431
        for (GSList *iterator  = gtk_file_names ; iterator; iterator = iterator->next)
432
            filenames << QString::fromUtf8((const char*)iterator->data);
433
        g_slist_free(gtk_file_names);
434
        if (selectedFilter) {
435
            GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter ((GtkFileChooser*)gtkFileChooser);
436
            *selectedFilter = filterMap.value(gtkFilter);
437
        }
438
    }
439
440
    QApplicationPrivate::leaveModal(&modal_widget);
441
    gtk_widget_destroy (gtkFileChooser);
442
    return filenames;
443
}
444
445
QString QMaemo5StylePrivate::saveFilename(QWidget *parent, const QString &, const QString &dir, const QString &filter,
446
                           QString *selectedFilter, QFileDialog::Options options)
447
{
448
    QMap<GtkFileFilter *, QString> filterMap;
449
    GtkWidget *gtkFileChooser = hildon_file_chooser_dialog_new (NULL, GTK_FILE_CHOOSER_ACTION_SAVE);
450
    setupGtkFileChooser(gtkFileChooser, parent, dir, filter, selectedFilter, options, true, &filterMap);
451
452
    QWidget modal_widget;
453
    modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
454
    modal_widget.setParent(parent, Qt::Window);
455
    QApplicationPrivate::enterModal(&modal_widget);
456
457
    QString filename;
458
    if (gtk_dialog_run ((GtkDialog*)gtkFileChooser) == GTK_RESPONSE_OK) {
459
        char *gtk_filename = gtk_file_chooser_get_filename ((GtkFileChooser*)gtkFileChooser);
460
        filename = QString::fromUtf8(gtk_filename);
461
        g_free (gtk_filename);
462
        if (selectedFilter) {
463
            GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter ((GtkFileChooser*)gtkFileChooser);
464
            *selectedFilter = filterMap.value(gtkFilter);
465
        }
466
    }
467
468
    QApplicationPrivate::leaveModal(&modal_widget);
469
    gtk_widget_destroy (gtkFileChooser);
470
    return filename;
471
}
472
473
474
/*! \internal
475
 *  Returns one int value from the HildonAppMenu widget style.
476
 */
477
int QMaemo5StylePrivate::getAppMenuMetric( const char *metricName, int defaultValue ) const
478
{
479
    guint value = defaultValue;
480
    if (GtkWidget *hildonAppMenu = gtkWidget("HildonAppMenu"))
481
        gtk_widget_style_get(hildonAppMenu, metricName, &value, NULL);
482
    return value;
483
}
484
485
486
/*!
487
    \class QMaemo5Style
488
    \brief The QMaemo5Style class provides a widget style rendered by GTK+
489
    \since 4.6
490
    \ingroup qtmaemo5
491
492
    The QMaemo5Style style provides a look and feel that integrates Qt widgets
493
    into Hildon-based desktop environments.
494
495
    It is largely based on QGtkStyle, but does some special magic to "Hildonize" the look and feel.
496
497
    \note The style requires GTK+ version 2.10 or later and the special patched GTK+ Hildon include files.
498
*/
499
500
/*!
501
    Constructs a QMaemo5Style object.
502
*/
503
QMaemo5Style::QMaemo5Style()
504
    : QGtkStyle(*new QMaemo5StylePrivate)
505
{
506
}
507
508
/*!
509
    Destroys the QMaemo5Style object.
510
*/
511
QMaemo5Style::~QMaemo5Style()
512
{
513
}
514
515
/*!
516
    \reimp
517
    Sets the Maemo style file dialog hooks.
518
*/
519
void QMaemo5Style::polish(QApplication *app)
520
{
521
    Q_D(QMaemo5Style);
522
523
    QGtkStyle::polish(app);
524
525
    if (d->hildon_init
526
            && app->desktopSettingsAware() && d->isThemeAvailable()) {
527
        // I want to use the maemo style dialogs
528
        qt_filedialog_open_filename_hook = &QMaemo5StylePrivate::openFilename;
529
        qt_filedialog_save_filename_hook = &QMaemo5StylePrivate::saveFilename;
530
        qt_filedialog_open_filenames_hook = &QMaemo5StylePrivate::openFilenames;
531
        qt_filedialog_existing_directory_hook = &QMaemo5StylePrivate::openDirectory;
532
    }
533
}
534
535
536
ScrollBarFader::ScrollBarFader(QAbstractScrollArea *area, int delay, int duration, int update_interval)
537
    : QObject(area), m_area(area), m_current_alpha(0.0)
538
{
539
    m_fade_timeline = new QTimeLine(duration, this);
540
    m_fade_timeline->setUpdateInterval(update_interval);
541
    connect(m_fade_timeline, SIGNAL(valueChanged(qreal)), this, SLOT(fade(qreal)));
542
    m_delay_timer = new QTimer(this);
543
    m_delay_timer->setInterval(delay);
544
    connect(m_delay_timer, SIGNAL(timeout()), this, SLOT(delayTimeout()));
545
546
    area->viewport()->installEventFilter(this);
547
}
548
549
ScrollBarFader::~ScrollBarFader()
550
{
551
}
552
553
bool ScrollBarFader::eventFilter(QObject *o, QEvent *e)
554
{
555
    if (o == m_area->viewport()) {
556
        switch (e->type()) {
557
        case QEvent::Show:
558
        case QEvent::MouseButtonPress:
559
        case QEvent::MouseMove:
560
        case QEvent::MouseButtonRelease:
561
        case QEvent::Wheel:
562
        case QEvent::KeyPress:
563
        case QEvent::KeyRelease:
564
            if (!m_delay_timer->isActive()) {
565
                m_fade_timeline->setDirection(QTimeLine::Forward);
566
                if (m_fade_timeline->state() != QTimeLine::Running)
567
                    m_fade_timeline->start();
568
            }
569
            m_delay_timer->start();
570
            break;
571
572
        default:
573
            break;
574
        }
575
    }
576
    return QObject::eventFilter(o, e);
577
}
578
579
void ScrollBarFader::delayTimeout()
580
{
581
    m_fade_timeline->setDirection(QTimeLine::Backward);
582
    m_fade_timeline->resume();
583
    m_delay_timer->stop();
584
}
585
586
void ScrollBarFader::fade(qreal value)
587
{
588
    m_current_alpha = value;
589
590
    if (QScrollBar *hsb = m_area->horizontalScrollBar()) {
591
        if (hsb->isVisible()) {
592
            QStyleOptionSlider option;
593
            option.initFrom(hsb);
594
            option.subControls = QStyle::SC_All;
595
            hsb->update(); // ### hsb->style()->subControlRect(QStyle::CC_ScrollBar, &option, QStyle::SC_ScrollBarSlider, hsb));
596
        }
597
    }
598
    if (QScrollBar *vsb = m_area->verticalScrollBar()) {
599
        if (vsb->isVisible()) {
600
            QStyleOptionSlider option;
601
            option.initFrom(vsb);
602
            option.subControls = QStyle::SC_All;
603
            vsb->update(); // ### vsb->style()->subControlRect(QStyle::CC_ScrollBar, &option, QStyle::SC_ScrollBarSlider, vsb));
604
        }
605
    }
606
}
607
608
609
/*!
610
    \reimp
611
*/
612
void QMaemo5Style::polish(QWidget *widget)
613
{
614
    Q_D(QMaemo5Style);
615
616
    QGtkStyle::polish(widget);
617
    if (QAbstractScrollArea *area = qobject_cast<QAbstractScrollArea *>(widget)) {
618
        if ((qobject_cast<QTextEdit *>(widget) && !qobject_cast<QTextBrowser *>(widget)) || qobject_cast<QPlainTextEdit *>(widget)) {
619
            area->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
620
            area->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
621
        } else {
622
            area->setFrameStyle(QFrame::NoFrame);
623
624
            d->scrollBarFaders.insert(area, new ScrollBarFader(area, d->scrollBarFadeDelay,
625
                                                                     d->scrollBarFadeDuration,
626
                                                                     d->scrollBarFadeUpdateInterval));
627
628
            // make sure that the scrollbars have a white text color, because the scroll indicators are
629
            // drawn using the QPalette::Text color
630
            if (GtkWidget *gtkWidget = d->gtkWidget("HildonPannableArea.GtkTreeView")) {
631
                GdkColor gdkText = gtkWidget->style->text[GTK_STATE_NORMAL];
632
                QPalette scrollPalette = area->horizontalScrollBar()->palette();
633
                scrollPalette.setColor(QPalette::Text, QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8));
634
                area->horizontalScrollBar()->setPalette(scrollPalette);
635
                area->verticalScrollBar()->setPalette(scrollPalette);
636
            }
637
638
            if (QAbstractItemView *itemview = qobject_cast<QAbstractItemView *>(area)) {
639
                itemview->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
640
                itemview->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
641
642
                if (GtkWidget *gtkWidget = d->gtkWidget("HildonPannableArea.GtkTreeView")) {
643
                    QPalette palette = widget->palette();
644
645
                    GdkColor gdkBg, gdkBase, gdkText, gdkForeground;
646
                    GdkColor gdkSbg, gdkSfg, gdkIbg, gdkIfg;
647
                    QColor bg, base, text, fg, highlight, highlightText, disabled, disabledText;
648
649
                    gdkBg = gtkWidget->style->bg[GTK_STATE_NORMAL];
650
                    gdkForeground = gtkWidget->style->fg[GTK_STATE_NORMAL];
651
652
                    // Our base and selected color is primarily used for text
653
                    // so we assume a gtkWidget will have the most correct value
654
                    gdkBase = gtkWidget->style->base[GTK_STATE_NORMAL];
655
                    gdkText = gtkWidget->style->text[GTK_STATE_NORMAL];
656
                    gdkSbg = gtkWidget->style->base[GTK_STATE_SELECTED];
657
                    gdkSfg = gtkWidget->style->text[GTK_STATE_SELECTED];
658
                    gdkIbg = gtkWidget->style->base[GTK_STATE_INSENSITIVE];
659
                    gdkIfg = gtkWidget->style->text[GTK_STATE_INSENSITIVE];
660
661
                    bg = QColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8);
662
                    text = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
663
                    fg = QColor(gdkForeground.red>>8, gdkForeground.green>>8, gdkForeground.blue>>8);
664
                    base = QColor(gdkBase.red>>8, gdkBase.green>>8, gdkBase.blue>>8);
665
                    highlight = QColor(gdkSbg.red>>8, gdkSbg.green>>8, gdkSbg.blue>>8);
666
                    highlightText = QColor(gdkSfg.red>>8, gdkSfg.green>>8, gdkSfg.blue>>8);
667
                    disabled = QColor(gdkIbg.red>>8, gdkIbg.green>>8, gdkIbg.blue>>8);
668
                    disabledText = QColor(gdkIfg.red>>8, gdkIfg.green>>8, gdkIfg.blue>>8);
669
670
                    palette.setColor(QPalette::HighlightedText, highlightText);
671
                    palette.setColor(QPalette::Light, bg.lighter(125));
672
                    palette.setColor(QPalette::Shadow, bg.darker(130));
673
                    palette.setColor(QPalette::Dark, bg.darker(120));
674
                    palette.setColor(QPalette::Text, text);
675
                    palette.setColor(QPalette::WindowText, fg);
676
                    palette.setColor(QPalette::ButtonText, fg);
677
                    palette.setColor(QPalette::Base, base);
678
679
                    palette.setColor(QPalette::Disabled, QPalette::Text, disabledText);
680
                    palette.setColor(QPalette::Disabled, QPalette::WindowText, disabledText);
681
                    palette.setColor(QPalette::Disabled, QPalette::Foreground, disabledText);
682
                    palette.setColor(QPalette::Disabled, QPalette::ButtonText, disabledText);
683
684
                    widget->setPalette(palette);
685
                }
686
            }
687
        }
688
    } else if (qobject_cast<QCommandLinkButton*>(widget)) {
689
        widget->setFont(standardFont(QLS("SystemFont")));
690
        widget->setAttribute(Qt::WA_Hover, false);
691
    }
692
693
    if (widget && widget->parentWidget() && widget->parentWidget()->inherits("QMaemo5InformationBox")) {
694
        const char name[] = "HildonNote-information-theme.GtkAlignment.GtkHBox.GtkVBox.GtkEventBox.GtkAlignment.GtkVBox.HildonNoteLabel-information-theme";
695
        if (d->gtkWidget(name))
696
            widget->setPalette(d->gtkWidgetPalette(name));
697
    }
698
}
699
700
/*!
701
  \reimp
702
  */
703
void QMaemo5Style::unpolish(QWidget *widget)
704
{
705
    Q_D(QMaemo5Style);
706
707
    QGtkStyle::unpolish(widget);
708
709
    if (QAbstractScrollArea *area = qobject_cast<QAbstractScrollArea*>(widget)) {
710
        delete d->scrollBarFaders.take(area);
711
712
        if (QAbstractItemView *itemview = qobject_cast<QAbstractItemView *>(area))
713
            itemview->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
714
    }
715
}
716
717
/*!
718
    \reimp
719
*/
720
int QMaemo5Style::pixelMetric(QStyle::PixelMetric metric,
721
                           const QStyleOption *option,
722
                           const QWidget *widget) const
723
{
724
    Q_D(const QMaemo5Style);
725
726
    if (!d->isThemeAvailable())
727
        return QCleanlooksStyle::pixelMetric(metric, option, widget);
728
729
    switch (metric) {
730
    case PM_DefaultLayoutSpacing:
731
        return 8;
732
    case PM_DefaultTopLevelMargin:
733
        return 8;
734
    case PM_LayoutLeftMargin:
735
    case PM_LayoutRightMargin:
736
        return 16;
737
    case PM_LayoutTopMargin:
738
        return 0;
739
    case PM_LayoutBottomMargin:
740
        return 8;
741
    case PM_DialogButtonsButtonHeight:
742
        return 70;
743
    case PM_DialogButtonsButtonWidth:
744
        return 180;
745
    case PM_MessageBoxIconSize:
746
        return 0;
747
    case PM_ToolBarIconSize:
748
        return 48; // thumb icon size
749
    case PM_TabBarIconSize:
750
    case PM_ListViewIconSize:
751
        return 48; // thumb icon size
752
    case PM_TextCursorWidth:
753
        return 2;
754
    case PM_SplitterWidth:
755
        return 14;
756
    case PM_HeaderGripMargin:
757
        return 30;
758
    case PM_SmallIconSize:
759
        return 24;
760
    case PM_LargeIconSize:
761
        return 48;
762
    case PM_ButtonShiftHorizontal: {
763
        guint horizontal_shift = 1;
764
        if (GtkWidget *gtkButton = d->gtkWidget("HildonButton-finger"))
765
            d->gtk_widget_style_get(gtkButton, "child-displacement-x", &horizontal_shift, NULL);
766
        return horizontal_shift;
767
    }
768
    case PM_ButtonShiftVertical: {
769
        guint vertical_shift = 1;
770
        if (GtkWidget *gtkButton = d->gtkWidget("HildonButton-finger"))
771
            d->gtk_widget_style_get(gtkButton, "child-displacement-y", &vertical_shift, NULL);
772
        return vertical_shift;
773
    }
774
    case PM_ScrollBarSliderMin: // fall through
775
    case PM_ScrollBarExtent: {
776
        guint indicator_width = 6;
777
        if (GtkWidget *hildonPan = d->gtkWidget("HildonPannableArea"))
778
            d->gtk_widget_style_get(hildonPan, "indicator-width", &indicator_width, NULL);
779
        return indicator_width;
780
    }
781
    case PM_ScrollView_ScrollBarSpacing:
782
        return 0;
783
784
    case PM_TabBarTabHSpace:
785
        return 38;
786
    case PM_TabBarTabVSpace:
787
        return 38;
788
789
    case PM_ExclusiveIndicatorWidth:
790
    case PM_ExclusiveIndicatorHeight:
791
    case PM_IndicatorWidth:
792
    case PM_IndicatorHeight: {
793
        gint size = 38, spacing = 8;
794
        if (GtkWidget *hildonCheckButton = d->gtkWidget("HildonCheckButton-finger"))
795
            d->gtk_widget_style_get(hildonCheckButton, "image-spacing", &spacing, "checkbox-size", &size, NULL);
796
        return size + 2 * spacing;
797
    }
798
    case PM_CheckBoxLabelSpacing:
799
        return 3; // image-spacing is already calculated into PM_IndicatorWidth, but we're still 3pix to narrow...
800
801
    case PM_Maemo5AppMenuHorizontalSpacing:
802
        return d->getAppMenuMetric("horizontal-spacing", 16);
803
    case PM_Maemo5AppMenuVerticalSpacing:
804
        return d->getAppMenuMetric("vertical-spacing", 16);
805
    case PM_Maemo5AppMenuContentMargin:
806
        return d->getAppMenuMetric("inner-border", 16);
807
    case PM_Maemo5AppMenuLandscapeXOffset:
808
        return d->getAppMenuMetric("external-border", 50);
809
    case PM_Maemo5AppMenuFilterGroupWidth:
810
        return d->getAppMenuMetric("filter-group-width", 444);
811
    case PM_Maemo5AppMenuFilterGroupVerticalSpacing:
812
        return d->getAppMenuMetric("filter-vertical-spacing", 16);
813
814
    case PM_Maemo5EditBarBackButtonWidth: {
815
        gint width = 112;
816
        if (GtkWidget *hildonEditBar = d->gtkWidget("toolbar-edit-mode"))
817
            d->gtk_widget_style_get(hildonEditBar, "arrow-width", &width, NULL);
818
        return width;
819
    }
820
    case PM_Maemo5EditBarBackButtonHeight: {
821
        gint height = 56;
822
        if (GtkWidget *hildonEditBar = d->gtkWidget("toolbar-edit-mode"))
823
            d->gtk_widget_style_get(hildonEditBar, "arrow-height", &height, NULL);
824
        return height;
825
    }
826
827
    default:
828
        return  QGtkStyle::pixelMetric(metric, option, widget);
829
    }
830
}
831
832
/*!
833
    \reimp
834
*/
835
int QMaemo5Style::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
836
                         QStyleHintReturn *returnData = 0) const
837
{
838
    Q_D(const QMaemo5Style);
839
840
    if (!d->isThemeAvailable())
841
        return QCleanlooksStyle::styleHint(hint, option, widget, returnData);
842
843
    switch (hint) {
844
    case SH_Menu_Scrollable:
845
        return int(true);
846
    case SH_DialogButtonBox_ButtonsHaveIcons:
847
    case SH_ScrollBar_ContextMenu:
848
        return int(false);
849
    case SH_UnderlineShortcut:
850
        return int(false);
851
    case SH_RequestSoftwareInputPanel:
852
        return int(RSIP_OnMouseClick);
853
    case SH_Slider_AbsoluteSetButtons:
854
        return int(Qt::LeftButton);
855
    case SH_Slider_PageSetButtons:
856
        return int(Qt::NoButton);
857
    case SH_ItemView_ActivateItemOnSingleClick:
858
        return int(true);
859
    case SH_ComboBox_ListMouseTracking:
860
        return int(false);
861
    case SH_ComboBox_Popup:
862
        return int(false);
863
    case SH_ComboBox_PopupFrameStyle:
864
        return int(QFrame::Plain | QFrame::Box);
865
    case SH_ProgressDialog_TextLabelAlignment:
866
        return int(Qt::AlignLeft | Qt::AlignVCenter);
867
    default:
868
        return QGtkStyle::styleHint(hint, option, widget, returnData);
869
    }
870
}
871
872
/*!
873
    \reimp
874
*/
875
void QMaemo5Style::drawPrimitive(QStyle::PrimitiveElement element,
876
                              const QStyleOption *option,
877
                              QPainter *painter,
878
                              const QWidget *widget) const
879
{
880
    Q_D(const QMaemo5Style);
881
882
    if (!d->isThemeAvailable()) {
883
        QCleanlooksStyle::drawPrimitive(element, option, painter, widget);
884
        return;
885
    }
886
887
    QGtkPainter gtkPainter(painter);
888
889
    switch (element) {
890
    case PE_PanelButtonTool: // fall through
891
    case PE_PanelButtonCommand: {
892
        bool isTool = (element == PE_PanelButtonTool);
893
894
        GtkStateType state = gtkPainter.gtkState(option);
895
        GtkShadowType shadow = GTK_SHADOW_OUT;
896
        if (option->state & State_On || option->state & State_Sunken) {
897
            state = GTK_STATE_ACTIVE;
898
            shadow = GTK_SHADOW_IN;
899
        }
900
        GtkWidget *gtkButton = 0;
901
        if (isTool)
902
            gtkButton = d->gtkWidget("GtkToolButton.GtkButton");
903
        else if (widget && qobject_cast<QDialogButtonBox *>(widget->parentWidget()))
904
            gtkButton = d->gtkWidget("HildonDialog.GtkAlignment.GtkHBox.hildon-dialog-action-area.GtkButton-finger");
905
        else if (widget && widget->parentWidget() && !qstrcmp(widget->parentWidget()->metaObject()->className(), "QMaemo5EditBar"))
906
            gtkButton = d->gtkWidget("toolbar-edit-mode.GtkAlignment.GtkHBox.GtkButton");
907
        else
908
            gtkButton = d->gtkWidget("HildonButton-finger");
909
910
        if (gtkButton) {
911
            gtkPainter.paintBox(gtkButton, "button", option->rect, state, shadow,
912
                                gtkButton->style, QString());
913
        }
914
        break;
915
    }
916
    case PE_IndicatorBranch:
917
        // Due to style guidelines, maemo5 does not draw
918
        // expanders. But for compatibilitys sake we have to
919
        if (option->state & State_Children) {
920
            if (option->state & State_Open)
921
                QCleanlooksStyle::drawPrimitive(PE_IndicatorArrowDown, option, painter, widget);
922
            else
923
                QCleanlooksStyle::drawPrimitive(PE_IndicatorArrowRight, option, painter, widget);
924
        }
925
        break;
926
927
    case PE_FrameFocusRect:
928
        // never required with Hildon
929
        break;
930
931
    case PE_Frame:
932
        if ((qobject_cast<const QTextEdit *>(widget) && !qobject_cast<const QTextBrowser *>(widget)) || qobject_cast<const QPlainTextEdit *>(widget))
933
            proxy()->drawPrimitive(PE_FrameLineEdit, option, painter, widget);
934
        else
935
            QGtkStyle::drawPrimitive(element, option, painter, widget);
936
        break;
937
938
    case PE_PanelLineEdit:
939
        {
940
            int lineWidth = 1;
941
            if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option))
942
                lineWidth = panel->lineWidth;
943
            // For embedded line edits we rely on the parent widget to fill the
944
            // background. The entry_bg is not a uniform color in Hildon
945
            if (lineWidth > 0) {
946
                GtkWidget *gtkEntry = d->gtkWidget("HildonEntry-finger");
947
                uint resolve_mask = option->palette.resolve();
948
                QRect textRect = option->rect.adjusted(gtkEntry->style->xthickness, gtkEntry->style->ythickness,
949
                                                       -gtkEntry->style->xthickness, -gtkEntry->style->ythickness);
950
                if (widget && widget->testAttribute(Qt::WA_SetPalette) &&
951
                    resolve_mask & (1 << QPalette::Base)) // Palette overridden by user
952
                    painter->fillRect(textRect, option->palette.base());
953
                else {
954
                    gtkPainter.paintFlatBox(gtkEntry, "entry_bg", textRect,
955
                                            option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, GTK_SHADOW_NONE, gtkEntry->style);
956
                }
957
                proxy()->drawPrimitive(PE_FrameLineEdit, option, painter, widget);
958
            } else if (option->state & State_Enabled) {
959
                // Note this is a bit of a workaround. Open item editors require us to fill the line
960
                // edit background but doing this will make it impossible to get the correct
961
                // background color for disabled spin box, line edit and combo box.
962
                //
963
                // This would be more elegant if we could either get the correct fill color for
964
                // disabled line edits or if itemview did not draw the item when an editor is open
965
                painter->fillRect(option->rect.adjusted(lineWidth, lineWidth, -lineWidth, -lineWidth),
966
                                  option->palette.brush(QPalette::Base));
967
            }
968
        }
969
        break;
970
971
    case PE_FrameLineEdit: {
972
        GtkWidget *gtkEntry = d->gtkWidget("HildonEntry-finger");
973
974
        gboolean interior_focus;
975
        gint focus_line_width;
976
        gboolean state_hint = false;
977
        QRect rect = option->rect;
978
        d->gtk_widget_style_get(gtkEntry,
979
                               "interior-focus", &interior_focus,
980
                               "focus-line-width", &focus_line_width,
981
                               "state-hint", &state_hint, NULL);
982
983
        // See https://bugzilla.mozilla.org/show_bug.cgi?id=405421 for info about this hack
984
        g_object_set_data(G_OBJECT(gtkEntry), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
985
986
        if (!interior_focus && option->state & State_HasFocus)
987
            rect.adjust(focus_line_width, focus_line_width, -focus_line_width, -focus_line_width);
988
989
        if (option->state & State_HasFocus)
990
            GTK_WIDGET_SET_FLAGS(gtkEntry, GTK_HAS_FOCUS);
991
        gtkPainter.paintShadow(gtkEntry, "entry", rect, option->state & State_Enabled ?
992
                               (state_hint && option->state & State_HasFocus ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL) : GTK_STATE_INSENSITIVE,
993
                               GTK_SHADOW_IN, gtkEntry->style,
994
                               option->state & State_HasFocus ? QLS("focus") : QString());
995
        if (!interior_focus && option->state & State_HasFocus)
996
            gtkPainter.paintShadow(gtkEntry, "entry", option->rect, option->state & State_Enabled ?
997
                                   GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
998
                                   GTK_SHADOW_IN, gtkEntry->style, QLS("GtkEntryShadowIn"));
999
1000
        if (option->state & State_HasFocus)
1001
            GTK_WIDGET_UNSET_FLAGS(gtkEntry, GTK_HAS_FOCUS);
1002
        break;
1003
    }
1004
1005
    case PE_IndicatorCheckBox: {
1006
        GtkShadowType shadow = GTK_SHADOW_OUT;
1007
        GtkStateType state = gtkPainter.gtkState(option);
1008
1009
        if (option->state & State_Sunken)
1010
            state = GTK_STATE_ACTIVE;
1011
1012
        if (option->state & State_NoChange)
1013
            shadow = GTK_SHADOW_ETCHED_IN;
1014
        else if (option->state & State_On)
1015
            shadow = GTK_SHADOW_IN;
1016
        else
1017
            shadow = GTK_SHADOW_OUT;
1018
1019
        int spacing;
1020
1021
        if (GtkWidget *gtkCheckButton = d->gtkWidget("HildonCheckButton-finger")) {
1022
            GtkWidget *gtkCellView = d->gtkWidget("HildonCheckButton-finger.GtkAlignment.GtkHBox.GtkCellView");
1023
            // Some styles such as aero-clone assume they can paint in the spacing area
1024
            gtkPainter.setClipRect(option->rect);
1025
1026
            d->gtk_widget_style_get(gtkCheckButton, "image-spacing", &spacing, NULL);
1027
1028
            QRect checkRect = option->rect.adjusted(spacing, spacing, -spacing, -spacing);
1029
1030
            gtkPainter.paintCheckbox(gtkCellView, checkRect, state, shadow, gtkCellView->style,
1031
                                     QLS("checkbutton"));
1032
        }
1033
        break;
1034
    }
1035
    case PE_PanelItemViewItem: {
1036
1037
            //To improve the performance we won't cache unusable states
1038
            QStyle::State old_state = option->state;
1039
            const_cast<QStyleOption*>(option)->state |= QStyle::State_Active;
1040
            const_cast<QStyleOption*>(option)->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
1041
1042
            BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("panelitem"));
1043
            QGtkPainter gtkCachedPainter(p);
1044
            QRect cacheRect(0, 0, option->rect.width(), option->rect.height());
1045
            if (GtkWidget *gtkTreeView = d->gtkWidget("HildonPannableArea.GtkTreeView")) {
1046
                const char *detail = "cell_odd_ruled";
1047
                const QStyleOptionViewItemV4 *vopt = qstyleoption_cast<const QStyleOptionViewItemV4 *>(option);
1048
                if (vopt && vopt->features & QStyleOptionViewItemV2::Alternate)
1049
                    detail = "cell_even_ruled";
1050
1051
                int rowHeight = 70;
1052
                if (GtkWidget *gtkTreeView = d->gtkWidget("HildonPannableArea.GtkTreeView"))
1053
                    d->gtk_widget_style_get(gtkTreeView, "row-height", &rowHeight, NULL);
1054
1055
                if (option->rect.height() != rowHeight) {
1056
                    QPixmap scalePix(option->rect.width(), rowHeight);
1057
                    scalePix.fill(Qt::transparent);
1058
                    QPainter scalePainter(&scalePix);
1059
                    QGtkPainter gtkScalePainter(&scalePainter);
1060
                    gtkScalePainter.setUsePixmapCache(false); // cached externally
1061
1062
                    // the sapwood engine won't scale the image, but instead tile it, which looks ridiculous
1063
                    gtkScalePainter.paintFlatBox(gtkTreeView, detail, QRect(0, 0, option->rect.width(), rowHeight),
1064
                                                 option->state & State_Selected ? GTK_STATE_SELECTED :
1065
                                                 option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE,
1066
                                                 GTK_SHADOW_NONE, gtkTreeView->style);
1067
1068
                    // don't just scale the whole pixmap - the bottom border line would look extremly ugly for big items
1069
                    int dh = 8; // just an arbitrary value which looks good with the default Maemo styles
1070
                    p->drawPixmap(cacheRect.topLeft(), scalePix, QRect(0, 0, scalePix.width(), dh));
1071
                    p->drawPixmap(cacheRect.adjusted(0, dh, 0, -dh), scalePix, QRect(0, dh, scalePix.width(), scalePix.height() - 2 * dh));
1072
                    p->drawPixmap(cacheRect.bottomLeft() - QPoint(0, dh), scalePix, QRect(0, scalePix.height() - dh, scalePix.width(), dh));
1073
                } else {
1074
                    gtkCachedPainter.paintFlatBox(gtkTreeView, detail, cacheRect,
1075
                                            option->state & State_Selected ? GTK_STATE_SELECTED :
1076
                                            option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE,
1077
                                            GTK_SHADOW_NONE, gtkTreeView->style);
1078
                }
1079
            }
1080
            END_STYLE_PIXMAPCACHE
1081
            const_cast<QStyleOption*>(option)->state = old_state;
1082
        break;
1083
    }
1084
    case PE_Maemo5InformationBox: {
1085
        if (GtkWidget *hildonInformation = d->gtkWidget("HildonNote-information-theme"))
1086
            gtkPainter.paintFlatBox(hildonInformation, NULL, option->rect, GTK_STATE_NORMAL, GTK_SHADOW_NONE, hildonInformation->style, QString());
1087
        break;
1088
    }
1089
    case PE_Maemo5AppMenu: {
1090
        if (GtkWidget *hildonAppMenu = d->gtkWidget("HildonAppMenu"))
1091
            gtkPainter.paintFlatBox(hildonAppMenu, NULL, option->rect, GTK_STATE_NORMAL, GTK_SHADOW_NONE, hildonAppMenu->style, QString());
1092
        break;
1093
    }
1094
    case PE_Maemo5EditBar: {
1095
        if (GtkWidget *hildonEditBar = d->gtkWidget("toolbar-edit-mode"))
1096
            gtkPainter.paintFlatBox(hildonEditBar, NULL, option->rect, GTK_STATE_NORMAL, GTK_SHADOW_NONE, hildonEditBar->style, QString());
1097
        break;
1098
    }
1099
    case PE_Maemo5EditBarBackButton: {
1100
        if (GtkWidget *hildonEditBarBackButton = d->gtkWidget("toolbar-edit-mode.hildon-edit-toolbar-arrow")) {
1101
            GtkStateType state = gtkPainter.gtkState(option);
1102
            GtkShadowType shadow = GTK_SHADOW_OUT;
1103
            if (option->state & State_On || option->state & State_Sunken) {
1104
                state = GTK_STATE_ACTIVE;
1105
                shadow = GTK_SHADOW_IN;
1106
            }
1107
            gtkPainter.paintBox(hildonEditBarBackButton, "button", option->rect, state, shadow, hildonEditBarBackButton->style, QString());
1108
        }
1109
        break;
1110
    }
1111
    default:
1112
        QGtkStyle::drawPrimitive(element, option, painter, widget);
1113
    }
1114
}
1115
1116
/*!
1117
    \reimp
1118
*/
1119
void QMaemo5Style::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
1120
                                   QPainter *painter, const QWidget *widget) const
1121
{
1122
    Q_D(const QMaemo5Style);
1123
1124
    if (!d->isThemeAvailable()) {
1125
1126
        QCleanlooksStyle::drawComplexControl(control, option, painter, widget);
1127
        return;
1128
    }
1129
1130
    QGtkPainter gtkPainter(painter);
1131
1132
    switch (control) {
1133
    case CC_Dial:
1134
        if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
1135
            QPalette pal = option->palette;
1136
            QColor buttonColor(0x404040); //= pal.button().color();
1137
            const int width = option->rect.width();
1138
            const int height = option->rect.height();
1139
            const bool enabled = option->state & QStyle::State_Enabled;
1140
            qreal r = qMin(width, height) / 2;
1141
            r -= r/50;
1142
            const qreal penSize = r/20.0;
1143
1144
            painter->save();
1145
            painter->setRenderHint(QPainter::Antialiasing);
1146
1147
            // Draw notches
1148
            if (option->subControls & QStyle::SC_DialTickmarks) {
1149
                painter->setPen(option->palette.dark().color().darker(120));
1150
                painter->drawLines(QStyleHelper::calcLines(scrollBar));
1151
            }
1152
1153
            // Cache dial background
1154
            BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("qdial"));
1155
            p->setRenderHint(QPainter::Antialiasing);
1156
1157
            const qreal d_ = r / 6;
1158
            const qreal dx = option->rect.x() + d_ + (width - 2 * r) / 2 + 1;
1159
            const qreal dy = option->rect.y() + d_ + (height - 2 * r) / 2 + 1;
1160
1161
            QRectF br = QRectF(dx + 0.5, dy + 0.5,
1162
                               int(r * 2 - 2 * d_ - 2),
1163
                               int(r * 2 - 2 * d_ - 2));
1164
            QColor shadowColor(0, 0, 0, 20);
1165
1166
            if (enabled) {
1167
                // Drop shadow
1168
                qreal shadowSize = qMax(1.0, penSize/2.0);
1169
                QRectF shadowRect= br.adjusted(-2*shadowSize, -2*shadowSize,
1170
                                               2*shadowSize, 2*shadowSize);
1171
                QRadialGradient shadowGradient(shadowRect.center().x(),
1172
                                               shadowRect.center().y(), shadowRect.width()/2.0,
1173
                                               shadowRect.center().x(), shadowRect.center().y());
1174
                shadowGradient.setColorAt(qreal(0.91), QColor(0, 0, 0, 40));
1175
                shadowGradient.setColorAt(qreal(1.0), Qt::transparent);
1176
                p->setBrush(shadowGradient);
1177
                p->setPen(Qt::NoPen);
1178
                p->translate(shadowSize, shadowSize);
1179
                p->drawEllipse(shadowRect);
1180
                p->translate(-shadowSize, -shadowSize);
1181
1182
                // Main gradient
1183
                QRadialGradient gradient(br.center().x() - br.width()/3, dy,
1184
                                         br.width()*1.3, br.center().x(),
1185
                                         br.center().y() - br.height()/2);
1186
                gradient.setColorAt(0, buttonColor);
1187
                gradient.setColorAt(qreal(0.6), buttonColor);
1188
                gradient.setColorAt(qreal(0.601), buttonColor.darker(107));
1189
                gradient.setColorAt(1, buttonColor.darker(107));
1190
                p->setBrush(gradient);
1191
            } else {
1192
                p->setBrush(Qt::NoBrush);
1193
            }
1194
1195
            p->setPen(QPen(buttonColor.lighter(160), 2));
1196
            p->drawEllipse(br);
1197
            p->setBrush(Qt::NoBrush);
1198
            p->setPen(buttonColor.lighter(110));
1199
            p->drawEllipse(br.adjusted(1, 1, -1, -1));
1200
1201
            if (option->state & QStyle::State_HasFocus) {
1202
                QColor highlight = pal.highlight().color();
1203
                highlight.setHsv(highlight.hue(),
1204
                                 qMin(190, highlight.saturation()),
1205
                                 qMax(210, highlight.value()));
1206
                highlight.setAlpha(200);
1207
                p->setPen(QPen(highlight, 2.0));
1208
                p->setBrush(Qt::NoBrush);
1209
                p->drawEllipse(br.adjusted(-1, -1, 1, 1));
1210
            }
1211
1212
            END_STYLE_PIXMAPCACHE
1213
1214
            QPointF dp = QStyleHelper::calcRadialPos(scrollBar, qreal(0.70));
1215
            buttonColor = buttonColor.darker(104);
1216
            buttonColor.setAlphaF(qreal(0.8));
1217
            const qreal ds = r/qreal(7.0);
1218
            QRectF dialRect(dp.x() - ds, dp.y() - ds, 2*ds, 2*ds);
1219
            QRadialGradient dialGradient(dialRect.center().x() + dialRect.width()/2,
1220
                                         dialRect.center().y() + dialRect.width(),
1221
                                         dialRect.width()*2,
1222
                                         dialRect.center().x(), dialRect.center().y());
1223
            dialGradient.setColorAt(1, buttonColor.darker(130));
1224
            dialGradient.setColorAt(qreal(0.4), buttonColor.darker(160));
1225
            dialGradient.setColorAt(0, buttonColor.darker(130));
1226
            if (penSize > 3.0) {
1227
                painter->setPen(QPen(QColor(255,255,255,150), penSize));
1228
                painter->drawLine(QStyleHelper::calcRadialPos(scrollBar, qreal(0.90)), QStyleHelper::calcRadialPos(scrollBar, qreal(0.96)));
1229
            }
1230
            painter->setBrush(dialGradient);
1231
            painter->setPen(QPen(Qt::white, 3));
1232
            painter->drawEllipse(dialRect.adjusted(-1, -1, 1, 1));
1233
            painter->setPen(QPen(Qt::black, 2));
1234
            painter->drawEllipse(dialRect);
1235
            painter->restore();
1236
    }
1237
    break;
1238
    case CC_ScrollBar:
1239
        if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
1240
            painter->fillRect(option->rect, option->palette.background());
1241
            QRect scrollBarSlider = proxy()->subControlRect(control, scrollBar, SC_ScrollBarSlider, widget);
1242
            QColor color = option->palette.color(QPalette::Text);
1243
1244
            if (widget && widget->parentWidget()) {
1245
                if (QAbstractScrollArea *area = qobject_cast<QAbstractScrollArea*>(widget->parentWidget()->parentWidget())) {
1246
                    if (ScrollBarFader *fader = d->scrollBarFaders.value(area)) {
1247
                        color.setAlphaF(fader->currentAlpha());
1248
                    }
1249
                }
1250
            }
1251
1252
            painter->fillRect(scrollBarSlider, color);
1253
        }
1254
        break;
1255
    case CC_SpinBox:
1256
        if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
1257
            bool sunken = (spinBox->state & State_Sunken);
1258
            bool upIsActive = (spinBox->activeSubControls == SC_SpinBoxUp);
1259
            bool downIsActive = (spinBox->activeSubControls == SC_SpinBoxDown);
1260
            bool reverse = (spinBox->direction == Qt::RightToLeft);
1261
1262
            QStyleOption editOpt = *spinBox;
1263
            QRect editRect = subControlRect(CC_SpinBox, option, SC_SpinBoxFrame, widget);
1264
            editOpt.rect = editRect;
1265
            drawPrimitive(PE_PanelLineEdit, &editOpt, painter, widget);
1266
1267
            if (spinBox->buttonSymbols != QAbstractSpinBox::NoButtons) {
1268
                QRect plusRect = subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget);
1269
                QRect minusRect = subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
1270
                QColor textColor = option->palette.color(QPalette::Text);
1271
                QColor highlightedTextColor = option->palette.color(QPalette::Highlight);
1272
                QColor disabledColor = option->palette.color(QPalette::Disabled, QPalette::Text);
1273
                disabledColor.setAlpha(100);
1274
                int size = 10;
1275
                int gripMargin = 1;
1276
                int centerX = minusRect.center().x();
1277
                int centerY = minusRect.center().y();
1278
1279
                // minus
1280
                centerX = minusRect.center().x();
1281
                QColor penColor = (downIsActive && sunken) ? highlightedTextColor : textColor;
1282
                painter->setPen(QPen(penColor, 5));
1283
                painter->drawLine(centerX - size, centerY, centerX + size, centerY);
1284
1285
                // plus
1286
                centerX = plusRect.center().x();
1287
                penColor = (upIsActive && sunken) ? highlightedTextColor : textColor;
1288
                painter->setPen(QPen(penColor, 5));
1289
                painter->drawLine(centerX - size, centerY, centerX + size, centerY);
1290
                painter->drawLine(centerX, centerY - size, centerX, centerY + size);
1291
1292
                // dots
1293
                size = 15;
1294
                textColor.setAlpha(50);
1295
                QRect scrollBarSlider = option->rect;
1296
                QRect rightRect = reverse ? minusRect : plusRect;
1297
                QRect leftRect = reverse ? plusRect: minusRect;
1298
                for (int i = -size; i < size ; i += 4) {
1299
                    QPoint left(leftRect.right() + gripMargin, leftRect.center().y()+ i);
1300
                    painter->fillRect(QRect(left, QSize(2,2)), textColor);
1301
                    QPoint right(rightRect.left() + gripMargin - 1, rightRect.center().y() + i);
1302
                    painter->fillRect(QRect(right, QSize(2,2)), textColor);
1303
                }
1304
            }
1305
        }
1306
1307
        break;
1308
1309
    case CC_ComboBox:
1310
        if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
1311
            if (!combo->editable) {
1312
                QStyleOptionButton button;
1313
                button.QStyleOption::operator=(*option); // copy QStyleOption members
1314
                button.features = QStyleOptionButton::None;
1315
                button.icon = combo->currentIcon;
1316
                button.iconSize = combo->iconSize;
1317
                button.text = QString();
1318
1319
                proxy()->drawControl(QStyle::CE_PushButtonBevel, &button, painter, widget);
1320
            } else {
1321
                if (const QStyleOptionComboBox *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
1322
                    bool sunken = comboBox->state & State_On; // play dead, if combobox has no items
1323
                    BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("cb-%0-%1").arg(sunken).arg(comboBox->editable));
1324
                    QGtkPainter gtkCachedPainter(p);
1325
                    gtkCachedPainter.setUsePixmapCache(false); // cached externally
1326
1327
                    bool isEnabled = (comboBox->state & State_Enabled);
1328
                    bool focus = isEnabled && (comboBox->state & State_HasFocus);
1329
                    QColor buttonShadow = option->palette.dark().color();
1330
                    GtkStateType state = gtkPainter.gtkState(option);
1331
                    int appears_as_list = !proxy()->styleHint(QStyle::SH_ComboBox_Popup, comboBox, widget);
1332
                    QPixmap cache;
1333
                    QString pixmapName;
1334
                    QStyleOptionComboBox comboBoxCopy = *comboBox;
1335
                    comboBoxCopy.rect = option->rect;
1336
1337
                    bool reverse = (option->direction == Qt::RightToLeft);
1338
                    QRect rect = option->rect;
1339
                    QRect arrowButtonRect = proxy()->subControlRect(CC_ComboBox, &comboBoxCopy,
1340
                                                           SC_ComboBoxArrow, widget);
1341
                    QRect editRect = proxy()->subControlRect(CC_ComboBox, &comboBoxCopy,
1342
                                                    SC_ComboBoxEditField, widget);
1343
1344
                    GtkShadowType shadow = (option->state & State_Sunken || option->state & State_On ) ?
1345
                                           GTK_SHADOW_IN : GTK_SHADOW_OUT;
1346
1347
                    // We use the gtk widget to position arrows and separators for us
1348
                    GtkWidget *gtkCombo = d->gtkWidget("hildon-combobox");
1349
                    GtkAllocation geometry = {0, 0, option->rect.width(), option->rect.height()};
1350
                    d->gtk_widget_set_direction(gtkCombo, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
1351
                    d->gtk_widget_size_allocate(gtkCombo, &geometry);
1352
1353
                    QHashableLatin1Literal buttonPath("hildon-combobox.hildon-combobox-disabled-button");
1354
                    if (focus)
1355
                        buttonPath = "hildon-combobox.hildon-combobox-active-button";
1356
                    else if (isEnabled)
1357
                        buttonPath = "hildon-combobox.hildon-combobox-button";
1358
                    GtkWidget *gtkToggleButton = d->gtkWidget(buttonPath);
1359
                    d->gtk_widget_set_direction(gtkToggleButton, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
1360
                    if (gtkToggleButton && (appears_as_list || comboBox->editable)) {
1361
                        if (focus)
1362
                            GTK_WIDGET_SET_FLAGS(gtkToggleButton, GTK_HAS_FOCUS);
1363
                        // Draw the combo box as a line edit with a button next to it
1364
                        if (comboBox->editable || appears_as_list) {
1365
                            GtkStateType frameState = (state == GTK_STATE_PRELIGHT) ? GTK_STATE_NORMAL : state;
1366
                            if (focus)
1367
                                frameState = GTK_STATE_ACTIVE;
1368
                            QHashableLatin1Literal entryPath("hildon-combobox.HildonEntry-finger");
1369
                            GtkWidget *gtkEntry = d->gtkWidget(entryPath);
1370
                            d->gtk_widget_set_direction(gtkEntry, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
1371
                            QRect frameRect = option->rect;
1372
1373
                            if (reverse)
1374
                                frameRect.setLeft(arrowButtonRect.right());
1375
                            else
1376
                                frameRect.setRight(arrowButtonRect.left());
1377
1378
                            // Fill the line edit background
1379
                            // We could have used flat_box with "entry_bg" but that is probably not worth the overhead
1380
                            uint resolve_mask = option->palette.resolve();
1381
                            int xt = gtkEntry->style->xthickness;
1382
                            int yt = gtkEntry->style->ythickness;
1383
                            QRect contentRect = frameRect.adjusted(xt, yt, -xt, -yt);
1384
                            // Required for inner blue highlight with clearlooks
1385
                            if (focus)
1386
                                GTK_WIDGET_SET_FLAGS(gtkEntry, GTK_HAS_FOCUS);
1387
1388
                            if (widget && widget->testAttribute(Qt::WA_SetPalette) &&
1389
                                resolve_mask & (1 << QPalette::Base)) // Palette overridden by user
1390
                                painter->fillRect(contentRect, option->palette.base().color());
1391
                            else {
1392
                                gtkCachedPainter.paintFlatBox(gtkEntry, "entry_bg", contentRect,
1393
                                                        option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE,
1394
                                                        GTK_SHADOW_NONE, gtkEntry->style, entryPath.toString() + QString::number(focus));
1395
                            }
1396
                            gtkCachedPainter.paintShadow(gtkEntry, comboBox->editable ? "entry" : "frame", frameRect, frameState,
1397
                                                   GTK_SHADOW_IN, gtkEntry->style, entryPath.toString() +
1398
                                                   QString::number(focus) + QString::number(comboBox->editable) +
1399
                                                   QString::number(option->direction)/* + QString::number(frameState)*/);
1400
                            if (focus)
1401
                                GTK_WIDGET_UNSET_FLAGS(gtkEntry, GTK_HAS_FOCUS);
1402
                        }
1403
1404
                        GtkStateType buttonState = GTK_STATE_NORMAL;
1405
1406
                        if (!(option->state & State_Enabled))
1407
                            buttonState = GTK_STATE_INSENSITIVE;
1408
                        else if (option->state & State_Sunken || option->state & State_On)
1409
                            buttonState = GTK_STATE_ACTIVE;
1410
                        else if (option->state & State_MouseOver && comboBox->activeSubControls & SC_ComboBoxArrow)
1411
                            buttonState = GTK_STATE_PRELIGHT;
1412
1413
                        Q_ASSERT(gtkToggleButton);
1414
                        gtkCachedPainter.paintBox( gtkToggleButton, "button", arrowButtonRect, buttonState,
1415
                                             shadow, gtkToggleButton->style, buttonPath.toString() +
1416
                                             QString::number(focus) + QString::number(option->direction));
1417
                        if (focus)
1418
                            GTK_WIDGET_UNSET_FLAGS(gtkToggleButton, GTK_HAS_FOCUS);
1419
                    }
1420
1421
                    END_STYLE_PIXMAPCACHE;
1422
                }
1423
            }
1424
        }
1425
        break;
1426
1427
    default:
1428
        QGtkStyle::drawComplexControl(control, option, painter, widget);
1429
    }
1430
}
1431
1432
static QHBoxLayout *findHBoxLayoutContaining(const QWidget *widget, QLayout *lay)
1433
{
1434
    for (int i = 0; i < lay->count(); ++i) {
1435
        if (QLayout *sublay = lay->itemAt(i)->layout()) {
1436
            if (QHBoxLayout *box = findHBoxLayoutContaining(widget, sublay))
1437
                return box;
1438
        } else if (lay->itemAt(i)->widget() == widget) {
1439
            return qobject_cast<QHBoxLayout *>(lay);
1440
        }
1441
    }
1442
    return 0;
1443
}
1444
1445
/*!
1446
    \reimp
1447
*/
1448
void QMaemo5Style::drawControl(ControlElement element,
1449
                               const QStyleOption *option,
1450
                               QPainter *painter,
1451
                               const QWidget *widget) const
1452
{
1453
    Q_D(const QMaemo5Style);
1454
1455
    if (!d->isThemeAvailable()) {
1456
        QCleanlooksStyle::drawControl(element, option, painter, widget);
1457
        return;
1458
    }
1459
1460
    QGtkPainter gtkPainter(painter);
1461
1462
    switch (element) {
1463
    case CE_PushButtonBevel:
1464
        // This code is copied from commonstyle sans the CommandLink part
1465
        if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1466
            QRect br = btn->rect;
1467
            int dbi = proxy()->pixelMetric(PM_ButtonDefaultIndicator, btn, widget);
1468
            if (btn->features & QStyleOptionButton::DefaultButton)
1469
                proxy()->drawPrimitive(PE_FrameDefaultButton, option, painter, widget);
1470
            if (btn->features & QStyleOptionButton::AutoDefaultButton)
1471
                br.setCoords(br.left() + dbi, br.top() + dbi, br.right() - dbi, br.bottom() - dbi);
1472
            if (!(btn->features & (QStyleOptionButton::Flat))
1473
                || btn->state & (State_Sunken | State_On)
1474
                || (btn->features & QStyleOptionButton::CommandLinkButton && btn->state & State_MouseOver)) {
1475
                QStyleOptionButton tmpBtn = *btn;
1476
                tmpBtn.rect = br;
1477
                proxy()->drawPrimitive(PE_PanelButtonCommand, &tmpBtn, painter, widget);
1478
            }
1479
            if (btn->features & QStyleOptionButton::HasMenu) {
1480
                int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, btn, widget);
1481
                QRect ir = btn->rect;
1482
                QStyleOptionButton newBtn = *btn;
1483
                newBtn.rect = QRect(ir.right() - mbi + 2, ir.height()/2 - mbi/2 + 3, mbi - 6, mbi - 6);
1484
                proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
1485
            }
1486
        }
1487
        break;
1488
    case CE_RadioButton:
1489
        if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1490
            bool handled = false;
1491
1492
            if (qobject_cast<const QRadioButton *>(widget) && widget->parentWidget() && widget->parentWidget()->layout()) {
1493
                if (QHBoxLayout *box = findHBoxLayoutContaining(widget, widget->parentWidget()->layout())) {
1494
                    QList<const QRadioButton *> buttonList;
1495
                    for (int i = 0; i < box->count(); ++i) {
1496
                        if (QRadioButton *radio = qobject_cast<QRadioButton *>(box->itemAt(i)->widget()))
1497
                            buttonList << radio;
1498
                    }
1499
1500
                    GtkWidget *gtkButton = 0;
1501
                    int pos = buttonList.indexOf(qobject_cast<const QRadioButton *>(widget));
1502
                    if (pos == 0)
1503
                        gtkButton = d->radioButtonLeft;
1504
                    else if (pos == buttonList.count() - 1)
1505
                        gtkButton = d->radioButtonRight;
1506
                    else
1507
                        gtkButton = d->radioButtonMiddle;
1508
1509
                    GtkStateType state = gtkPainter.gtkState(option);
1510
                    GtkShadowType shadow = GTK_SHADOW_OUT;
1511
                    if (option->state & State_On || option->state & State_Sunken) {
1512
                        state = GTK_STATE_ACTIVE;
1513
                        shadow = GTK_SHADOW_IN;
1514
                    }
1515
                    if (gtkButton) {
1516
                        gtkPainter.paintBox(gtkButton, "button", option->rect, state, shadow,
1517
                                            gtkButton->style, QString());
1518
                    }
1519
                    handled = true;
1520
                }
1521
            }
1522
            if (!handled)
1523
                proxy()->drawControl(CE_PushButtonBevel, btn, painter, widget);
1524
1525
            QStyleOptionButton subopt = *btn;
1526
            subopt.rect = proxy()->subElementRect(SE_RadioButtonContents, btn, widget);
1527
            proxy()->drawControl(CE_RadioButtonLabel, &subopt, painter, widget);
1528
        }
1529
        break;
1530
1531
    case CE_CheckBox:
1532
        if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1533
            QStyleOptionButton bevelopt = *btn;
1534
            bevelopt.state &= ~State_On;
1535
            proxy()->drawControl(CE_PushButtonBevel, &bevelopt, painter, widget);
1536
            QStyleOptionButton subopt = *btn;
1537
            subopt.rect = subElementRect(SE_PushButtonContents, btn, widget);
1538
            QStyleOptionButton checkopt = subopt;
1539
            checkopt.rect = proxy()->subElementRect(SE_CheckBoxIndicator, &subopt, widget);
1540
            proxy()->drawPrimitive(PE_IndicatorCheckBox, &checkopt, painter, widget);
1541
            checkopt.rect = subElementRect(SE_CheckBoxContents, &subopt, widget);
1542
            proxy()->drawControl(CE_CheckBoxLabel, &checkopt, painter, widget);
1543
        }
1544
        break;
1545
1546
    case CE_PushButton:
1547
        if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1548
            proxy()->drawControl(CE_PushButtonBevel, btn, painter, widget);
1549
            QStyleOptionButton subopt = *btn;
1550
            subopt.rect = subElementRect(SE_PushButtonContents, btn, widget);
1551
            proxy()->drawControl(CE_PushButtonLabel, &subopt, painter, widget);
1552
        }
1553
        break;
1554
1555
    case CE_RadioButtonLabel: // fall through
1556
    case CE_PushButtonLabel:
1557
        if (const QStyleOptionMaemo5ValueButton *button = qstyleoption_cast<const QStyleOptionMaemo5ValueButton *>(option)) {
1558
            QRect ir = button->rect;
1559
            uint tf = Qt::AlignVCenter | Qt::TextHideMnemonic | Qt::AlignLeft;
1560
            int spacing = 8;
1561
            QRect textRect, valueRect, iconRect;
1562
1563
            if (!button->text.isEmpty())
1564
                textRect = ir;
1565
            if (!button->value.isEmpty())
1566
                valueRect = ir;
1567
            if (!button->icon.isNull()) {
1568
                iconRect = ir;
1569
                iconRect.setTop(ir.top() + (ir.height() - button->iconSize.height()) / 2);
1570
                iconRect.setWidth(button->iconSize.width());
1571
                iconRect.setHeight(button->iconSize.height());
1572
                textRect.setLeft(iconRect.right() + spacing);
1573
                valueRect.setLeft(textRect.left());
1574
            }
1575
1576
            QFont valuefont = painter->font();
1577
            QFontMetrics valuefm = button->fontMetrics;
1578
1579
            if (button->styles & QStyleOptionMaemo5ValueButton::ValueUnderText) {
1580
                valuefont = standardFont(QLS("SmallSystemFont"));
1581
                valuefm = QFontMetrics(valuefont);
1582
            }
1583
1584
            QSize textSize = button->fontMetrics.size(Qt::TextSingleLine, button->text);
1585
            QSize valueSize = valuefm.size(Qt::TextSingleLine, button->value);
1586
1587
            if (textRect.isValid())
1588
                textRect.setWidth(textSize.width());
1589
            if (valueRect.isValid())
1590
                valueRect.setWidth(valueSize.width());
1591
1592
            if (button->styles & QStyleOptionMaemo5ValueButton::ValueUnderText) {
1593
                if (valueRect.isValid() && textRect.isValid()) {
1594
                    int delta = (ir.height() - (textSize.height() + valueSize.height())) / 2;
1595
                    textRect.setTop(textRect.top() + delta);
1596
                    textRect.setHeight(textSize.height());
1597
                    valueRect.setTop(textRect.bottom() + 1);
1598
                    valueRect.setHeight(valueSize.height());
1599
                }
1600
                if (button->styles & QStyleOptionMaemo5ValueButton::Centered) {
1601
                    int deltaText = (textRect.width() - valueRect.width()) / 2;
1602
                    int offsetX = ir.left() + (ir.width() - ((iconRect.isValid() ? iconRect.width() + spacing : 0) + qMax(textRect.width(), valueRect.width()))) / 2;
1603
                    if (iconRect.isValid()) {
1604
                        iconRect.moveLeft(offsetX);
1605
                        offsetX += (iconRect.width() + spacing);
1606
                    }
1607
                    if (textRect.isValid())
1608
                        textRect.moveLeft(offsetX + (deltaText > 0 ? 0 : -deltaText));
1609
                    if (valueRect.isValid())
1610
                        valueRect.moveLeft(offsetX + (deltaText < 0 ? 0 : deltaText));
1611
                }
1612
            } else {
1613
                int middle = ir.center().x();
1614
                if (textRect.isValid() && valueRect.isValid()) {
1615
                    if ((textRect.right() + spacing) < middle)
1616
                        valueRect.moveLeft(middle);
1617
                    else
1618
                        valueRect.moveLeft(textRect.right() + spacing);
1619
                }
1620
            }
1621
            valueRect = visualRect(button->direction, ir, valueRect & ir);
1622
            textRect = visualRect(button->direction, ir, textRect & ir);
1623
            iconRect = visualRect(button->direction, ir, iconRect & ir);
1624
1625
            //qDebug() << "Icon, Text, Value: " << iconRect << textRect << valueRect;
1626
1627
            if (iconRect.isValid()) {
1628
                QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal
1629
                                                              : QIcon::Disabled;
1630
                QIcon::State state = QIcon::Off;
1631
                if (button->state & State_On)
1632
                    state = QIcon::On;
1633
1634
                QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state);
1635
                painter->drawPixmap(iconRect, pixmap);
1636
            }
1637
            if (textRect.isValid()) {
1638
                proxy()->drawItemText(painter, textRect, tf, button->palette, (button->state & State_Enabled),
1639
                                      button->text, QPalette::ButtonText);
1640
            }
1641
            if (valueRect.isValid()) {
1642
                QPalette pal = button->palette;
1643
                if (!(button->state & (State_On | State_Sunken)) && (button->state & State_Enabled)) {
1644
                    QColor color = standardColor(button->styles & QStyleOptionMaemo5ValueButton::PickButton ? QLS("ActiveTextColor") : QLS("SecondaryTextColor"));
1645
                    pal.setBrush(QPalette::ButtonText, color);
1646
                }
1647
                QFont oldfont = painter->font();
1648
                painter->setFont(valuefont);
1649
1650
                proxy()->drawItemText(painter, valueRect, tf, pal, (button->state & State_Enabled),
1651
                                      button->value, QPalette::ButtonText);
1652
                painter->setFont(oldfont);
1653
            }
1654
        } else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1655
            QRect ir = button->rect;
1656
            uint tf = Qt::AlignVCenter | Qt::TextHideMnemonic;
1657
1658
            if (!button->icon.isNull()) {
1659
                //Center both icon and text
1660
                QPoint point;
1661
1662
                QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal
1663
                                                              : QIcon::Disabled;
1664
                QIcon::State state = QIcon::Off;
1665
                if (button->state & State_On)
1666
                    state = QIcon::On;
1667
1668
                QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state);
1669
                int w = pixmap.width();
1670
                int h = pixmap.height();
1671
1672
                if (!button->text.isEmpty())
1673
                    w += button->fontMetrics.boundingRect(option->rect, tf, button->text).width() + 2;
1674
1675
                point = QPoint(ir.x() + ir.width() / 2 - w / 2,
1676
                               ir.y() + ir.height() / 2 - h / 2);
1677
1678
                if (button->direction == Qt::RightToLeft)
1679
                    point.rx() += pixmap.width();
1680
1681
                painter->drawPixmap(visualPos(button->direction, button->rect, point), pixmap);
1682
1683
                if (button->direction == Qt::RightToLeft)
1684
                    ir.translate(-point.x() - 2, 0);
1685
                else
1686
                    ir.translate(point.x() + pixmap.width(), 0);
1687
1688
                // left-align text if there is
1689
                if (!button->text.isEmpty())
1690
                    tf |= Qt::AlignLeft;
1691
1692
            } else {
1693
                tf |= Qt::AlignHCenter;
1694
            }
1695
1696
            if (button->features & QStyleOptionButton::HasMenu)
1697
                ir = ir.adjusted(0, 0, -pixelMetric(PM_MenuButtonIndicator, button, widget), 0);
1698
            proxy()->drawItemText(painter, ir, tf, button->palette, (button->state & State_Enabled),
1699
                         button->text, QPalette::ButtonText);
1700
        }
1701
        break;
1702
1703
    case CE_ComboBoxLabel:
1704
        if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
1705
            if (!combo->editable) {
1706
                QStyleOptionMaemo5ValueButton button;
1707
                button.QStyleOption::operator=(*option); // copy QStyleOption members
1708
                button.features = QStyleOptionButton::None;
1709
                button.icon = combo->currentIcon;
1710
                button.iconSize = combo->iconSize;
1711
                button.text = QString();
1712
                button.styles = QStyleOptionMaemo5ValueButton::ValueBesideText | QStyleOptionMaemo5ValueButton::PickButton;
1713
                button.value = combo->currentText;
1714
                button.rect = proxy()->subElementRect(QStyle::SE_PushButtonContents, &button, widget);
1715
1716
                proxy()->drawControl(CE_PushButtonLabel, &button, painter, widget);
1717
            } else {
1718
                QRect editRect = proxy()->subControlRect(CC_ComboBox, combo, SC_ComboBoxEditField, widget);
1719
                painter->save();
1720
                painter->setClipRect(editRect);
1721
1722
                if (!combo->currentIcon.isNull()) {
1723
                    QIcon::Mode mode = combo->state & State_Enabled ? QIcon::Normal
1724
                                       : QIcon::Disabled;
1725
                    QPixmap pixmap = combo->currentIcon.pixmap(combo->iconSize, mode);
1726
                    QRect iconRect(editRect);
1727
                    iconRect.setWidth(combo->iconSize.width() + 4);
1728
1729
                    iconRect = alignedRect(combo->direction,
1730
                                           Qt::AlignLeft | Qt::AlignVCenter,
1731
                                           iconRect.size(), editRect);
1732
1733
                    proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap);
1734
1735
                    if (combo->direction == Qt::RightToLeft)
1736
                        editRect.translate(-4 - combo->iconSize.width(), 0);
1737
                    else
1738
                        editRect.translate(combo->iconSize.width() + 4, 0);
1739
                }
1740
                painter->restore();
1741
            }
1742
        }
1743
        break;
1744
1745
    case CE_Splitter:
1746
        painter->save();
1747
        {
1748
            QColor gripShadow(0xffffff);
1749
            gripShadow.setAlpha(190);
1750
            bool vertical = !(option->state & State_Horizontal);
1751
            QRect scrollBarSlider = option->rect;
1752
            int gripMargin = 5;
1753
            int size = 15;
1754
            //draw grips
1755
            if (vertical) {
1756
                for( int i = -size; i< size ; i += 4) {
1757
                    painter->setPen(QPen(gripShadow, 2));
1758
                    painter->drawLine(
1759
                        QPoint(scrollBarSlider.center().x() + i ,
1760
                               scrollBarSlider.top() + gripMargin),
1761
                        QPoint(scrollBarSlider.center().x() + i,
1762
                               scrollBarSlider.bottom() - gripMargin));
1763
                }
1764
            } else {
1765
                for (int i = -size; i < size ; i += 4) {
1766
                    painter->setPen(QPen(gripShadow, 2));
1767
                    painter->drawLine(
1768
                        QPoint(scrollBarSlider.left() + gripMargin ,
1769
                               scrollBarSlider.center().y()+ i),
1770
                        QPoint(scrollBarSlider.right() - gripMargin,
1771
                               scrollBarSlider.center().y()+ i));
1772
                }
1773
            }
1774
        }
1775
        painter->restore();
1776
        break;
1777
1778
    default:
1779
        QGtkStyle::drawControl(element, option, painter, widget);
1780
        break;
1781
    }
1782
}
1783
1784
/*!
1785
    \reimp
1786
*/
1787
QRect QMaemo5Style::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
1788
                                   SubControl subControl, const QWidget *widget) const
1789
{
1790
    Q_D(const QMaemo5Style);
1791
    QRect rect;
1792
    switch (control) {
1793
1794
    case CC_SpinBox:
1795
        if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
1796
            int x = spinbox->rect.x();
1797
            int y = spinbox->rect.y();
1798
            int h = spinbox->rect.height();
1799
            int w = spinbox->rect.width();
1800
            int buttonSize = 0, spacing = 0;
1801
            if (spinbox->buttonSymbols != QAbstractSpinBox::NoButtons)
1802
                buttonSize = 54;
1803
1804
            int frameOffset = buttonSize + spacing;
1805
1806
            GtkBorder *border = 0;
1807
            GtkWidget *gtkEntry = d->gtkWidget("HildonNumberEditor.GtkEntry");
1808
            d->gtk_widget_style_get(gtkEntry, "inner-border", &border, NULL);
1809
            int xframe = border->left + gtkEntry->style->xthickness + 10;
1810
            int yframe = border->top + gtkEntry->style->ythickness + 12;
1811
            d->gtk_border_free(border);
1812
1813
            switch (subControl) {
1814
            case SC_SpinBoxFrame:
1815
                rect = spinbox->rect;
1816
                break;
1817
1818
            case SC_SpinBoxEditField:
1819
                rect = QRect(x + frameOffset + xframe, y + yframe,
1820
                             w - 2 * (frameOffset + xframe),
1821
                             h - 2 * yframe);
1822
                break;
1823
1824
            case SC_SpinBoxUp:
1825
                rect = QRect(x + w - buttonSize, y, buttonSize, h);
1826
                break;
1827
1828
            case SC_SpinBoxDown:
1829
                rect = QRect(x, y, buttonSize, h);
1830
                break;
1831
1832
            default:
1833
                break;
1834
1835
            }
1836
            rect = visualRect(spinbox->direction, spinbox->rect, rect);
1837
        }
1838
        break;
1839
    case CC_ScrollBar:
1840
        if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
1841
            const QRect scrollBarRect = scrollbar->rect;
1842
            int maxlen = ((scrollbar->orientation == Qt::Horizontal) ?
1843
                          scrollBarRect.width() : scrollBarRect.height());
1844
            int sliderlen;
1845
1846
            // calculate slider length
1847
            if (scrollbar->maximum != scrollbar->minimum) {
1848
                uint range = scrollbar->maximum - scrollbar->minimum;
1849
                sliderlen = (qint64(scrollbar->pageStep) * maxlen) / (range + scrollbar->pageStep);
1850
1851
                int slidermin = proxy()->pixelMetric(PM_ScrollBarSliderMin, scrollbar, widget);
1852
                if (sliderlen < slidermin || range > INT_MAX / 2)
1853
                    sliderlen = slidermin;
1854
                if (sliderlen > maxlen)
1855
                    sliderlen = maxlen;
1856
            } else {
1857
                sliderlen = maxlen;
1858
            }
1859
1860
            int sliderstart = sliderPositionFromValue(scrollbar->minimum,
1861
                                                      scrollbar->maximum,
1862
                                                      scrollbar->sliderPosition,
1863
                                                      maxlen - sliderlen,
1864
                                                      scrollbar->upsideDown);
1865
1866
            switch (subControl) {
1867
            case SC_ScrollBarSubLine:            // top/left button
1868
            case SC_ScrollBarAddLine:            // bottom/right button
1869
                rect = QRect();
1870
                break;
1871
            case SC_ScrollBarSubPage:            // between top/left button and slider
1872
                if (scrollbar->orientation == Qt::Horizontal)
1873
                    rect.setRect(0, 0, sliderstart, scrollBarRect.height());
1874
                else
1875
                    rect.setRect(0, 0, scrollBarRect.width(), sliderstart);
1876
                break;
1877
            case SC_ScrollBarAddPage:            // between bottom/right button and slider
1878
                if (scrollbar->orientation == Qt::Horizontal)
1879
                    rect.setRect(sliderstart + sliderlen, 0,
1880
                              maxlen - sliderstart - sliderlen, scrollBarRect.height());
1881
                else
1882
                    rect.setRect(0, sliderstart + sliderlen, scrollBarRect.width(),
1883
                              maxlen - sliderstart - sliderlen);
1884
                break;
1885
            case SC_ScrollBarGroove:
1886
                rect = scrollBarRect;
1887
                break;
1888
            case SC_ScrollBarSlider:
1889
                if (scrollbar->orientation == Qt::Horizontal)
1890
                    rect.setRect(sliderstart, 0, sliderlen, scrollBarRect.height());
1891
                else
1892
                    rect.setRect(0, sliderstart, scrollBarRect.width(), sliderlen);
1893
                break;
1894
            default:
1895
                break;
1896
            }
1897
            rect = visualRect(scrollbar->direction, scrollBarRect, rect);
1898
        }
1899
        break;
1900
1901
    case CC_ComboBox:
1902
        if (const QStyleOptionComboBox *box = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
1903
            if (!box->editable) {
1904
                switch (subControl) {
1905
                case SC_ComboBoxArrow:
1906
                    rect = QRect(option->rect.topLeft(), QSize(0, 0));
1907
                    break;
1908
                default:
1909
                    rect = option->rect;
1910
                    break;
1911
                }
1912
                break;
1913
            }
1914
1915
            // We employ the gtk widget to position arrows and separators for us
1916
            GtkWidget *gtkCombo = d->gtkWidget("hildon-combobox");
1917
            d->gtk_widget_set_direction(gtkCombo, (option->direction == Qt::RightToLeft) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR);
1918
            GtkAllocation geometry = {0, 0, qMax(0, option->rect.width()), qMax(0, option->rect.height())};
1919
            d->gtk_widget_size_allocate(gtkCombo, &geometry);
1920
1921
            GtkWidget *arrowWidget = d->gtkWidget("hildon-combobox.hildon-combobox-button");
1922
            GtkWidget *entryWidget = d->gtkWidget("hildon-combobox.HildonEntry-finger");
1923
            if (!arrowWidget || !entryWidget)
1924
                return QCleanlooksStyle::subControlRect(control, option, subControl, widget);
1925
1926
            QRect buttonRect(option->rect.left() + arrowWidget->allocation.x,
1927
                             option->rect.top() + arrowWidget->allocation.y,
1928
                             arrowWidget->allocation.width, arrowWidget->allocation.height);
1929
1930
            switch (subControl) {
1931
1932
            case SC_ComboBoxArrow: // Note: this indicates the arrowbutton for editable combos
1933
                rect = buttonRect;
1934
                break;
1935
1936
            case SC_ComboBoxEditField: {
1937
                rect = visualRect(option->direction, option->rect, rect);
1938
                int xMargin = 8, yMargin = 12;
1939
                rect.setRect(option->rect.left() + entryWidget->style->xthickness + xMargin,
1940
                             option->rect.top()  + entryWidget->style->ythickness + yMargin,
1941
                             option->rect.width() - buttonRect.width() - 2*(entryWidget->style->xthickness + xMargin),
1942
                             option->rect.height() - 2*(entryWidget->style->ythickness + yMargin));
1943
                rect = visualRect(option->direction, option->rect, rect);
1944
                break;
1945
            }
1946
1947
            default:
1948
                rect = QWindowsStyle::subControlRect(control, option, subControl, widget);
1949
                break;
1950
            }
1951
        }
1952
1953
        break;
1954
1955
    default:
1956
        rect = QGtkStyle::subControlRect(control, option, subControl, widget);
1957
        break;
1958
    }
1959
    return rect;
1960
}
1961
1962
/*!
1963
    \reimp
1964
*/
1965
QSize QMaemo5Style::sizeFromContents(ContentsType type, const QStyleOption *option,
1966
                                  const QSize &size, const QWidget *widget) const
1967
{
1968
    Q_D(const QMaemo5Style);
1969
1970
    QSize newSize = QGtkStyle::sizeFromContents(type, option, size, widget);
1971
    if (!d->isThemeAvailable())
1972
        return newSize;
1973
1974
    GtkWidget *gtkWidget = 0;
1975
1976
    switch (type) {
1977
    case CT_ToolButton:
1978
        if (const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
1979
            // tool buttons are always at least as high as the icon (even if it only has a text)
1980
            // the 78/70 is a hack, since these values are also hardcoded in Hildon
1981
            newSize = newSize.expandedTo(toolbutton->iconSize + QSize(12, 12)).expandedTo(QSize(78, 70));
1982
        }
1983
        break;
1984
1985
    case CT_LineEdit: {
1986
        if (const QStyleOptionFrameV2 *frame = qstyleoption_cast<const QStyleOptionFrameV2 *>(option)) {
1987
            if (!frame->lineWidth)
1988
                return QCleanlooksStyle::sizeFromContents(type, option, size, widget);
1989
1990
            gtkWidget = d->gtkWidget("HildonEntry-finger");
1991
            if (gtkWidget) {
1992
                newSize = size + 2 * QSize(gtkWidget->style->xthickness, 1 + gtkWidget->style->ythickness);
1993
                GtkBorder *border = 0;
1994
                d->gtk_widget_style_get(gtkWidget, "inner-border", &border, NULL);
1995
                if (border) {
1996
                    newSize += QSize(border->left + border->right, border->top + border->bottom + 3);
1997
                    d->gtk_border_free(border);
1998
                } else {
1999
                    newSize += QSize(4, 7); // default inner-border
2000
                }
2001
                //The widget size should not be adjusted automaticly if it is too small
2002
                //otherwise the tst_QLineEdit::textMargin() fails
2003
                gtkWidget = 0;
2004
            }
2005
        }
2006
        break;
2007
    }
2008
2009
    case CT_ComboBox: {
2010
        if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
2011
            if (!combo->editable) {
2012
                QStyleOptionButton button;
2013
                button.QStyleOption::operator=(*option); // copy QStyleOption members
2014
                button.features = QStyleOptionButton::None;
2015
                button.icon = combo->currentIcon;
2016
                button.iconSize = combo->iconSize;
2017
                button.text = combo->currentText;
2018
2019
                newSize = proxy()->sizeFromContents(CT_PushButton, &button, size, widget);
2020
            } else {
2021
                GtkWidget *gtkCombo = d->gtkWidget("hildon-combobox.HildonEntry-finger");
2022
                QRect arrowButtonRect = proxy()->subControlRect(CC_ComboBox, combo, SC_ComboBoxArrow, widget);
2023
                newSize = size + QSize(12 + arrowButtonRect.width() + 2*gtkCombo->style->xthickness, 4 + 2*gtkCombo->style->ythickness);
2024
                newSize = newSize.expandedTo(QSize(0, 70));
2025
            }
2026
        }
2027
        break;
2028
    }
2029
2030
    case CT_PushButton: {
2031
        if (widget && qobject_cast<QDialogButtonBox *>(widget->parentWidget()))
2032
            newSize.setWidth(qMax(newSize.width(), 174)); // hardcoded value in gtkdialog.c
2033
        gtkWidget = d->gtkWidget("HildonButton-finger");
2034
        break;
2035
    }
2036
2037
    case CT_RadioButton:
2038
    case CT_CheckBox: {
2039
        gtkWidget = d->gtkWidget("HildonButton-finger");
2040
        if (gtkWidget)
2041
        {
2042
            gint w = -1, h = -1;
2043
            d->gtk_widget_get_size_request(gtkWidget, &w, &h);
2044
            newSize = newSize.expandedTo(QSize(w, h));
2045
2046
            GtkBorder *border = 0;
2047
            d->gtk_widget_style_get(gtkWidget, "inner-border", &border, NULL);
2048
            if (border)
2049
            {
2050
                newSize += QSize(border->left + border->right,0);
2051
                d->gtk_border_free(border);
2052
            }
2053
            gtkWidget = 0;
2054
        }
2055
        break;
2056
    }
2057
2058
    case CT_MenuItem: {
2059
        if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
2060
            int textMargin = 8;
2061
2062
            if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) {
2063
                GtkWidget *gtkMenuSeparator = d->gtkWidget("hildon-context-sensitive-menu.GtkSeparatorMenuItem");
2064
                GtkRequisition sizeReq = {0, 0};
2065
                d->gtk_widget_size_request(gtkMenuSeparator, &sizeReq);
2066
                newSize = QSize(size.width(), sizeReq.height);
2067
                break;
2068
            }
2069
2070
            GtkWidget *gtkMenuItem = d->gtkWidget("hildon-context-sensitive-menu.GtkCheckMenuItem");
2071
            GtkStyle* style = gtkMenuItem->style;
2072
2073
            // Note we get the perfect height for the default font since we
2074
            // set a fake text label on the gtkMenuItem
2075
            // But if custom fonts are used on the widget we need a minimum size
2076
            GtkRequisition sizeReq = {0, 0};
2077
            d->gtk_widget_size_request(gtkMenuItem, &sizeReq);
2078
            newSize.setHeight(qMax(newSize.height() - 4, sizeReq.height));
2079
            newSize += QSize(textMargin + style->xthickness - 1, 0);
2080
2081
            // Cleanlooks assumes a check column of 20 pixels so we need to
2082
            // expand it a bit
2083
            gint checkSize;
2084
            d->gtk_widget_style_get(gtkMenuItem, "indicator-size", &checkSize, NULL);
2085
            newSize.setWidth(newSize.width() + qMax(0, checkSize - 20));
2086
        }
2087
        break;
2088
    }
2089
#ifndef QT_NO_ITEMVIEWS
2090
    case CT_ItemViewItem: {
2091
         uint rowHeight = 70;
2092
         if (GtkWidget *gtkTreeView = d->gtkWidget("HildonPannableArea.GtkTreeView"))
2093
             d->gtk_widget_style_get(gtkTreeView, "row-height", &rowHeight, NULL);
2094
         newSize = newSize.expandedTo(QSize(0, rowHeight));
2095
         break;
2096
    }
2097
#endif // QT_NO_ITEMVIEWS
2098
    case CT_ProgressBar: {
2099
        gint minw = 70, minh = 70;
2100
        if (GtkWidget *gtkProgressBar = d->gtkWidget("GtkProgressBar")) {
2101
            d->gtk_widget_style_get(gtkProgressBar,
2102
                                   "min-vertical-bar-width",    &minw,
2103
                                   "min-horizontal-bar-height", &minh,
2104
                                   NULL);
2105
        }
2106
        newSize = newSize.expandedTo(QSize(minw, minh));
2107
        break;
2108
    }
2109
2110
    case CT_Slider: {
2111
        if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
2112
            // I have no idea why the Gtk widget ends up being 70px high given the Hildon gtkrc and the gtkscale source code...
2113
            QSize expandFix(0, 70);
2114
            if (slider->orientation == Qt::Horizontal) {
2115
                gtkWidget = d->gtkWidget("GtkHScale");
2116
            } else {
2117
                gtkWidget = d->gtkWidget("GtkVScale");
2118
                expandFix.transpose();
2119
            }
2120
            newSize = newSize.expandedTo(expandFix);
2121
        }
2122
        break;
2123
    }
2124
2125
    default:
2126
        break;
2127
    }
2128
2129
    if (gtkWidget) {
2130
        gint w = -1, h = -1;
2131
        d->gtk_widget_get_size_request(gtkWidget, &w, &h);
2132
        newSize = newSize.expandedTo(QSize(w, h));
2133
    }
2134
    return newSize;
2135
}
2136
2137
/*!
2138
    \reimp
2139
*/
2140
QRect QMaemo5Style::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
2141
{
2142
    Q_D(const QMaemo5Style);
2143
2144
    QRect r;
2145
    switch (element) {
2146
    case SE_TreeViewDisclosureItem:
2147
        // Increases the actual expander hitrect for more finger friendliness
2148
        r = option->rect.adjusted(0, 0, 20, 0);
2149
        break;
2150
2151
    case SE_LineEditContents:
2152
        if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
2153
            if (!frame->lineWidth)
2154
                return QCleanlooksStyle::subElementRect(element, option, widget);
2155
        }
2156
        r = option->rect;
2157
        if (GtkWidget *gtkEntry = d->gtkWidget("HildonEntry-finger")) {
2158
            int xt = gtkEntry->style->xthickness;
2159
            int yt = gtkEntry->style->ythickness;
2160
2161
            r.adjust(xt, yt, -xt, -yt);
2162
2163
            GtkBorder *border = 0;
2164
            d->gtk_widget_style_get(gtkEntry, "inner-border", &border, NULL);
2165
            if (border) {
2166
                r.adjust(border->left, border->top, -border->right, -border->bottom);
2167
                d->gtk_border_free(border);
2168
            } else {
2169
                r.adjust(2, 2, -2, -2); // default inner-border
2170
            }
2171
        }
2172
        r = visualRect(option->direction, option->rect, r);
2173
        break;
2174
2175
    case SE_CheckBoxIndicator: {
2176
        int h = proxy()->pixelMetric(PM_IndicatorHeight, option, widget);
2177
        r.setRect(option->rect.x(), option->rect.y() + ((option->rect.height() - h) / 2),
2178
                  proxy()->pixelMetric(PM_IndicatorWidth, option, widget), h);
2179
        r = visualRect(option->direction, option->rect, r);
2180
        break;
2181
    }
2182
    case SE_RadioButtonContents:
2183
        r = proxy()->subElementRect(SE_PushButtonContents, option, widget);
2184
        break;
2185
2186
    case SE_RadioButtonClickRect: // fall through
2187
    case SE_CheckBoxClickRect:
2188
        r = visualRect(option->direction, option->rect, option->rect);
2189
        break;
2190
2191
    case SE_FrameContents:
2192
        if ((qobject_cast<const QTextEdit *>(widget) && !qobject_cast<const QTextBrowser *>(widget)) || qobject_cast<const QPlainTextEdit *>(widget))
2193
            r = proxy()->subElementRect(SE_LineEditContents, option, widget);
2194
        else
2195
            r = QGtkStyle::subElementRect(element, option, widget);
2196
        break;
2197
2198
    default:
2199
        r = QGtkStyle::subElementRect(element, option, widget);
2200
    }
2201
    return r;
2202
}
2203
2204
/*!
2205
    \reimp
2206
*/
2207
QStyle::SubControl QMaemo5Style::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
2208
                              const QPoint &pt, const QWidget *w) const
2209
{
2210
    switch (cc) {
2211
    case CC_ScrollBar:
2212
        return SC_None;
2213
    default:
2214
        return QGtkStyle::hitTestComplexControl(cc, opt, pt, w);
2215
    }
2216
}
2217
2218
/*!
2219
    Returns a font for the given \a logicalFontName.
2220
2221
    Please see the Maemo 5 style guides for a list of valid logical font
2222
    names.  If the named font cannot be found, the function returns the
2223
    application's default font.
2224
2225
    \sa standardColor()
2226
*/
2227
QFont QMaemo5Style::standardFont(const QString &logicalFontName)
2228
{
2229
    return QGtkStylePrivate::getThemeFont(logicalFontName);
2230
}
2231
2232
/*!
2233
    Returns a color for the given \a logicalColorName.
2234
2235
    Please see the Maemo 5 style guides for a list of valid logical color
2236
    names.  If the named color cannot be found, the function returns an
2237
    invalid color.
2238
2239
    \sa standardFont()
2240
*/
2241
QColor QMaemo5Style::standardColor(const QString &logicalColorName)
2242
{
2243
    if (GtkStyle *style = QGtkStylePrivate::gtkStyle()) {
2244
        GdkColor color;
2245
        if (QMaemo5StylePrivate::gtk_style_lookup_color(style, logicalColorName.toUtf8().constData(), &color))
2246
            return QColor(color.red>>8, color.green>>8, color.blue>>8);
2247
    }
2248
    return QColor();
2249
}
2250
2251
2252
/*!
2253
    \enum QMaemo5Style::PrimitiveElement
2254
    \since 4.6
2255
2256
    This enum contains additional Maemo 5 specific PrimitiveElement entries.
2257
2258
    \value PE_Maemo5InformationBox Represents a Maemo 5 information box overlay.
2259
2260
    \value PE_Maemo5AppMenu        Represents a Maemo 5 application menu frame.
2261
2262
    \value PE_Maemo5EditBar        Represents the special Maemo 5 edit bar pop-up background.
2263
2264
    \value PE_Maemo5EditBarBackButton  Represents the back button for the edit bar.
2265
2266
    \sa QStyle::PrimitiveElement
2267
*/
2268
2269
/*!
2270
    \enum QMaemo5Style::PixelMetric
2271
    \since 4.6
2272
2273
    This enum contains additional Maemo 5 specific PixelMetric entries.
2274
2275
    \value PM_Maemo5AppMenuHorizontalSpacing The horizontal space between entries inside the Maemo 5 application menu.
2276
2277
    \value PM_Maemo5AppMenuVerticalSpacing   The vertical space between entries inside the Maemo 5 application menu.
2278
2279
    \value PM_Maemo5AppMenuContentMargin     The space around the Maemo 5 app menu content.
2280
2281
    \value PM_Maemo5AppMenuLandscapeXOffset  The space from the edge of the screen to the menu border.
2282
2283
    \value PM_Maemo5AppMenuFilterGroupWidth   The width of the Maemo5 menu filter group.
2284
2285
    \value PM_Maemo5AppMenuFilterGroupVerticalSpacing The space between the filter group and the rest of the Maemo5 menu.
2286
2287
    \value PM_Maemo5EditBarBackButtonWidth   The width of the Maemo5 edit bar back button.
2288
2289
    \value PM_Maemo5EditBarBackButtonHeight  The height of the Maemo5 edit bar back button.
2290
2291
    \sa QStyle::PixelMetric
2292
*/
2293
2294
/*! \internal */
2295
QIcon QMaemo5Style::standardIconImplementation(StandardPixmap standardIcon,
2296
        const QStyleOption *option, const QWidget *widget) const
2297
{
2298
    return standardPixmap(standardIcon, option, widget);
2299
}
2300
2301
/*! \reimp */
2302
QPixmap QMaemo5Style::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
2303
                       const QWidget *widget) const
2304
{
2305
    switch (standardPixmap) {
2306
    case SP_MediaPlay:
2307
        return QPixmap(QLatin1String("/etc/hildon/theme/mediaplayer/Play.png"));
2308
    case SP_MediaStop:
2309
        return QPixmap(QLatin1String("/etc/hildon/theme/mediaplayer/Stop.png"));
2310
    case SP_MediaPause:
2311
        return QPixmap(QLatin1String("/etc/hildon/theme/mediaplayer/Pause.png"));
2312
    case SP_MediaSkipForward:
2313
        return QPixmap(QLatin1String("/etc/hildon/theme/mediaplayer/Forward.png"));
2314
    case SP_MediaSkipBackward:
2315
        return QPixmap(QLatin1String("/etc/hildon/theme/mediaplayer/Back.png"));
2316
    default:
2317
        return QGtkStyle::standardPixmap(standardPixmap, opt, widget);
2318
    }
2319
}
2320
2321
QT_END_NAMESPACE
2322
2323
#endif