1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL21$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** $QT_END_LICENSE$
31 **
32 ****************************************************************************/
33 
34 #include "qgtkstyle_p_p.h"
35 
36 // This file is responsible for resolving all GTK functions we use
37 // dynamically. This is done to avoid link-time dependency on GTK
38 // as well as crashes occurring due to usage of the GTK_QT engines
39 //
40 // Additionally we create a map of common GTK widgets that we can pass
41 // to the GTK theme engine as many engines resort to querying the
42 // actual widget pointers for details that are not covered by the
43 // state flags
44 
45 #include <QtCore/qglobal.h>
46 
47 #include <QtCore/QEvent>
48 #include <QtCore/QFile>
49 #include <QtCore/QStringList>
50 #include <QtCore/QTextStream>
51 #include <QtCore/QHash>
52 #include <QtCore/QUrl>
53 #include <QtCore/QDebug>
54 
55 #include "qgtk2painter_p.h"
56 #include <private/qapplication_p.h>
57 #include <private/qiconloader_p.h>
58 #include <qpa/qplatformfontdatabase.h>
59 
60 #include <QtWidgets/QMenu>
61 #include <QtWidgets/QStyle>
62 #include <QtWidgets/QApplication>
63 #include <QtGui/QPixmapCache>
64 #include <QtWidgets/QStatusBar>
65 #include <QtWidgets/QMenuBar>
66 #include <QtWidgets/QToolBar>
67 #include <QtWidgets/QToolButton>
68 
69 // X11 Includes:
70 
71 // the following is necessary to work around breakage in many versions
72 // of XFree86's Xlib.h still in use
73 // ### which versions?
74 #if defined(_XLIB_H_) // crude hack, but...
75 #error "cannot include <X11/Xlib.h> before this file"
76 #endif
77 #define XRegisterIMInstantiateCallback qt_XRegisterIMInstantiateCallback
78 #define XUnregisterIMInstantiateCallback qt_XUnregisterIMInstantiateCallback
79 #define XSetIMValues qt_XSetIMValues
80 #include <X11/Xlib.h>
81 #undef XRegisterIMInstantiateCallback
82 #undef XUnregisterIMInstantiateCallback
83 #undef XSetIMValues
84 
85 QT_BEGIN_NAMESPACE
86 
87 Q_GLOBAL_STATIC(QGtkStyleUpdateScheduler, styleScheduler)
88 
89 Ptr_ubuntu_gtk_set_use_overlay_scrollbar QGtkStylePrivate::ubuntu_gtk_set_use_overlay_scrollbar = 0;
90 
91 typedef int (*x11ErrorHandler)(Display*, XErrorEvent*);
92 
93 QT_END_NAMESPACE
94 
95 Q_DECLARE_METATYPE(QGtkStylePrivate*);
96 
97 QT_BEGIN_NAMESPACE
98 
gtkStyleSetCallback(GtkWidget *)99 static void gtkStyleSetCallback(GtkWidget*)
100 {
101     qRegisterMetaType<QGtkStylePrivate *>();
102 
103     // We have to let this function return and complete the event
104     // loop to ensure that all gtk widgets have been styled before
105     // updating
106     QMetaObject::invokeMethod(styleScheduler(), "updateTheme", Qt::QueuedConnection);
107 }
108 
update_toolbar_style(GtkWidget * gtkToolBar,GParamSpec *,gpointer)109 static void update_toolbar_style(GtkWidget *gtkToolBar, GParamSpec *, gpointer)
110 {
111     GtkToolbarStyle toolbar_style = GTK_TOOLBAR_ICONS;
112     g_object_get(gtkToolBar, "toolbar-style", &toolbar_style, NULL);
113     QWidgetList widgets = QApplication::allWidgets();
114     for (int i = 0; i < widgets.size(); ++i) {
115         QWidget *widget = widgets.at(i);
116         if (qobject_cast<QToolButton*>(widget)) {
117             QEvent event(QEvent::StyleChange);
118             QApplication::sendEvent(widget, &event);
119         }
120     }
121 }
122 
classPath(GtkWidget * widget)123 static QHashableLatin1Literal classPath(GtkWidget *widget)
124 {
125     char *class_path;
126     gtk_widget_path (widget, NULL, &class_path, NULL);
127 
128     char *copy = class_path;
129     if (strncmp(copy, "GtkWindow.", 10) == 0)
130         copy += 10;
131     if (strncmp(copy, "GtkFixed.", 9) == 0)
132         copy += 9;
133 
134     copy = strdup(copy);
135 
136     g_free(class_path);
137 
138     return QHashableLatin1Literal::fromData(copy);
139 }
140 
141 
142 
eventFilter(QObject * obj,QEvent * e)143 bool QGtkStyleFilter::eventFilter(QObject *obj, QEvent *e)
144 {
145     if (e->type() == QEvent::ApplicationPaletteChange) {
146         // Only do this the first time since this will also
147         // generate applicationPaletteChange events
148         //if (!qt_app_palettes_hash() ||  qt_app_palettes_hash()->isEmpty()) {
149         //    stylePrivate->applyCustomPaletteHash();
150         //}
151     }
152     return QObject::eventFilter(obj, e);
153 }
154 
155 QList<QGtkStylePrivate *> QGtkStylePrivate::instances;
156 QGtkStylePrivate::WidgetMap *QGtkStylePrivate::widgetMap = 0;
157 
QGtkStylePrivate()158 QGtkStylePrivate::QGtkStylePrivate()
159   : QCommonStylePrivate()
160   , filter(this)
161 {
162     instances.append(this);
163     animationFps = 60;
164 }
165 
~QGtkStylePrivate()166 QGtkStylePrivate::~QGtkStylePrivate()
167 {
168     instances.removeOne(this);
169 }
170 
init()171 void QGtkStylePrivate::init()
172 {
173     initGtkWidgets();
174 }
175 
gtkPainter(QPainter * painter)176 QGtkPainter* QGtkStylePrivate::gtkPainter(QPainter *painter)
177 {
178     // TODO: choose between gtk2 and gtk3
179     static QGtk2Painter instance;
180     instance.reset(painter);
181     return &instance;
182 }
183 
gtkWidget(const QHashableLatin1Literal & path)184 GtkWidget* QGtkStylePrivate::gtkWidget(const QHashableLatin1Literal &path)
185 {
186     GtkWidget *widget = gtkWidgetMap()->value(path);
187     if (!widget) {
188         // Theme might have rearranged widget internals
189         widget = gtkWidgetMap()->value(path);
190     }
191     return widget;
192 }
193 
gtkStyle(const QHashableLatin1Literal & path)194 GtkStyle* QGtkStylePrivate::gtkStyle(const QHashableLatin1Literal &path)
195 {
196     if (GtkWidget *w = gtkWidgetMap()->value(path))
197         return gtk_widget_get_style(w);
198     return 0;
199 }
200 
gtkWidgetSetFocus(GtkWidget * widget,bool focus)201 void QGtkStylePrivate::gtkWidgetSetFocus(GtkWidget *widget, bool focus)
202 {
203     GdkEvent *event = gdk_event_new(GDK_FOCUS_CHANGE);
204     event->focus_change.type = GDK_FOCUS_CHANGE;
205     event->focus_change.in = focus;
206     gtk_widget_send_focus_change(widget, event);
207     gdk_event_free(event);
208 }
209 
210 /* \internal
211  * Initializes a number of gtk menu widgets.
212  * The widgets are cached.
213  */
initGtkMenu() const214 void QGtkStylePrivate::initGtkMenu() const
215 {
216     // Create menubar
217     GtkWidget *gtkMenuBar = gtk_menu_bar_new();
218     setupGtkWidget(gtkMenuBar);
219 
220     GtkWidget *gtkMenuBarItem = gtk_menu_item_new_with_label("X");
221     gtk_menu_shell_append((GtkMenuShell*)(gtkMenuBar), gtkMenuBarItem);
222     gtk_widget_realize(gtkMenuBarItem);
223 
224     // Create menu
225     GtkWidget *gtkMenu = gtk_menu_new();
226     gtk_menu_item_set_submenu((GtkMenuItem*)(gtkMenuBarItem), gtkMenu);
227     gtk_widget_realize(gtkMenu);
228 
229     GtkWidget *gtkMenuItem = gtk_menu_item_new_with_label("X");
230     gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuItem);
231     gtk_widget_realize(gtkMenuItem);
232 
233     GtkWidget *gtkCheckMenuItem = gtk_check_menu_item_new_with_label("X");
234     gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkCheckMenuItem);
235     gtk_widget_realize(gtkCheckMenuItem);
236 
237     GtkWidget *gtkMenuSeparator = gtk_separator_menu_item_new();
238     gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuSeparator);
239 
240     addAllSubWidgets(gtkMenuBar);
241     addAllSubWidgets(gtkMenu);
242 }
243 
244 
initGtkTreeview() const245 void QGtkStylePrivate::initGtkTreeview() const
246 {
247     GtkWidget *gtkTreeView = gtk_tree_view_new();
248     gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new());
249     gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new());
250     gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new());
251     addWidget(gtkTreeView);
252 }
253 
254 
255 /* \internal
256  * Initializes a number of gtk widgets that we can later on use to determine some of our styles.
257  * The widgets are cached.
258  */
initGtkWidgets() const259 void QGtkStylePrivate::initGtkWidgets() const
260 {
261     // From gtkmain.c
262     uid_t ruid = getuid ();
263     uid_t rgid = getgid ();
264     uid_t euid = geteuid ();
265     uid_t egid = getegid ();
266     if (ruid != euid || rgid != egid) {
267         qWarning("\nThis process is currently running setuid or setgid.\nGTK+ does not allow this "
268                  "therefore Qt cannot use the GTK+ integration.\nTry launching your app using \'gksudo\', "
269                  "\'kdesudo\' or a similar tool.\n\n"
270                  "See http://www.gtk.org/setuid.html for more information.\n");
271         return;
272     }
273 
274     // Gtk will set the Qt error handler so we have to reset it afterwards
275     x11ErrorHandler qt_x_errhandler = XSetErrorHandler(0);
276     gtk_init (NULL, NULL);
277     XSetErrorHandler(qt_x_errhandler);
278 
279     ubuntu_gtk_set_use_overlay_scrollbar = (Ptr_ubuntu_gtk_set_use_overlay_scrollbar)QLibrary::resolve(QLS("gtk-x11-2.0"), "ubuntu_gtk_set_use_overlay_scrollbar");
280     if (ubuntu_gtk_set_use_overlay_scrollbar)
281         ubuntu_gtk_set_use_overlay_scrollbar(false);
282 
283     // make a window
284     GtkWidget* gtkWindow = gtk_window_new(GTK_WINDOW_POPUP);
285     gtk_widget_realize(gtkWindow);
286     QHashableLatin1Literal widgetPath = QHashableLatin1Literal::fromData(strdup("GtkWindow"));
287     removeWidgetFromMap(widgetPath);
288     gtkWidgetMap()->insert(widgetPath, gtkWindow);
289 
290 
291     // Make all other widgets. respect the text direction
292     if (qApp->layoutDirection() == Qt::RightToLeft)
293         gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL);
294 
295     if (!gtkWidgetMap()->contains("GtkButton")) {
296         GtkWidget *gtkButton = gtk_button_new();
297         addWidget(gtkButton);
298         g_signal_connect(gtkButton, "style-set", G_CALLBACK(gtkStyleSetCallback), 0);
299         addWidget(GTK_WIDGET(gtk_tool_button_new(NULL, "Qt")));
300         addWidget(GTK_WIDGET(gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE)));
301         addWidget(GTK_WIDGET(gtk_hbutton_box_new()));
302         addWidget(GTK_WIDGET(gtk_check_button_new()));
303         addWidget(GTK_WIDGET(gtk_radio_button_new(NULL)));
304         addWidget(GTK_WIDGET(gtk_combo_box_new()));
305         addWidget(GTK_WIDGET(gtk_combo_box_entry_new()));
306         GtkWidget *entry = gtk_entry_new();
307         // gtk-im-context-none is supported in gtk+ since 2.19.5
308         // and also exists in gtk3
309         // http://git.gnome.org/browse/gtk+/tree/gtk/gtkimmulticontext.c?id=2.19.5#n33
310         // reason that we don't use gtk-im-context-simple here is,
311         // gtk-im-context-none has less overhead, and 2.19.5 is
312         // relatively old. and even for older gtk+, it will fallback
313         // to gtk-im-context-simple if gtk-im-context-none doesn't
314         // exists.
315         g_object_set(entry, "im-module", "gtk-im-context-none", NULL);
316         addWidget(entry);
317         addWidget(gtk_frame_new(NULL));
318         addWidget(gtk_expander_new(""));
319         addWidget(gtk_statusbar_new());
320         addWidget(gtk_hscale_new(GTK_ADJUSTMENT(gtk_adjustment_new(1, 0, 1, 0, 0, 0))));
321         addWidget(gtk_hscrollbar_new(NULL));
322         addWidget(gtk_scrolled_window_new(NULL, NULL));
323 
324         initGtkMenu();
325         addWidget(gtk_notebook_new());
326         addWidget(gtk_progress_bar_new());
327         addWidget(gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(1, 0, 1, 0, 0, 0)), 0.1, 3));
328         GtkWidget *toolbar = gtk_toolbar_new();
329         g_signal_connect (toolbar, "notify::toolbar-style", G_CALLBACK (update_toolbar_style), toolbar);
330         gtk_toolbar_insert((GtkToolbar*)toolbar, gtk_separator_tool_item_new(), -1);
331         addWidget(toolbar);
332         initGtkTreeview();
333         addWidget(gtk_vscale_new(GTK_ADJUSTMENT(gtk_adjustment_new(1, 0, 1, 0, 0, 0))));
334         addWidget(gtk_vscrollbar_new(NULL));
335     }
336     else // Rebuild map
337     {
338         // When styles change subwidgets can get rearranged
339         // as with the combo box. We need to update the widget map
340         // to reflect this;
341         QHash<QHashableLatin1Literal, GtkWidget*> oldMap = *gtkWidgetMap();
342         gtkWidgetMap()->clear();
343         QHashIterator<QHashableLatin1Literal, GtkWidget*> it(oldMap);
344         while (it.hasNext()) {
345             it.next();
346             if (!strchr(it.key().data(), '.')) {
347                 addAllSubWidgets(it.value());
348             }
349             free(const_cast<char *>(it.key().data()));
350         }
351     }
352 }
353 
354 /*! \internal
355  * destroys all previously buffered widgets.
356  */
cleanupGtkWidgets()357 void QGtkStylePrivate::cleanupGtkWidgets()
358 {
359     if (!widgetMap)
360         return;
361     if (widgetMap->contains("GtkWindow")) // Gtk will destroy all children
362         gtk_widget_destroy(widgetMap->value("GtkWindow"));
363     for (QHash<QHashableLatin1Literal, GtkWidget *>::const_iterator it = widgetMap->constBegin();
364          it != widgetMap->constEnd(); ++it)
365         free(const_cast<char *>(it.key().data()));
366 }
367 
getThemeName()368 QString QGtkStylePrivate::getThemeName()
369 {
370     QString themeName;
371     // Read the theme name from GtkSettings
372     GtkSettings *settings = gtk_settings_get_default();
373     gchararray value;
374     g_object_get(settings, "gtk-theme-name", &value, NULL);
375     themeName = QString::fromUtf8(value);
376     g_free(value);
377     return themeName;
378 }
379 
380 // Get size of the arrow controls in a GtkSpinButton
getSpinboxArrowSize() const381 int QGtkStylePrivate::getSpinboxArrowSize() const
382 {
383     const int MIN_ARROW_WIDTH = 6;
384     GtkWidget *spinButton = gtkWidget("GtkSpinButton");
385     GtkStyle *style = gtk_widget_get_style(spinButton);
386     gint size = pango_font_description_get_size (style->font_desc);
387     gint arrow_size;
388     arrow_size = qMax(PANGO_PIXELS (size), MIN_ARROW_WIDTH) + style->xthickness;
389     arrow_size += arrow_size%2 + 1;
390     return arrow_size;
391 }
392 
393 
isKDE4Session()394 bool QGtkStylePrivate::isKDE4Session()
395 {
396     static int version = -1;
397     if (version == -1)
398         version = qgetenv("KDE_SESSION_VERSION").toInt();
399     return (version == 4);
400 }
401 
applyCustomPaletteHash()402 void QGtkStylePrivate::applyCustomPaletteHash()
403 {
404     QPalette menuPal = gtkWidgetPalette("GtkMenu");
405     GdkColor gdkBg = gtk_widget_get_style(gtkWidget("GtkMenu"))->bg[GTK_STATE_NORMAL];
406     QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8);
407     menuPal.setBrush(QPalette::Base, bgColor);
408     menuPal.setBrush(QPalette::Window, bgColor);
409     qApp->setPalette(menuPal, "QMenu");
410 
411     QPalette toolbarPal = gtkWidgetPalette("GtkToolbar");
412     qApp->setPalette(toolbarPal, "QToolBar");
413 
414     QPalette menuBarPal = gtkWidgetPalette("GtkMenuBar");
415     qApp->setPalette(menuBarPal, "QMenuBar");
416 }
417 
418 /*! \internal
419  *  Returns the gtk Widget that should be used to determine text foreground and background colors.
420 */
getTextColorWidget() const421 GtkWidget* QGtkStylePrivate::getTextColorWidget() const
422 {
423     return  gtkWidget("GtkEntry");
424 }
425 
setupGtkWidget(GtkWidget * widget)426 void QGtkStylePrivate::setupGtkWidget(GtkWidget* widget)
427 {
428     if (GTK_IS_WIDGET(widget)) {
429         GtkWidget *protoLayout = gtkWidgetMap()->value("GtkContainer");
430         if (!protoLayout) {
431             protoLayout = gtk_fixed_new();
432             gtk_container_add((GtkContainer*)(gtkWidgetMap()->value("GtkWindow")), protoLayout);
433             QHashableLatin1Literal widgetPath = QHashableLatin1Literal::fromData(strdup("GtkContainer"));
434             gtkWidgetMap()->insert(widgetPath, protoLayout);
435         }
436         Q_ASSERT(protoLayout);
437 
438         if (!gtk_widget_get_parent(widget) && !gtk_widget_is_toplevel(widget))
439             gtk_container_add((GtkContainer*)(protoLayout), widget);
440         gtk_widget_realize(widget);
441     }
442 }
443 
removeWidgetFromMap(const QHashableLatin1Literal & path)444 void QGtkStylePrivate::removeWidgetFromMap(const QHashableLatin1Literal &path)
445 {
446     WidgetMap *map = gtkWidgetMap();
447     WidgetMap::iterator it = map->find(path);
448     if (it != map->end()) {
449         char* keyData = const_cast<char *>(it.key().data());
450         map->erase(it);
451         free(keyData);
452     }
453 }
454 
addWidgetToMap(GtkWidget * widget)455 void QGtkStylePrivate::addWidgetToMap(GtkWidget *widget)
456 {
457     if (GTK_IS_WIDGET(widget)) {
458         gtk_widget_realize(widget);
459         QHashableLatin1Literal widgetPath = classPath(widget);
460 
461         removeWidgetFromMap(widgetPath);
462         gtkWidgetMap()->insert(widgetPath, widget);
463 #ifdef DUMP_GTK_WIDGET_TREE
464         qWarning("Inserted Gtk Widget: %s", widgetPath.data());
465 #endif
466     }
467  }
468 
addAllSubWidgets(GtkWidget * widget,gpointer v)469 void QGtkStylePrivate::addAllSubWidgets(GtkWidget *widget, gpointer v)
470 {
471     Q_UNUSED(v);
472     addWidgetToMap(widget);
473     if (G_TYPE_CHECK_INSTANCE_TYPE ((widget), gtk_container_get_type()))
474         gtk_container_forall((GtkContainer*)widget, addAllSubWidgets, NULL);
475 }
476 
477 // Updates window/windowtext palette based on the indicated gtk widget
gtkWidgetPalette(const QHashableLatin1Literal & gtkWidgetName) const478 QPalette QGtkStylePrivate::gtkWidgetPalette(const QHashableLatin1Literal &gtkWidgetName) const
479 {
480     GtkWidget *gtkWidget = QGtkStylePrivate::gtkWidget(gtkWidgetName);
481     Q_ASSERT(gtkWidget);
482     QPalette pal = QApplication::palette();
483     GdkColor gdkBg = gtk_widget_get_style(gtkWidget)->bg[GTK_STATE_NORMAL];
484     GdkColor gdkText = gtk_widget_get_style(gtkWidget)->fg[GTK_STATE_NORMAL];
485     GdkColor gdkDisabledText = gtk_widget_get_style(gtkWidget)->fg[GTK_STATE_INSENSITIVE];
486     QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8);
487     QColor textColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8);
488     QColor disabledTextColor(gdkDisabledText.red>>8, gdkDisabledText.green>>8, gdkDisabledText.blue>>8);
489     pal.setBrush(QPalette::Window, bgColor);
490     pal.setBrush(QPalette::Button, bgColor);
491     pal.setBrush(QPalette::All, QPalette::WindowText, textColor);
492     pal.setBrush(QPalette::Disabled, QPalette::WindowText, disabledTextColor);
493     pal.setBrush(QPalette::All, QPalette::ButtonText, textColor);
494     pal.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledTextColor);
495     return pal;
496 }
497 
498 
updateTheme()499 void QGtkStyleUpdateScheduler::updateTheme()
500 {
501     static QString oldTheme(QLS("qt_not_set"));
502     QPixmapCache::clear();
503 
504     QFont font = QGtkStylePrivate::getThemeFont();
505     if (QApplication::font() != font)
506         qApp->setFont(font);
507 
508       if (oldTheme != QGtkStylePrivate::getThemeName()) {
509           oldTheme = QGtkStylePrivate::getThemeName();
510           QPalette newPalette = qApp->style()->standardPalette();
511 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
512           QApplicationPrivate::setSystemPalette(newPalette);
513 #endif
514           QApplication::setPalette(newPalette);
515           if (!QGtkStylePrivate::instances.isEmpty()) {
516               QGtkStylePrivate::instances.last()->initGtkWidgets();
517               QGtkStylePrivate::instances.last()->applyCustomPaletteHash();
518           }
519           QList<QWidget*> widgets = QApplication::allWidgets();
520           // Notify all widgets that size metrics might have changed
521           foreach (QWidget *widget, widgets) {
522               QEvent e(QEvent::StyleChange);
523               QApplication::sendEvent(widget, &e);
524           }
525       }
526     QIconLoader::instance()->updateSystemTheme();
527 }
528 
addWidget(GtkWidget * widget)529 void QGtkStylePrivate::addWidget(GtkWidget *widget)
530 {
531     if (widget) {
532         setupGtkWidget(widget);
533         addAllSubWidgets(widget);
534     }
535 }
536 
537 
538 // Fetch the application font from the pango font description
539 // contained in the theme.
getThemeFont()540 QFont QGtkStylePrivate::getThemeFont()
541 {
542     QFont font;
543     GtkStyle *style = gtkStyle();
544     if (style && qApp->desktopSettingsAware())
545     {
546         PangoFontDescription *gtk_font = style->font_desc;
547         font.setPointSizeF((float)(pango_font_description_get_size(gtk_font))/PANGO_SCALE);
548 
549         QString family = QString::fromLatin1(pango_font_description_get_family(gtk_font));
550         if (!family.isEmpty())
551             font.setFamily(family);
552 
553 #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
554         const int weight = pango_font_description_get_weight(gtk_font);
555         font.setWeight(QPlatformFontDatabase::weightFromInteger(weight));
556 #else
557         int weight = pango_font_description_get_weight(gtk_font);
558         if (weight >= PANGO_WEIGHT_HEAVY)
559             font.setWeight(QFont::Black);
560         else if (weight >= PANGO_WEIGHT_BOLD)
561             font.setWeight(QFont::Bold);
562         else if (weight >= PANGO_WEIGHT_SEMIBOLD)
563             font.setWeight(QFont::DemiBold);
564         else if (weight >= PANGO_WEIGHT_NORMAL)
565             font.setWeight(QFont::Normal);
566         else
567             font.setWeight(QFont::Light);
568 #endif
569 
570         PangoStyle fontstyle = pango_font_description_get_style(gtk_font);
571         if (fontstyle == PANGO_STYLE_ITALIC)
572             font.setStyle(QFont::StyleItalic);
573         else if (fontstyle == PANGO_STYLE_OBLIQUE)
574             font.setStyle(QFont::StyleOblique);
575         else
576             font.setStyle(QFont::StyleNormal);
577     }
578     return font;
579 }
580 
operator ==(const QHashableLatin1Literal & l1,const QHashableLatin1Literal & l2)581 bool operator==(const QHashableLatin1Literal &l1, const QHashableLatin1Literal &l2)
582 {
583     return l1.size() == l2.size() || qstrcmp(l1.data(), l2.data()) == 0;
584 }
585 
586 // copied from qHash.cpp
qHash(const QHashableLatin1Literal & key)587 uint qHash(const QHashableLatin1Literal &key)
588 {
589     int n = key.size();
590     const uchar *p = reinterpret_cast<const uchar *>(key.data());
591     uint h = 0;
592     uint g;
593 
594     while (n--) {
595         h = (h << 4) + *p++;
596         if ((g = (h & 0xf0000000)) != 0)
597             h ^= g >> 23;
598         h &= ~g;
599     }
600     return h;
601 }
602 
603 QT_END_NAMESPACE
604