changeset 668:dac3781826da thorn-cpp

Experimental branch based on Thorn C++ code from RG
author Chris Cannam
date Tue, 30 Apr 2019 11:36:38 +0100
parents 31ea416fea3c
children
files files.pri style/AppEventFilter.cpp style/AppEventFilter.h style/ThornStyle.cpp style/ThornStyle.h
diffstat 5 files changed, 1654 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/files.pri	Fri Apr 26 18:39:46 2019 +0100
+++ b/files.pri	Tue Apr 30 11:36:38 2019 +0100
@@ -12,7 +12,9 @@
            framework/OSCScript.h \
            framework/SVFileReader.h \
            framework/TransformUserConfigurator.h \
-           framework/VersionTester.h
+           framework/VersionTester.h \
+           style/AppEventFilter.h \
+           style/ThornStyle.h
 
 SVAPP_SOURCES += \
            audio/AudioCallbackPlaySource.cpp \
@@ -26,4 +28,6 @@
            framework/MainWindowBase.cpp \
            framework/SVFileReader.cpp \
            framework/TransformUserConfigurator.cpp \
-           framework/VersionTester.cpp
+           framework/VersionTester.cpp \
+           style/AppEventFilter.cpp \
+           style/ThornStyle.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/style/AppEventFilter.cpp	Tue Apr 30 11:36:38 2019 +0100
@@ -0,0 +1,170 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vect
+    An experimental audio player for plural recordings of a work
+    Centre for Digital Music, Queen Mary, University of London.
+
+    This file is taken from Rosegarden, a MIDI and audio sequencer and
+    musical notation editor. Copyright 2000-2018 the Rosegarden
+    development team. Thorn style developed in stylesheet form by
+    D. Michael McIntyre and reimplemented as a class by David Faure.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "ThornStyle.h"
+#include "AppEventFilter.h"
+
+#include <QApplication>
+#include <QFileDialog>
+#include <QAbstractItemView>
+#include <QCheckBox>
+#include <QLabel>
+#include <QRadioButton>
+#include <QToolBar>
+#include <QWidget>
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QComboBox>
+#include <QSpinBox>
+
+// Apply the style to widget and its children, recursively
+// Even though every widget goes through the event filter, this is needed
+// for the case where a whole widget hierarchy is suddenly reparented into the file dialog.
+// Then we need to apply the app style again. Testcase: scrollbars in file dialog.
+static void applyStyleRecursive(QWidget* widget, QStyle *style)
+{
+    if (widget->style() != style) {
+        widget->setStyle(style);
+    }
+    foreach (QObject* obj, widget->children()) {
+        if (obj->isWidgetType()) {
+            QWidget *w = static_cast<QWidget *>(obj);
+            applyStyleRecursive(w, style);
+        }
+    }
+}
+
+AppEventFilter::AppEventFilter() :
+    m_systemPalette(qApp->palette()),
+    m_systemStyle(qApp->style()) {
+}
+
+bool
+AppEventFilter::shouldIgnoreThornStyle(QWidget *widget) const {
+    return qobject_cast<QFileDialog *>(widget)
+        || widget->inherits("KDEPlatformFileDialog")
+        || widget->inherits("KDirSelectDialog");
+}
+
+// when we ditch Qt4, we can switch to qCDebug...
+//#define DEBUG_EVENTFILTER
+
+bool AppEventFilter::eventFilter(QObject *watched, QEvent *event)
+{
+    static bool s_insidePolish = false; // setStyle calls polish again, so skip doing the work twice
+    if (!s_insidePolish && watched->isWidgetType() && event->type() == QEvent::Polish) {
+        s_insidePolish = true;
+        // This is called after every widget is created and just before being shown
+        // (we use this so that it has a proper parent widget already)
+        QWidget *widget = static_cast<QWidget *>(watched);
+        if (shouldIgnoreThornStyle(widget)) {
+            // The palette from the mainwindow propagated to the dialog, restore it.
+            widget->setPalette(m_systemPalette);
+#ifdef DEBUG_EVENTFILTER
+            qDebug() << widget << "now using app style (recursive)";
+#endif
+            applyStyleRecursive(widget, qApp->style());
+            s_insidePolish = false;
+            return false;
+        }
+        QWidget *toplevel = widget->window();
+#ifdef DEBUG_EVENTFILTER
+        qDebug() << widget << "current widget style=" << widget->style() << "shouldignore=" << shouldIgnoreThornStyle(toplevel);
+#endif
+        if (shouldIgnoreThornStyle(toplevel)) {
+            // Here we should apply qApp->style() recursively on widget and its children, in case one was reparented
+#ifdef DEBUG_EVENTFILTER
+            qDebug() << widget << widget->objectName() << "in" << toplevel << "now using app style (recursive)";
+#endif
+            applyStyleRecursive(widget, qApp->style());
+        } else if (widget->style() != &m_style) {
+#ifdef DEBUG_EVENTFILTER
+            //qDebug() << "    ToolTipBase=" << widget->palette().color(QPalette::ToolTipBase).name();
+#endif
+            // Apply style recursively because some child widgets (e.g. QHeaderView in QTreeWidget, in DeviceManagerDialog) don't seem to get here.
+            if (qobject_cast<QAbstractItemView *>(widget)) {
+                applyStyleRecursive(widget, &m_style);
+            } else {
+                widget->setStyle(&m_style);
+            }
+#ifdef DEBUG_EVENTFILTER
+            qDebug() << "    now using style" << widget->style();
+#endif
+            if (widget->windowType() != Qt::Widget) { // window, tooltip, ...
+                widget->setPalette(m_style.standardPalette());
+#ifdef DEBUG_EVENTFILTER
+                qDebug() << "    after setPalette:     ToolTipBase=" << widget->palette().color(QPalette::ToolTipBase).name();
+#endif
+            } else {
+#ifdef DEBUG_EVENTFILTER
+                //qDebug() << "    not a toplevel. ToolTipBase=" << widget->palette().color(QPalette::ToolTipBase).name();
+#endif
+            }
+            polishWidget(widget);
+        }
+        s_insidePolish = false;
+    }
+    return false; // don't eat the event
+}
+
+void AppEventFilter::polishWidget(QWidget *widget)
+{
+    if (QLabel *label = qobject_cast<QLabel *>(widget)) {
+        if (qobject_cast<QToolBar *>(widget->parentWidget())) {
+            /* Toolbars must be light enough for black icons, therefore black text on their
+               QLabels, rather than white, is more appropriate.
+               QToolBar QLabel { color: #000000; } */
+            QPalette pal = label->palette();
+            pal.setColor(label->foregroundRole(), Qt::black);
+            label->setPalette(pal);
+            //qDebug() << "made label black:" << label << label->text();
+        }
+        if (widget->objectName() == "SPECIAL_LABEL") {
+            widget->setAutoFillBackground(true);
+            // QWidget#SPECIAL_LABEL { color: #000000; background-color: #999999; }
+            QPalette palette = widget->palette();
+            palette.setColor(QPalette::WindowText, Qt::black);
+            palette.setColor(QPalette::Window, QColor(0x99, 0x99, 0x99));
+            widget->setPalette(palette);
+        }
+    } else if (widget->objectName() == "Rosegarden Transport") {
+        // Give the non-LED parts of the dialog the groupbox "lighter black"
+        // background for improved contrast.
+        QPalette transportPalette = widget->palette();
+        transportPalette.setColor(widget->backgroundRole(), QColor(0x40, 0x40, 0x40));
+        widget->setPalette(transportPalette);
+        widget->setAutoFillBackground(true);
+    } else if (QCheckBox *cb = qobject_cast<QCheckBox *>(widget)) {
+        cb->setAttribute(Qt::WA_Hover);
+    } else if (QRadioButton *rb = qobject_cast<QRadioButton *>(widget)) {
+        rb->setAttribute(Qt::WA_Hover);
+    } else if (QPushButton *pb = qobject_cast<QPushButton *>(widget)) {
+        pb->setAttribute(Qt::WA_Hover);
+        if (qobject_cast<QDialogButtonBox *>(widget->parentWidget())) {
+            // Bug in QDialogButtonBox: if the app style sets QStyle::SH_DialogButtonBox_ButtonsHaveIcons
+            // a later call to setStyle() doesn't remove the button icon again.
+            // Fix submitted at https://codereview.qt-project.org/183788
+            pb->setIcon(QIcon());
+        }
+    } else if (QComboBox *cb = qobject_cast<QComboBox *>(widget)) {
+        cb->setAttribute(Qt::WA_Hover);
+    } else if (QAbstractSpinBox *sb = qobject_cast<QAbstractSpinBox *>(widget)) {
+        sb->setAttribute(Qt::WA_Hover);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/style/AppEventFilter.h	Tue Apr 30 11:36:38 2019 +0100
@@ -0,0 +1,45 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vect
+    An experimental audio player for plural recordings of a work
+    Centre for Digital Music, Queen Mary, University of London.
+
+    This file is taken from Rosegarden, a MIDI and audio sequencer and
+    musical notation editor. Copyright 2000-2018 the Rosegarden
+    development team. Thorn style developed in stylesheet form by
+    D. Michael McIntyre and reimplemented as a class by David Faure.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef SV_THORN_STYLE_APPEVENT_FILTER_H
+#define SV_THORN_STYLE_APPEVENT_FILTER_H
+
+#include "ThornStyle.h"
+
+/**
+ * The AppEventFilter class is notified when a new widget is created
+ * and can decide whether to apply the Thorn Style to it or not.
+ */
+class AppEventFilter : public QObject
+{
+    Q_OBJECT
+    
+public:
+    AppEventFilter();
+    bool eventFilter(QObject *watched, QEvent *event) override;
+    bool shouldIgnoreThornStyle(QWidget *widget) const;
+    void polishWidget(QWidget *widget);
+
+private:
+    ThornStyle m_style;
+    QPalette m_systemPalette;
+    QStyle *m_systemStyle;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/style/ThornStyle.cpp	Tue Apr 30 11:36:38 2019 +0100
@@ -0,0 +1,1331 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vect
+    An experimental audio player for plural recordings of a work
+    Centre for Digital Music, Queen Mary, University of London.
+
+    This file is based on Rosegarden, a MIDI and audio sequencer and
+    musical notation editor. Copyright 2000-2018 the Rosegarden
+    development team. Thorn style developed in stylesheet form by
+    D. Michael McIntyre and reimplemented as a class by David Faure.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "ThornStyle.h"
+#include "AppEventFilter.h"
+
+#include "base/ResourceFinder.h"
+#include "widgets/IconLoader.h"
+
+#include <QApplication>
+#include <QAbstractItemView>
+#include <QCheckBox>
+#include <QDebug>
+#include <qdrawutil.h>
+#include <QEvent>
+#include <QFile>
+#include <QFileDialog>
+#include <QLabel>
+#include <QLayout>
+#include <QPainter>
+#include <QRadioButton>
+#include <QStyleFactory>
+#include <QStyleOption>
+#include <QToolBar>
+#include <QWidget>
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QComboBox>
+#include <QSpinBox>
+#include <QScrollBar>
+#include <QAbstractScrollArea>
+
+// Silence gcc compiler warnings due to the switches below not covering all cases, on purpose
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch-enum"
+
+static QPixmap loadPix(const QString &name)
+{
+    QPixmap pix(name);
+    if (pix.isNull()) {
+        qWarning() << "Pixmap not found:" << name;
+        Q_ASSERT(0);
+    }
+    return pix;
+}
+
+Q_GLOBAL_STATIC(AppEventFilter, s_eventFilter) // created on demand in setEnabled
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ThornStyle::ThornStyle()
+      // We could load these on demand, but the mainwindow needs most of them already anyway
+    : m_horizontalToolbarSeparatorPixmap(loadPix(":/icons/style/htoolbar-separator.png")),
+      m_verticalToolbarSeparatorPixmap(loadPix(":/icons/style/vtoolbar-separator.png")),
+      m_checkboxUncheckedPixmap(loadPix(":/icons/style/checkbox_unchecked.png")),
+      m_checkboxUncheckedHoverPixmap(loadPix(":/icons/style/checkbox_unchecked_hover.png")),
+      m_checkboxUncheckedDisabledPixmap(loadPix(":/icons/style/checkbox_disabled.png")),
+      m_checkboxUncheckedPressedPixmap(loadPix(":/icons/style/checkbox_unchecked_pressed.png")),
+      m_checkboxCheckedPixmap(loadPix(":/icons/style/checkbox_checked.png")),
+      m_checkboxCheckedHoverPixmap(loadPix(":/icons/style/checkbox_checked_hover.png")),
+      m_checkboxCheckedDisabledPixmap(loadPix(":/icons/style/checkbox_checked_disabled.png")),
+      m_checkboxCheckedPressedPixmap(loadPix(":/icons/style/checkbox_checked_pressed.png")),
+      m_checkboxIndeterminatePixmap(loadPix(":/icons/style/checkbox_indeterminate.png")),
+      m_checkboxIndeterminateHoverPixmap(loadPix(":/icons/style/checkbox_indeterminate_hover.png")),
+      //m_checkboxIndeterminateDisabledPixmap(loadPix(":/icons/style/checkbox_indeterminate_disabled.png")),
+      m_checkboxIndeterminatePressedPixmap(loadPix(":/icons/style/checkbox_indeterminate_pressed.png")),
+      m_radiobuttonUncheckedPixmap(loadPix(":/icons/style/radiobutton_unchecked.png")),
+      m_radiobuttonUncheckedHoverPixmap(loadPix(":/icons/style/radiobutton_unchecked_hover.png")),
+      m_radiobuttonUncheckedDisabledPixmap(loadPix(":/icons/style/radiobutton_unchecked_disabled.png")),
+      m_radiobuttonUncheckedPressedPixmap(loadPix(":/icons/style/radiobutton_unchecked_pressed.png")),
+      m_radiobuttonCheckedPixmap(loadPix(":/icons/style/radiobutton_checked.png")),
+      m_radiobuttonCheckedHoverPixmap(loadPix(":/icons/style/radiobutton_checked_hover.png")),
+      m_radiobuttonCheckedDisabledPixmap(loadPix(":/icons/style/radiobutton_checked_disabled.png")),
+      m_radiobuttonCheckedPressedPixmap(loadPix(":/icons/style/radiobutton_checked_pressed.png")),
+      m_arrowDownSmallPixmap(loadPix(":/icons/style/arrow-down-small.png")),
+      m_arrowDownSmallInvertedPixmap(loadPix(":/icons/style/arrow-down-small-inverted.png")),
+      m_arrowUpSmallPixmap(loadPix(":/icons/style/arrow-up-small.png")),
+      m_arrowUpSmallInvertedPixmap(loadPix(":/icons/style/arrow-up-small-inverted.png")),
+      m_arrowLeftPixmap(":/icons/style/arrow-left.png"),
+      m_arrowRightPixmap(":/icons/style/arrow-right.png"),
+      m_arrowUpPixmap(":/icons/style/arrow-up.png"),
+      m_arrowDownPixmap(":/icons/style/arrow-down.png"),
+      m_spinupPixmap(":/icons/style/spinup.png"),
+      m_spinupHoverPixmap(":/icons/style/spinup_hover.png"),
+      m_spinupOffPixmap(":/icons/style/spinup_off.png"),
+      m_spinupPressedPixmap(":/icons/style/spinup_pressed.png"),
+      m_spindownPixmap(":/icons/style/spindown.png"),
+      m_spindownHoverPixmap(":/icons/style/spindown_hover.png"),
+      m_spindownOffPixmap(":/icons/style/spindown_off.png"),
+      m_spindownPressedPixmap(":/icons/style/spindown_pressed.png"),
+      m_titleClosePixmap(":/icons/style/title-close.png"),
+      m_titleUndockPixmap(":/icons/style/title-undock.png")
+{
+    // Qt 5 removes QPlastiqueStyle and defaults to a new style called "Fusion."
+    // This style forces combo boxes to do bad things.
+    // I concluded that using "windows" as a base style causes an acceptable amount
+    // of damage while leaving things largely intact.
+    setBaseStyle(QStyleFactory::create("windows"));
+
+    m_standardPalette.setColor(QPalette::Window, QColor(0x30, 0x30, 0x30));
+
+    // QLabel { color: white }
+    m_standardPalette.setColor(QPalette::WindowText, Qt::white);
+
+    // QListView, QTableView, QTreeView, QLineEdit... :
+    // background-color: #FFFFFF;
+    // color: #000000;
+    // selection-background-color: #80AFFF;
+    // selection-color: #FFFFFF;
+    m_standardPalette.setColor(QPalette::Base, Qt::white);
+    m_standardPalette.setColor(QPalette::Text, Qt::black);
+    m_standardPalette.setColor(QPalette::Highlight, QColor(0x80, 0xAF, 0xFF));
+    m_standardPalette.setColor(QPalette::HighlightedText, Qt::white);
+
+    // for QPushButton but also QMenu
+    const QColor buttonColor = QColor(0xEE, 0xEE, 0xEE);
+    m_standardPalette.setColor(QPalette::Button, buttonColor);
+    m_standardPalette.setColor(QPalette::ButtonText, Qt::black); // enabled button texts and menu items
+
+    // alternate-background-color: #EEEEFF;
+    m_standardPalette.setColor(QPalette::AlternateBase, QColor(0xEE, 0xEE, 0xFF));
+
+    // QToolTip { background-color: #fffbd4; color: #000000; + some awful pixmap hack }
+    m_standardPalette.setColor(QPalette::ToolTipBase, QColor(0xFF, 0xFB, 0xD4));
+    m_standardPalette.setColor(QPalette::ToolTipText, Qt::black);
+}
+
+ThornStyle::~ThornStyle()
+{
+}
+
+QIcon ThornStyle::standardIcon(QStyle::StandardPixmap standardIcon, const QStyleOption *option, const QWidget *parent) const
+{
+    IconLoader loader;
+    
+    // NOTE: see src/gui/styles/qcommonstyle.cpp in the Qt source for examples
+    // of how to extend this whenever more custom icons are called for
+    switch (standardIcon) {
+
+    // custom icons for QMessageBox
+    case SP_MessageBoxInformation:
+        return loader.load("style/messagebox-information");
+
+    case SP_MessageBoxWarning:
+        return loader.load("style/warning");
+
+    case SP_MessageBoxCritical:
+        return loader.load("style/messagebox-critical");
+
+    case SP_MessageBoxQuestion:
+        return loader.load("style/messagebox-question");
+
+    case SP_TitleBarNormalButton:
+        return m_titleUndockPixmap;
+
+    case SP_DockWidgetCloseButton:
+    case SP_TitleBarCloseButton:
+        return m_titleClosePixmap;
+
+    default:
+        // let the base class handle the rest
+        return QProxyStyle::standardPixmap(standardIcon, option, parent);
+    }
+}
+
+QSize ThornStyle::pixmapSize(const QPixmap &pixmap) const
+{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+    return QSize(int(round(pixmap.width() / pixmap.devicePixelRatio())),
+                 int(round(pixmap.height() / pixmap.devicePixelRatio())));
+#else
+    return pixmap.size();
+#endif
+}
+
+static bool s_thornStyleEnabled = false;
+
+// This method currently only supports being called once
+void ThornStyle::setEnabled(bool b)
+{
+    s_thornStyleEnabled = b;
+    if (b) {
+        qApp->installEventFilter(s_eventFilter());
+    }
+}
+
+bool ThornStyle::isEnabled()
+{
+    return s_thornStyleEnabled;
+}
+
+QPalette ThornStyle::standardPalette() const
+{
+    return m_standardPalette;
+}
+
+int ThornStyle::styleHint(QStyle::StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const
+{
+    switch (hint) {
+    case SH_EtchDisabledText:
+        return 0;
+    case SH_Table_GridLineColor:
+        // QTableView { gridline-color: #202020; }
+        return qRgb(0x20, 0x20, 0x20);
+    case SH_GroupBox_TextLabelColor:
+        // QGroupBox::title { color: #FFFFFF; }
+        // QGroupBox::title:!enabled { color: #000000; }
+        // but it was etched; plain black is unreadable, so let's use another color now
+        return option->state & State_Enabled ? qRgb(0xFF, 0xFF, 0xFF) : qRgb(0xAA, 0xAA, 0xAA);
+    case SH_DialogButtonBox_ButtonsHaveIcons:
+        return 0;
+    case SH_DockWidget_ButtonsHaveFrame:
+        return 1;
+    default:
+        break;
+    }
+    return QProxyStyle::styleHint(hint, option, widget, returnData);
+}
+
+int ThornStyle::pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
+{
+    switch (metric) {
+    case PM_SplitterWidth: //fall-through
+    case PM_DockWidgetSeparatorExtent:
+        // QMainWindow::separator { height: 5px; }
+        return 5;
+    case PM_TabBarScrollButtonWidth:
+        // QTabBar::scroller { /* the width of the scroll buttons */ width: 13px; }
+        return 13;
+    case PM_TabBarBaseOverlap:
+        return 0;
+    case PM_ToolBarHandleExtent: // Horizontal toolbar: width of the handle. Vertical toolbar: height of the handle
+        if (option->state & State_Horizontal)
+            return m_horizontalToolbarSeparatorPixmap.width();
+        else
+            return m_verticalToolbarSeparatorPixmap.height();
+    case PM_ExclusiveIndicatorWidth:
+        return m_radiobuttonUncheckedPixmap.width();
+    case PM_ExclusiveIndicatorHeight:
+        return m_radiobuttonUncheckedPixmap.height();
+    case PM_IndicatorWidth:
+        return m_checkboxUncheckedPixmap.width();
+    case PM_IndicatorHeight:
+        return m_checkboxUncheckedPixmap.height();
+    case PM_MenuPanelWidth:
+        return 1;
+    case PM_MenuBarHMargin:
+        // QMenuBar { padding: 4px; }
+        return 4;
+    case PM_MenuBarItemSpacing:
+        // QMenuBar::item { spacing: 3px; padding: 1px 4px; }
+        return 4;
+    case PM_ScrollBarExtent: {
+        QWidget *parent = widget ? widget->parentWidget() : nullptr;
+        QWidget *combo = parent ? parent->parentWidget() : nullptr;
+        if (qobject_cast<QComboBox *>(combo)) {
+            // QComboBox QAbstractItemView QScrollBar:vertical { width: 12px; }
+            return 12;
+        }
+        // QScrollBar:horizontal { height: 16px; }
+        // QScrollBar:vertical { width: 16px; }
+        return 16;
+    }
+    case PM_ToolBarItemSpacing:
+        return 0;
+    case PM_ToolBarItemMargin:
+    case PM_ToolBarFrameWidth:
+        return 0;
+    case PM_DefaultFrameWidth:
+        return 2;
+    case PM_SpinBoxFrameWidth:
+        return 2;
+    case PM_DockWidgetTitleBarButtonMargin:
+        // icon size is 16x16 but somehow the buttons ended up 13x13
+        return -1;
+    case PM_DockWidgetTitleMargin: // space above and below the title
+        return 0;
+    case PM_DockWidgetFrameWidth:
+        // QDockWidget { border: none; }
+        return 0;
+    case PM_SmallIconSize:
+        return 16;
+    default:
+        return QProxyStyle::pixelMetric(metric, option, widget);
+    }
+}
+
+void ThornStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
+{
+    switch (element) {
+    case PE_IndicatorDockWidgetResizeHandle:
+        // QMainWindow::separator:hover { background-color: #CCDFFF; }
+        if (option->state & QStyle::State_MouseOver) {
+            painter->fillRect(option->rect, QColor(0xCC, 0xDF, 0xFF));
+            return;
+        }
+        break;
+    case PE_FrameTabWidget: // The tab widget frame
+        // QTabWidget::pane { border: 2px solid #BBBBBB; border-radius: 4px; padding: 2px; // and background: #404040;
+        painter->save();
+        painter->setPen(QPen(QColor(0xBB, 0xBB, 0xBB), 2));
+        painter->setBrush(QColor(0x40, 0x40, 0x40));
+        painter->setRenderHint(QPainter::Antialiasing);
+        painter->drawRoundedRect(option->rect.adjusted(2, 2, -2, -2), 4, 4);
+        painter->restore();
+        return;
+    case PE_FrameGroupBox: // same as above but not the background, already done
+        // QGroupBox { background: #404040; color: #FFFFFF; border: 2px solid #BBBBBB; border-radius: 4px; }
+        painter->save();
+        painter->setPen(QPen(QColor(0xBB, 0xBB, 0xBB), 2));
+        painter->setRenderHint(QPainter::Antialiasing);
+        painter->drawRoundedRect(option->rect.adjusted(2, 2, -2, -2), 4, 4);
+        painter->restore();
+        return;
+    case PE_IndicatorToolBarHandle: {
+        // top or bottom: image: url(:/icons/style/htoolbar-separator.png);
+        // left or right: image: url(:icons/style/vtoolbar-separator.png);
+        QPixmap pixmap = option->state & State_Horizontal ? m_horizontalToolbarSeparatorPixmap : m_verticalToolbarSeparatorPixmap;
+        const QRect rect = alignedRect(Qt::LayoutDirectionAuto, Qt::AlignCenter, option->rect.size(), option->rect);
+        painter->drawPixmap(rect, pixmap);
+        return;
+    }
+    case PE_PanelMenu:
+        // QMenu { background-color: #EEEEEE; border: 1px solid black; }
+        painter->fillRect(option->rect, QColor(0xEE, 0xEE, 0xEE));
+        return;
+    case PE_FrameMenu:
+        painter->setPen(Qt::black);
+        painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
+        return;
+    case PE_FrameDockWidget: // only called when the dockwidget is floating
+        // QDockWidget { border: none; }
+        return;
+    case PE_PanelStatusBar: // no frame around the statusbar
+    case PE_FrameStatusBarItem: // no frame around the statusbar items
+        return;
+    case PE_PanelLineEdit:
+        // QLineEdit { border: 1px solid #AAAAAA; background-color: #FFFFFF; }
+        if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
+            if (frame->lineWidth > 0) // i.e. not inside QSpinBox
+                painter->setPen(QColor(0xAA, 0xAA, 0xAA));
+            else
+                painter->setPen(Qt::NoPen);
+            painter->setBrush(Qt::white);
+            painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
+        }
+        return;
+    case PE_IndicatorMenuCheckMark:
+        return; // done in CE_MenuItem
+    case PE_IndicatorCheckBox:
+    case PE_IndicatorRadioButton: {
+        const bool checked = !(option->state & State_Off);
+        const bool disabled = !(option->state & State_Enabled);
+        const bool pressed = option->state & State_Sunken;
+        const bool hover = option->state & State_MouseOver;
+        QPixmap pixmap;
+        if (element == PE_IndicatorCheckBox) {
+            if (option->state & State_NoChange) {
+                // missing icon for disabled
+                if (pressed)
+                    pixmap = m_checkboxIndeterminatePressedPixmap;
+                else if (hover)
+                    pixmap = m_checkboxIndeterminateHoverPixmap;
+                else
+                    pixmap = m_checkboxIndeterminatePixmap;
+            } else {
+                if (disabled) {
+                    pixmap = checked ? m_checkboxCheckedDisabledPixmap : m_checkboxUncheckedDisabledPixmap;
+                } else {
+                    if (pressed)
+                        pixmap = checked ? m_checkboxCheckedPressedPixmap : m_checkboxUncheckedPressedPixmap;
+                    else if (hover)
+                        pixmap = checked ? m_checkboxCheckedHoverPixmap : m_checkboxUncheckedHoverPixmap;
+                    else
+                        pixmap = checked ? m_checkboxCheckedPixmap : m_checkboxUncheckedPixmap;
+                }
+            }
+        } else {
+            if (disabled) {
+                pixmap = checked ? m_radiobuttonCheckedDisabledPixmap : m_radiobuttonUncheckedDisabledPixmap;
+            } else {
+                if (pressed)
+                    pixmap = checked ? m_radiobuttonCheckedPressedPixmap : m_radiobuttonUncheckedPressedPixmap;
+                else if (hover)
+                    pixmap = checked ? m_radiobuttonCheckedHoverPixmap : m_radiobuttonUncheckedHoverPixmap;
+                else
+                    pixmap = checked ? m_radiobuttonCheckedPixmap : m_radiobuttonUncheckedPixmap;
+            }
+        }
+        QRect pmr(QPoint(0, 0), pixmapSize(pixmap));
+        pmr.moveCenter(option->rect.center());
+        painter->drawPixmap(pmr.topLeft(), pixmap);
+        return;
+    }
+    case PE_IndicatorHeaderArrow:
+        // QHeaderView::down-arrow / QHeaderView::up-arrow
+        if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+            const bool up = (header->sortIndicator & QStyleOptionHeader::SortUp);
+            const QPixmap pixmap = up ? m_arrowUpSmallInvertedPixmap : m_arrowDownSmallInvertedPixmap;
+            painter->drawPixmap(option->rect.topLeft(), pixmap);
+        }
+        return;
+    case PE_IndicatorArrowUp:
+    case PE_IndicatorArrowDown:
+    case PE_IndicatorArrowLeft:
+    case PE_IndicatorArrowRight: {
+        QPixmap pixmap;
+        if (element == PE_IndicatorArrowLeft)
+            pixmap = m_arrowLeftPixmap;
+        else if (element == PE_IndicatorArrowRight)
+            pixmap = m_arrowRightPixmap;
+        else if (element == PE_IndicatorArrowUp)
+            pixmap = m_arrowUpPixmap;
+        else if (element == PE_IndicatorArrowDown)
+            pixmap = m_arrowDownPixmap;
+        // Scale the pixmap to the desired rect (for scrollbars the pixmap is 12x12 but the rect is 8x8)
+        pixmap = pixmap.scaled(option->rect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+        // In case the scaling didn't occupy the full height (due to aspect ratio), center the resulting pixmap (eg: toolbutton menu indicator)
+        const QRect drawRect = alignedRect(option->direction, Qt::AlignCenter, pixmap.size(), option->rect);
+        painter->drawPixmap(drawRect.topLeft(), pixmap);
+    }
+        return;
+    case PE_PanelButtonTool:
+        if (widget && widget->inherits("QDockWidgetTitleButton")) {
+            // QDockWidget::close-button, QDockWidget::float-button {
+            //    border: 1px solid #AAAAAA;
+            //    border-radius: 3px;
+            //    background-color: qlineargradient(x1:0, y1:1, x2:0, y2:0, stop:0 #999999, stop:1 #DDDDDD);
+            //}
+            painter->save();
+            painter->setPen(QPen(QColor(0xAA, 0xAA, 0xAA)));
+            QLinearGradient gradient;
+            gradient.setStart(0, 0);
+            gradient.setFinalStop(0, option->rect.height());
+            gradient.setColorAt(0, QColor(0xDD, 0xDD, 0xDD));
+            gradient.setColorAt(1, QColor(0x99, 0x99, 0x99));
+            painter->setBrush(gradient);
+            painter->setRenderHint(QPainter::Antialiasing);
+            painter->drawRoundedRect(option->rect.adjusted(1, 1, -1, -1), 3, 3);
+            painter->restore();
+        }
+        else if ((option->state & State_On) || (option->state & State_Sunken)) {
+            // QToolButton::pressed, QToolButton::checked { border: 1px solid #AAAAAA; border-radius: 2px;
+            //            background-color: qlineargradient(x1:0, y1:1, x2:0, y2:0, stop:0 #E0E0E0, stop:1 #EEEEEE); }
+            painter->save();
+            painter->setPen(QPen(QColor(0xAA, 0xAA, 0xAA)));
+            QLinearGradient gradient;
+            gradient.setStart(0, 0);
+            gradient.setFinalStop(0, option->rect.height());
+            gradient.setColorAt(0, QColor(0xEE, 0xEE, 0xEE));
+            gradient.setColorAt(1, QColor(0xE0, 0xE0, 0xE0));
+            painter->setBrush(gradient);
+            painter->setRenderHint(QPainter::Antialiasing);
+            painter->drawRoundedRect(option->rect.adjusted(0, 0, -1, -1), 2, 2);
+            painter->restore();
+        }
+        else if ((option->state & State_MouseOver) && (option->state & State_Enabled)) {
+            // QToolButton::enabled:hover { border: 1px solid #AAAAAA; border-radius: 2px; background-color: #CCDFFF; }
+            painter->save();
+            painter->setPen(QPen(QColor(0xAA, 0xAA, 0xAA)));
+            painter->setBrush(QColor(0xCC, 0xDF, 0xFF));
+            painter->setRenderHint(QPainter::Antialiasing);
+            painter->drawRoundedRect(option->rect.adjusted(0, 0, -1, -1), 2, 2);
+            painter->restore();
+        }
+        return;
+    case PE_IndicatorProgressChunk:
+        if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+            QStyleOptionProgressBar copy = *pb;
+            // QProgressBar::chunk { background-color: #D6E8FB; }
+            copy.palette.setColor(QPalette::Highlight, QColor(0xD6, 0xE8, 0xFB)); // qwindowsstyle.cpp uses Highlight
+            QProxyStyle::drawPrimitive(element, &copy, painter, widget);
+        }
+        return;
+    default:
+        break;
+    }
+    QProxyStyle::drawPrimitive(element, option, painter, widget);
+}
+
+void ThornStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
+{
+    switch (element) {
+    case CE_Splitter: // currently unused, but consistent with PE_IndicatorDockWidgetResizeHandle
+        if (option->state & QStyle::State_MouseOver) {
+            painter->fillRect(option->rect, QColor(0xCC, 0xDF, 0xFF));
+            return;
+        }
+        break;
+    case CE_TabBarTab:
+        /* border: 1px solid #AAAAAA;
+           background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 #999999, stop:1 #DDDDDD);
+           color: #000000;
+           border-bottom-color: #BBBBBB; // same as the pane color
+           border-top-left-radius: 4px;
+           border-top-right-radius: 4px;
+        */
+        if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
+            QRect tabRect = tab->rect;
+            QColor borderColor;
+            QLinearGradient gradient(0, 0, 0, tab->rect.height());
+            const bool selected = tab->state & State_Selected;
+            if (selected) {
+                // QTabBar::tab:top:selected said
+                // background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 #E0E0E0, stop:1 #EEEEEE);
+                gradient.setColorAt(0, QColor(0xE0, 0xE0, 0xE0));
+                gradient.setColorAt(1, QColor(0xEE, 0xEE, 0xEE));
+                // border: 1px solid #E0E0E0;
+                borderColor = QColor(0xE0, 0xE0, 0xE0);
+            } else {
+                gradient.setColorAt(0, QColor(0x99, 0x99, 0x99));
+                gradient.setColorAt(1, QColor(0xDD, 0xDD, 0xDD));
+                borderColor = QColor(0xAA, 0xAA, 0xAA);
+            }
+
+            QRect roundedRect;
+            // Ruse: we draw a rounded rect that is too big at the bottom (or top, for South), but clipped, so that it looks square
+            switch (tab->shape) {
+            case QTabBar::RoundedNorth:
+            case QTabBar::TriangularNorth:
+                if (!selected) {
+                    // QTabBar::tab:top:!selected { margin-top: 2px; }
+                    tabRect.adjust(0, 2, 0, 0);
+                }
+                roundedRect = tabRect.adjusted(0, 0, 0, 5);
+                break;
+            case QTabBar::RoundedSouth:
+            case QTabBar::TriangularSouth:
+                if (!selected) {
+                    // QTabBar::tab:bottom:!selected { margin-bottom: 2px }
+                    tabRect.adjust(0, 0, 0, -2);
+                }
+                roundedRect = tabRect.adjusted(0, -5, 0, 0);
+                break;
+            default:
+                qWarning() << "Vertical tabbars not implemented yet, call David";
+            }
+
+            // Draw tab shape
+            painter->save();
+            painter->setClipRect(tabRect);
+            painter->setPen(borderColor);
+            painter->setBrush(gradient);
+            painter->setRenderHint(QPainter::Antialiasing);
+            painter->drawRoundedRect(roundedRect, 4, 4);
+            painter->restore();
+
+            // Draw tab text
+            QStyleOptionTab modifiedOption = *tab;
+            modifiedOption.palette.setColor(QPalette::WindowText, Qt::black);
+            QProxyStyle::drawControl(CE_TabBarTabLabel, &modifiedOption, painter, widget);
+        }
+        return;
+    case CE_ToolBar:
+        {
+            /*
+             top or bottom: background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 #999999, stop:1 #DDDDDD)
+             left or right: background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 #DDDDDD, stop:1 #999999)
+            */
+            QLinearGradient gradient;
+            if (option->state & State_Horizontal) {
+                gradient.setStart(0, 0);
+                gradient.setFinalStop(0, option->rect.height());
+            } else {
+                gradient.setStart(0, 0);
+                gradient.setFinalStop(option->rect.width(), 0);
+            }
+            gradient.setColorAt(0, QColor(0xDD, 0xDD, 0xDD));
+            gradient.setColorAt(1, QColor(0x99, 0x99, 0x99));
+            painter->fillRect(option->rect, gradient);
+        }
+        return;
+    case CE_PushButtonBevel:
+        painter->save();
+        painter->setRenderHint(QPainter::Antialiasing);
+        if (option->state & State_MouseOver) {
+            // QPushButton:hover { border: 1px solid #AAAAAA; border-radius: 3px; background-color: #DDEFFF; }
+            painter->setPen(QPen(QColor(0xAA, 0xAA, 0xAA)));
+            painter->setBrush(QColor(0xDD, 0xEF, 0xFF));
+        } else if ((option->state & State_On) || (option->state & State_Sunken)) {
+            // QPushButton::checked, QPushButton::pressed { border: 1px solid #AAAAAA; border-radius: 2px; background-color: qlineargradient(x1:0, y1:1, x2:0, y2:0, stop:0 #E0E0EA, stop:1 #BBCFFF); }
+            QLinearGradient gradient;
+            gradient.setStart(0, 0);
+            gradient.setFinalStop(0, option->rect.height());
+            gradient.setColorAt(0, QColor(0xE0, 0xE0, 0xEA));
+            gradient.setColorAt(1, QColor(0xBB, 0xCF, 0xFF));
+            painter->setPen(QPen(QColor(0xAA, 0xAA, 0xAA)));
+            painter->setBrush(gradient);
+        } else {
+            // QPushButton::enabled { border: 1px solid #AAAAAA; border-radius: 3px; background-color: qlineargradient(x1:0, y1:1, x2:0, y2:0, stop:0 #999999, stop:1 #DDDDDD); }
+            // QPushButton::!enabled { border: 1px solid #808080; + same background as Qt does the sunken effect on these, and that looks fine. }
+            QLinearGradient gradient;
+            gradient.setStart(0, 0);
+            gradient.setFinalStop(0, option->rect.height());
+            gradient.setColorAt(0, QColor(0xDD, 0xDD, 0xDD));
+            gradient.setColorAt(1, QColor(0x99, 0x99, 0x99));
+            if (option->state & State_Enabled)
+                painter->setPen(QPen(QColor(0xAA, 0xAA, 0xAA)));
+            else
+                painter->setPen(QPen(QColor(0x80, 0x80, 0x80)));
+            painter->setBrush(gradient);
+        }
+        painter->drawRoundedRect(option->rect.adjusted(0, 0, -1, -1), 3, 3);
+        painter->restore();
+        return;
+    case CE_MenuItem:
+        if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+            int x, y, w, h;
+            menuitem->rect.getRect(&x, &y, &w, &h);
+            const bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable ? menuitem->checked : false;
+            const bool selected = menuitem->state & State_Selected;
+            const bool disabled = !(menuitem->state & State_Enabled);
+
+            QColor textColor;
+            if (selected) {
+                // QMenu::item:selected { background-color: #80AFFF; }
+                const QColor fill(0x80, 0xAF, 0xFF);
+                painter->fillRect(menuitem->rect.adjusted(0, 0, -1, 0), fill);
+
+                // QMenu::item:selected { color: #FFFFFF; }
+                textColor = QColor(0xFF, 0xFF, 0xFF);
+            } else {
+                if (disabled) {
+                    // QMenu::item:!enabled { color: #AAAAAA; }
+                    textColor = QColor(0xAA, 0xAA, 0xAA);
+                } else {
+                    // QMenu::item:enabled { color: #000000; }
+                    textColor = Qt::black;
+                }
+            }
+
+            if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
+                /* QMenu::separator {
+                       height: 2px;
+                       background: #AAAAAA;
+                       margin-left: 10px;
+                       margin-right: 5px;
+                   } */
+                const int yoff = y-1 + h / 2;
+                painter->fillRect(x + 10, yoff, w - 15, 2, QColor(0xAA, 0xAA, 0xAA));
+                return;
+            }
+
+            // taken from qwindowsstyle.cpp, and modified (due to the width of the checkmarks)
+
+            const bool checkable = menuitem->checkType != QStyleOptionMenuItem::NotCheckable;
+            const int checkcol = qMax<int>(menuitem->maxIconWidth, m_checkboxCheckedPixmap.width() + 4);
+            const QRect vCheckRect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x(), menuitem->rect.y(), checkcol, menuitem->rect.height()));
+
+            // Draw icon or checkmark
+            QPixmap pixmap;
+            if (!menuitem->icon.isNull()) {
+                QIcon::Mode mode = disabled ? QIcon::Disabled : QIcon::Normal;
+                if (selected && !disabled)
+                    mode = QIcon::Active;
+                if (checked)
+                    pixmap = menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On);
+                else
+                    pixmap = menuitem->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), mode);
+            } else if (checkable) {
+                if (!selected) {
+                    if (menuitem->checkType & QStyleOptionMenuItem::Exclusive) {
+                        pixmap = menuitem->checked ? m_radiobuttonCheckedPixmap :  m_radiobuttonUncheckedPixmap;
+                    } else {
+                        pixmap = menuitem->checked ? m_checkboxCheckedPixmap :  m_checkboxUncheckedPixmap;
+                    }
+                } else {
+                    if (menuitem->checkType & QStyleOptionMenuItem::Exclusive) {
+                        pixmap = menuitem->checked ? m_radiobuttonCheckedHoverPixmap :  m_radiobuttonUncheckedHoverPixmap;
+                    } else {
+                        pixmap = menuitem->checked ? m_checkboxCheckedHoverPixmap :  m_checkboxUncheckedHoverPixmap;
+                    }
+                }
+            }
+            QRect pmr(QPoint(0, 0), pixmapSize(pixmap));
+            pmr.moveCenter(vCheckRect.center());
+            painter->setPen(menuitem->palette.text().color());
+            painter->drawPixmap(pmr.topLeft(), pixmap);
+
+            const int itemFrame = 2;
+            const int itemHMargin = 2;
+            const int itemVMargin = 2;
+            const int rightBorder = 15;
+            const int arrowHMargin = 6;
+            const int xm = itemFrame + checkcol + itemHMargin;
+            const int xpos = menuitem->rect.x() + xm;
+            painter->setPen(textColor);
+            QRect textRect(xpos, y + itemVMargin,
+                           w - xm - rightBorder - menuitem->tabWidth + 1, h - 2 * itemVMargin);
+            QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect);
+            QString s(menuitem->text);
+            if (!s.isEmpty()) {                     // draw text
+                int t = s.indexOf(QLatin1Char('\t'));
+                int text_flags = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
+                if (!styleHint(SH_UnderlineShortcut, menuitem, widget))
+                    text_flags |= Qt::TextHideMnemonic;
+                if (t >= 0) {
+                    QRect vShortcutRect = visualRect(option->direction, menuitem->rect,
+                        QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
+                    const QString textToDraw = s.mid(t + 1);
+                    painter->drawText(vShortcutRect, text_flags, textToDraw);
+                    s = s.left(t);
+                }
+                QFont font = menuitem->font;
+                if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
+                    font.setBold(true);
+                painter->setFont(font);
+                const QString textToDraw = s.left(t);
+                painter->drawText(vTextRect, text_flags, textToDraw);
+            }
+            if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) { // draw sub menu arrow
+                const int dim = (h - 2 * itemFrame) / 2;
+                const PrimitiveElement arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+                const int submenuXPos = x + w - arrowHMargin - itemFrame - dim;
+                const QRect vSubMenuRect = visualRect(option->direction, menuitem->rect, QRect(submenuXPos, y + h / 2 - dim / 2, dim, dim));
+                QStyleOptionMenuItem newMI = *menuitem;
+                newMI.rect = vSubMenuRect;
+                newMI.state = disabled ? State_None : State_Enabled;
+                if (selected) {
+                    newMI.palette.setColor(QPalette::ButtonText, textColor);
+                }
+                QProxyStyle::drawPrimitive(arrow, &newMI, painter, widget);
+            }
+        }
+        return;
+    case CE_CheckBoxLabel:
+    case CE_RadioButtonLabel:
+        if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
+            QStyleOptionButton modifiedOption = *btn;
+            const bool disabled = !(option->state & State_Enabled);
+            // QCheckBox:!enabled { color: #000000; }
+            // QCheckBox:enabled { color: #FFFFFF }
+            modifiedOption.palette.setColor(QPalette::WindowText, disabled ? Qt::black : Qt::white);
+            QProxyStyle::drawControl(element, &modifiedOption, painter, widget);
+        }
+        return;
+    case CE_ComboBoxLabel:
+        if (option->state & State_Enabled) {
+            painter->setPen(Qt::black);
+        } else {
+            // disabled text light enough to be legible but not as stark as white
+            painter->setPen(QColor(0xEE, 0xEE, 0xEE));
+        }
+        QCommonStyle::drawControl(element, option, painter, widget);
+        return;
+    case CE_Header:
+        // Copied straight from qwindowsstyle.cpp because the subElementRect calls don't use proxy()->.
+        if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+            QRegion clipRegion = painter->clipRegion();
+            painter->setClipRect(option->rect);
+            drawControl(CE_HeaderSection, header, painter, widget);
+            QStyleOptionHeader subopt = *header;
+            subopt.rect = subElementRect(SE_HeaderLabel, header, widget);
+            if (subopt.rect.isValid()) {
+                subopt.palette.setColor(QPalette::ButtonText, Qt::white); // QHeaderView::section { color: #FFFFFF; }
+                drawControl(CE_HeaderLabel, &subopt, painter, widget);
+            }
+            if (header->sortIndicator != QStyleOptionHeader::None) {
+                subopt.rect = subElementRect(SE_HeaderArrow, option, widget);
+                drawPrimitive(PE_IndicatorHeaderArrow, &subopt, painter, widget);
+            }
+            painter->setClipRegion(clipRegion);
+        }
+        return;
+    case CE_HeaderSection:
+        if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+            // QHeaderView::section { background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 #707070, stop:1 #808080); }
+            QLinearGradient gradient;
+            gradient.setStart(0, 0);
+            gradient.setFinalStop(0, option->rect.height());
+            gradient.setColorAt(0, QColor(0x80, 0x80, 0x80));
+            gradient.setColorAt(1, QColor(0x70, 0x70, 0x70));
+            painter->setBrush(gradient);
+            // border: 1px solid #AAAAAA;
+            painter->setPen(QPen(QColor(0xAA, 0xAA, 0xAA)));
+            painter->drawRect(header->rect);
+            // Not converted, not sure what it did:   padding-left: 4px; padding-right: 1em;
+        }
+        return;
+    case CE_MenuBarItem:
+        if (const QStyleOptionMenuItem *m = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
+            QStyleOptionMenuItem modifiedOption(*m);
+            if (option->state & State_Selected) {
+                // QMenuBar::item:selected { background-color: #80AFFF; }
+                modifiedOption.palette.setColor(QPalette::Button, QColor(0x80, 0xAF, 0xFF));
+            } else if (option->state & State_Sunken) {
+                // QMenuBar::item:pressed { background-color: #BBCEFF; }
+                modifiedOption.palette.setColor(QPalette::Button, QColor(0xBB, 0xCE, 0xFF));
+            } else {
+                // QMenuBar { background-color: #404040; }
+                modifiedOption.palette.setColor(QPalette::Button, QColor(0x40, 0x40, 0x40));
+            }
+            // QMenuBar::item:selected { color: #FFFFFF; }
+            // QMenuBar::item { color: #FFFFFF; }
+            modifiedOption.palette.setColor(QPalette::ButtonText, Qt::white);
+
+            QProxyStyle::drawControl(element, &modifiedOption, painter, widget);
+        }
+        return;
+    case CE_MenuBarEmptyArea:
+        // QMenuBar { background-color: #404040; }
+        painter->fillRect(option->rect, QColor(0x40, 0x40, 0x40));
+        return;
+    case CE_ScrollBarSubLine:
+    case CE_ScrollBarAddLine:
+        {
+            // QScrollBar::add-line:horizontal { border: 2px solid #404040; background: #808080; } but the 2px border didn't appear
+            painter->fillRect(option->rect, QColor(0x80, 0x80, 0x80));
+            // taken from qwindowsstyle.cpp
+            PrimitiveElement arrow;
+            if (option->state & State_Horizontal) {
+                if (element == CE_ScrollBarAddLine)
+                    arrow = option->direction == Qt::LeftToRight ? PE_IndicatorArrowRight : PE_IndicatorArrowLeft;
+                else
+                    arrow = option->direction == Qt::LeftToRight ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
+            } else {
+                if (element == CE_ScrollBarAddLine)
+                    arrow = PE_IndicatorArrowDown;
+                else
+                    arrow = PE_IndicatorArrowUp;
+            }
+            QStyleOption arrowOpt = *option;
+            // QScrollBar:*-arrow { width: 8px; height 8px; }
+            arrowOpt.rect = alignedRect(option->direction, Qt::AlignCenter, QSize(8, 8), option->rect);
+            drawPrimitive(arrow, &arrowOpt, painter, widget);
+        }
+        return;
+    case CE_ScrollBarSubPage:
+    case CE_ScrollBarAddPage:
+        // Nothing to be done, CC_ScrollBar did the full fillRect()
+        return;
+    case CE_ScrollBarSlider:
+        {
+            // QScrollBar::handle:horizontal { background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 #999999, stop:1 #DDDDDD); }
+            // QScrollBar::handle:vertical { background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 #DDDDDD, stop:1 #999999); }
+            QLinearGradient gradient;
+            gradient.setStart(0, 0);
+            if (option->state & State_Horizontal)
+                gradient.setFinalStop(0, option->rect.height());
+            else
+                gradient.setFinalStop(option->rect.width(), 0);
+            gradient.setColorAt(0, QColor(0xDD, 0xDD, 0xDD));
+            gradient.setColorAt(1, QColor(0x99, 0x99, 0x99));
+            painter->fillRect(option->rect, gradient);
+        }
+        return;
+    case CE_DockWidgetTitle: // titlebar on floating dock widgets
+        if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
+            const bool active = dwOpt->state & State_Active;
+            const bool floating = dwOpt->movable && (dwOpt->state & State_Window);
+            QStyleOptionDockWidget copy = *dwOpt;
+            copy.palette.setColor(QPalette::Dark, Qt::transparent); // QCommonStyle draws a border around the title with this color, we don't want it
+            if (floating && !active)
+                copy.palette.setColor(QPalette::WindowText, QColor(0xAA, 0xAA, 0xAA));
+            else
+                copy.palette.setColor(QPalette::WindowText, Qt::white);
+            // Don't use QWindowsStyle for this, it uses black for the floating && !active case, cached from the initial app palette.
+            QCommonStyle::drawControl(element, &copy, painter, widget);
+        }
+        return;
+    case CE_ProgressBarGroove:
+        if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+            // QProgressBar { background: #FFFFFF; border: 1px solid #AAAAAA; border-radius: 3px; }
+            painter->save();
+            painter->setBrush(Qt::white);
+            painter->setPen(QColor(0xAA, 0xAA, 0xAA));
+            painter->drawRoundedRect(pb->rect.adjusted(0, 0, -1, -1), 3, 3);
+            painter->restore();
+        }
+        return;
+    case CE_ProgressBarLabel:
+        if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+            // QProgressBar { color: #000000; text-align: center; }
+            QStyleOptionProgressBar copy = *pb;
+            copy.palette.setColor(QPalette::HighlightedText, Qt::black);
+            QProxyStyle::drawControl(element, &copy, painter, widget);
+        }
+        return;
+    default:
+        break;
+    }
+    QProxyStyle::drawControl(element, option, painter, widget);
+}
+
+void ThornStyle::drawComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const
+{
+    switch (control) {
+    case CC_ScrollBar:
+        if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+            // QScrollBar { border: 2px solid #404040; background-color: none; }
+            // QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { background: #404040; }
+            // Done here by filling the whole scrollbar, and moving the subcontrols by 2 pixels, then drawing nothing for AddPage/SubPage
+            painter->fillRect(scrollBar->rect, QColor(0x40, 0x40, 0x40));
+        }
+        break; // let the base class do the rest
+    case CC_Slider:
+        if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+            const QRect groove = subControlRect(CC_Slider, slider, SC_SliderGroove, widget);
+            const QRect handle = subControlRect(CC_Slider, slider, SC_SliderHandle, widget);
+            if ((slider->subControls & SC_SliderGroove) && groove.isValid()) {
+                // QSlider::groove:horizontal { background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #E0E0E0, stop:1 #EEEEEE); }
+                QLinearGradient gradient;
+                gradient.setStart(0, 0);
+                if (option->state & State_Horizontal)
+                    gradient.setFinalStop(0, option->rect.height());
+                else
+                    gradient.setFinalStop(option->rect.width(), 0);
+                gradient.setColorAt(0, QColor(0xE0, 0xE0, 0xE0));
+                gradient.setColorAt(1, QColor(0xEE, 0xEE, 0xEE));
+                painter->fillRect(groove, gradient);
+            }
+#if 0 // no tickmarks
+            if (slider->subControls & SC_SliderTickmarks) {
+                QStyleOptionSlider tmpSlider = *slider;
+                tmpSlider.subControls = SC_SliderTickmarks;
+                QCommonStyle::drawComplexControl(control, &tmpSlider, painter, widget);
+            }
+#endif
+            if (slider->subControls & SC_SliderHandle) {
+                // QSlider::handle:horizontal { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #b4b4b4, stop:1 #8f8f8f);
+                //                              border: 1px solid #5c5c5c; border-radius: 3px; }
+                QLinearGradient gradient;
+                gradient.setStart(0, 0);
+                gradient.setFinalStop(option->rect.width(), option->rect.height());
+                gradient.setColorAt(0, QColor(0xB4, 0xB4, 0xB4));
+                gradient.setColorAt(1, QColor(0x8F, 0x8F, 0x8F));
+                painter->setPen(QColor(0x5C, 0x5C, 0x5C));
+                painter->setBrush(gradient);
+                painter->setRenderHint(QPainter::Antialiasing);
+                painter->drawRoundedRect(handle, 3, 3);
+            }
+        }
+        return;
+    case CC_GroupBox:
+        if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) {
+            QStyleOptionGroupBox copy = *groupBox;
+            // QGroupBox::title { subcontrol-position: top center; }
+            copy.textAlignment = Qt::AlignHCenter;
+
+            // Draw frame
+            QRect textRect = subControlRect(CC_GroupBox, &copy, SC_GroupBoxLabel, widget);
+            QRect checkBoxRect = subControlRect(CC_GroupBox, &copy, SC_GroupBoxCheckBox, widget);
+            if (groupBox->subControls & QStyle::SC_GroupBoxFrame) {
+                QStyleOptionFrame frame;
+                frame.QStyleOption::operator=(*groupBox);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+                frame.features = groupBox->features;
+#endif
+                frame.lineWidth = groupBox->lineWidth;
+                frame.midLineWidth = groupBox->midLineWidth;
+                frame.rect = subControlRect(CC_GroupBox, &copy, SC_GroupBoxFrame, widget);
+                painter->save();
+
+                QRegion region(groupBox->rect);
+                if (!groupBox->text.isEmpty()) {
+                    bool ltr = groupBox->direction == Qt::LeftToRight;
+                    QRect finalRect;
+                    if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) {
+                        finalRect = checkBoxRect.united(textRect);
+                        finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
+                    } else {
+                        finalRect = textRect;
+                    }
+                    region -= finalRect;
+                }
+
+                // Draw background without clipping
+                painter->setPen(Qt::NoPen);
+                painter->setBrush(QColor(0x40, 0x40, 0x40));
+                painter->drawRoundedRect(frame.rect.adjusted(2, 2, -2, -2), 4, 4);
+
+                painter->setClipRegion(region);
+                // Draw frame with clipping
+                drawPrimitive(PE_FrameGroupBox, &frame, painter, widget);
+                painter->restore();
+            }
+
+            copy.subControls &= ~SC_GroupBoxFrame;
+            QProxyStyle::drawComplexControl(control, &copy, painter, widget);
+        }
+        return;
+    case CC_ToolButton:
+        if (const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
+            QRect button = subControlRect(control, toolbutton, SC_ToolButton, widget);
+            QRect menuarea = subControlRect(control, toolbutton, SC_ToolButtonMenu, widget);
+            State bflags = toolbutton->state & ~State_Sunken;
+
+            /*if (bflags & State_AutoRaise) {
+                if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) {
+                    bflags &= ~State_Raised;
+                }
+            }*/
+            State mflags = bflags;
+            if (toolbutton->state & State_Sunken) {
+                if (toolbutton->activeSubControls & SC_ToolButton)
+                    bflags |= State_Sunken;
+                else // added this "else" so that the down arrow only shifts by +1,+1 when actually clicking on it, not when clicking on the main toolbutton
+                    mflags |= State_Sunken;
+            }
+
+            QStyleOption tool = *toolbutton;
+            if (toolbutton->subControls & SC_ToolButton) {
+                tool.rect = button;
+                tool.state = bflags;
+                drawPrimitive(PE_PanelButtonTool, &tool, painter, widget);
+            }
+            QStyleOptionToolButton label = *toolbutton;
+            label.state = bflags;
+            int fw = pixelMetric(PM_DefaultFrameWidth, option, widget);
+            label.rect = button.adjusted(fw, fw, -fw, -fw);
+            // QToolButton { color: #FFFFFF; }
+            label.palette.setColor(QPalette::ButtonText, Qt::white);
+            drawControl(CE_ToolButtonLabel, &label, painter, widget);
+
+            if (mflags & State_Sunken) {
+                // QToolButton::menu-arrow:open { top: 1px; left: 1px; /* shift it a bit */ }
+                painter->translate(1, 1);
+            }
+            if (toolbutton->subControls & SC_ToolButtonMenu) { // popupMode == QToolButton::MenuButtonPopup
+                tool.rect = menuarea;
+                tool.state = mflags;
+                drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget);
+            } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) { // InstantPopup, like QPushButton menu
+                // "the arrow on tool buttons with menus in InstantPopup mode is intentionally styled out of existence"
+            }
+        }
+        return;
+    case CC_ComboBox:
+        if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+            if ((cmb->subControls & SC_ComboBoxFrame)) {
+                // QComboBox { border: 1px solid #AAAAAA; border-radius: 3px; }
+                // QComboBox::!enabled { border: 1px solid #808080; border-radius: 3px; }
+                if (option->state & State_Enabled)
+                    painter->setPen(QColor(0xAA, 0xAA, 0xAA));
+                else
+                    painter->setPen(QColor(0x80, 0x80, 0x80));
+
+                if (option->state & State_MouseOver) {
+                    // QComboBox:hover { background-color: #CCDFFF; }
+                    painter->setBrush(QColor(0xCC, 0xDF, 0xFF));
+                } else if (cmb->editable) {
+                    // QComboBox::editable { background-color: #FFFFFF; }
+                    painter->setBrush(Qt::white);
+                } else {
+                    // QComboBox::!editable { background-color: qlineargradient(x1:0, y1:1, x2:0, y2:0, stop:0 #999999, stop:1 #DDDDDD); }
+                    // QComboBox::!editable::on { background-color: qlineargradient(x1:0, y1:1, x2:0, y2:0, stop:0 #E0E0E0, stop:1 #EEEEEE); }
+                    QLinearGradient gradient;
+                    gradient.setStart(0, 0);
+                    gradient.setFinalStop(0, option->rect.height());
+                    if (option->state & State_On) {
+                        gradient.setColorAt(0, QColor(0xEE, 0xEE, 0xEE));
+                        gradient.setColorAt(1, QColor(0xE0, 0xE0, 0xE0));
+                    } else {
+                        if (qobject_cast<QToolBar*>(widget->parentWidget())) {
+                            gradient.setColorAt(0, QColor(0xEE, 0xEE, 0xEE));
+                            gradient.setColorAt(1, QColor(0xDD, 0xDD, 0xDD));
+                        } else {
+                            gradient.setColorAt(0, QColor(0xDD, 0xDD, 0xDD));
+                            gradient.setColorAt(1, QColor(0x99, 0x99, 0x99));
+                        }
+                    }
+                    painter->setBrush(gradient);
+                }
+                painter->setRenderHint(QPainter::Antialiasing);
+                painter->drawRoundedRect(option->rect, 3, 3);
+            }
+            if (cmb->subControls & SC_ComboBoxArrow) {
+                // from qwindowsstyle.cpp, without the rect around the arrow
+                State flags = State_None;
+                QRect ar = subControlRect(CC_ComboBox, cmb, SC_ComboBoxArrow, widget);
+                bool sunkenArrow = cmb->activeSubControls == SC_ComboBoxArrow
+                        && cmb->state & State_Sunken;
+                ar.adjust(2, 2, -2, -2);
+                if (option->state & State_Enabled)
+                    flags |= State_Enabled;
+                if (option->state & State_HasFocus)
+                    flags |= State_HasFocus;
+
+                if (sunkenArrow)
+                    flags |= State_Sunken;
+                QStyleOption arrowOpt = *cmb;
+                arrowOpt.rect = ar.adjusted(1, 1, -1, -1);
+                arrowOpt.state = flags;
+                drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, painter, widget);
+            }
+#if 0
+            if (cmb->subControls & SC_ComboBoxEditField) {
+                QRect re = subControlRect(CC_ComboBox, cmb, SC_ComboBoxEditField, widget);
+                if (cmb->state & State_HasFocus && !cmb->editable)
+                    p->fillRect(re.x(), re.y(), re.width(), re.height(),
+                                cmb->palette.brush(QPalette::Highlight));
+            }
+#endif
+        }
+        return;
+    case CC_SpinBox:
+        if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+            bool enabled = option->state & State_Enabled;
+            painter->setRenderHint(QPainter::Antialiasing);
+            QPainterPath framePath;
+            // Draw background color (white), clipped to the rounded rect frame,
+            // without drawing that frame yet (we'll do that last).
+            // That same clipping will be useful for the round corner of the up/down buttons
+            QRect frameRect = subControlRect(CC_SpinBox, sb, SC_SpinBoxFrame, widget).adjusted(2, 2, -2, -2);
+            framePath.addRoundedRect(frameRect, 4, 4);
+            painter->setClipPath(framePath);
+            painter->fillRect(frameRect, sb->palette.brush(QPalette::Base));
+            const QRect spinupRect = subControlRect(CC_SpinBox, sb, SC_SpinBoxUp, widget);
+            QLinearGradient gradient;
+            gradient.setColorAt(0, QColor(0xDD, 0xDD, 0xDD));
+            gradient.setColorAt(1, QColor(0x99, 0x99, 0x99));
+            painter->setPen(QColor(0xAA, 0xAA, 0xAA));
+            if (sb->subControls & SC_SpinBoxUp) {
+                gradient.setStart(spinupRect.topLeft());
+                gradient.setFinalStop(spinupRect.bottomLeft());
+                if (sb->activeSubControls == SC_SpinBoxUp && !(option->state & State_Sunken))
+                    painter->setBrush(QColor(0xDD, 0xEF, 0xFF));
+                else
+                    painter->setBrush(gradient);
+                painter->drawRect(spinupRect);
+
+                QPixmap pixmap = enabled ? m_arrowUpSmallPixmap
+                                         : m_arrowUpSmallInvertedPixmap; // off state when value is max
+                pixmap = pixmap.scaled(QSize(7, 7), Qt::KeepAspectRatio, Qt::SmoothTransformation); // TODO: edit the PNGs instead, would be faster
+                const QRect drawRect = alignedRect(option->direction, Qt::AlignCenter, pixmap.size(), spinupRect);
+                painter->drawPixmap(drawRect.topLeft(), pixmap);
+            }
+            if (sb->subControls & SC_SpinBoxDown) {
+                const QRect spindownRect = subControlRect(CC_SpinBox, sb, SC_SpinBoxDown, widget);
+                gradient.setStart(spindownRect.topLeft());
+                gradient.setFinalStop(spindownRect.bottomLeft());
+                if (sb->activeSubControls == SC_SpinBoxDown && (option->state & State_MouseOver))
+                    painter->setBrush(QColor(0xDD, 0xEF, 0xFF));
+                else
+                    painter->setBrush(gradient);
+                painter->drawRect(spindownRect);
+
+                QPixmap pixmap = enabled ? m_arrowDownSmallPixmap
+                                         : m_arrowDownSmallInvertedPixmap; // off state when value is max
+                pixmap = pixmap.scaled(QSize(7, 7), Qt::KeepAspectRatio, Qt::SmoothTransformation); // TODO: edit the PNGs instead, would be faster
+                const QRect drawRect = alignedRect(option->direction, Qt::AlignCenter, pixmap.size(), spindownRect);
+                painter->drawPixmap(drawRect.topLeft(), pixmap);
+            }
+            painter->setClipping(false);
+            painter->setBrush(Qt::NoBrush);
+            painter->setPen(QColor(0xB0, 0xB0, 0xB0)); // grabbed in frame.png
+            painter->drawRoundedRect(frameRect, 4, 4);
+        }
+        return;
+    default:
+        break;
+    }
+    QProxyStyle::drawComplexControl(control, option, painter, widget);
+}
+
+QSize ThornStyle::sizeFromContents(QStyle::ContentsType type, const QStyleOption *option, const QSize &size, const QWidget *widget) const
+{
+    QSize sz = QProxyStyle::sizeFromContents(type, option, size, widget);
+    switch (type) {
+    case CT_LineEdit:
+        // Reduce size of lineedits, to make the NameSetEditor more compact (in the BankEditorDialog)
+        sz -= QSize(2, 2);
+        break;
+    case CT_SpinBox:
+        if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+            // Add button + frame widths
+            const int buttonWidth = m_spinupPixmap.width();
+            const int fw = vopt->frame ? pixelMetric(PM_SpinBoxFrameWidth, vopt, widget) : 0;
+            sz += QSize(buttonWidth + 2*fw, 2*fw);
+        }
+        break;
+    default:
+        break;
+    }
+    return sz;
+}
+
+QRect ThornStyle::subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const
+{
+    QRect rect = QProxyStyle::subElementRect(element, option, widget);
+    switch (element) {
+    case SE_TabWidgetTabBar:
+        // QTabWidget::tab-bar { left: 5px; /* move to the right by 5px */ }
+        return rect.translated(5, 0);
+    case SE_TabWidgetTabPane:
+        return rect;
+    case SE_TabWidgetTabContents:
+        return rect.adjusted(2, 2, -2, -2);
+    case SE_HeaderArrow: {
+        const QSize size = pixmapSize(m_arrowUpSmallInvertedPixmap);
+        QRect sectionRect = option->rect.adjusted(0, 0, -5, 0);
+        const QRect ret = alignedRect(option->direction, Qt::AlignRight | Qt::AlignVCenter, size, sectionRect);
+        return ret;
+    }
+    case SE_ToolBarHandle:
+        if (const QStyleOptionToolBar *tbopt = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
+            // initially from qcommonstyle.cpp
+            if (tbopt->features & QStyleOptionToolBar::Movable) {
+                const QToolBar *tb = qobject_cast<const QToolBar*>(widget);
+                const int margin = 1;
+                const int handleExtent = pixelMetric(QStyle::PM_ToolBarHandleExtent, option, tb);
+                QRect ret;
+                if (tbopt->state & QStyle::State_Horizontal) {
+                    ret = QRect(margin, margin, handleExtent, tbopt->rect.height() - 2*margin);
+                    ret = QStyle::visualRect(tbopt->direction, tbopt->rect, ret);
+                } else {
+                    ret = QRect(margin, margin, tbopt->rect.width() - 2*margin, handleExtent);
+                }
+                return ret;
+            }
+        }
+        break;
+    default:
+        break;
+    }
+
+    return rect;
+}
+
+QRect ThornStyle::subControlRect(QStyle::ComplexControl cc, const QStyleOptionComplex *option, QStyle::SubControl sc, const QWidget *widget) const
+{
+    switch (cc) {
+    case CC_ScrollBar:
+        if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+            QStyleOptionSlider copy = *scrollBar;
+            // QScrollBar said { border: 2px solid #404040; }
+            // We draw that border by filling everything and reducing the available size for contents
+            copy.rect.adjust(2, 2, -2, -2);
+            return QProxyStyle::subControlRect(cc, &copy, sc, widget).adjusted(2, 2, 2, 2);
+        }
+        break;
+    case CC_Slider:
+        if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+            //QSlider::groove:horizontal { height: 5px; }
+            const int grooveThickness = 5;
+            QRect ret;
+
+            switch (sc) {
+            case SC_SliderHandle: {
+                int sliderPos = 0;
+                // QSlider::handle:horizontal { width: 8px; }, but this needs to be 9 to look like the old qss for some reason
+                int len = 9;
+                bool horizontal = slider->orientation == Qt::Horizontal;
+                sliderPos = sliderPositionFromValue(slider->minimum, slider->maximum,
+                                                    slider->sliderPosition,
+                                                    (horizontal ? slider->rect.width()
+                                                                : slider->rect.height()) - len,
+                                                    slider->upsideDown);
+                if (horizontal) {
+                    ret.setRect(slider->rect.x() + sliderPos, slider->rect.y() + 1, len, slider->rect.height() - 2);
+                } else {
+                    ret.setRect(slider->rect.x() + 1, slider->rect.y() + sliderPos, slider->rect.width() - 2, len);
+                }
+                //qDebug() << "ret=" << ret;
+                break;
+            }
+            case SC_SliderGroove:
+                if (slider->orientation == Qt::Horizontal) {
+                    const int yOff = (slider->rect.height() - grooveThickness ) / 2;
+                    ret.setRect(slider->rect.x(), slider->rect.y() + yOff,
+                                slider->rect.width(), grooveThickness);
+                } else {
+                    const int xOff = (slider->rect.width() - grooveThickness ) / 2;
+                    ret.setRect(slider->rect.x() + xOff, slider->rect.y(),
+                                grooveThickness, slider->rect.height());
+                }
+                break;
+            default:
+                break;
+            }
+            return visualRect(slider->direction, slider->rect, ret);
+        }
+        break;
+    case CC_SpinBox:
+        if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
+            const int fw = 3; // frame width
+            const int buttonTopBottomMargin = fw - 1;
+            const QSize buttonSize(18, (sb->rect.height() - buttonTopBottomMargin) / 2 - 1);
+            const int x = sb->rect.x() + sb->rect.width() - fw - buttonSize.width();
+            const int y = sb->rect.y() + buttonTopBottomMargin;
+            QRect ret;
+            switch (sc) {
+            case SC_SpinBoxFrame:
+                return sb->rect;
+            case SC_SpinBoxUp:
+                if (sb->buttonSymbols == QAbstractSpinBox::NoButtons)
+                    return QRect();
+                ret = QRect(x, y, buttonSize.width(), buttonSize.height());
+                break;
+            case SC_SpinBoxDown:
+                if (sb->buttonSymbols == QAbstractSpinBox::NoButtons)
+                    return QRect();
+                ret = QRect(x, sb->rect.height() - buttonTopBottomMargin - buttonSize.height(), buttonSize.width(), buttonSize.height());
+                break;
+            case SC_SpinBoxEditField:
+                if (sb->buttonSymbols == QAbstractSpinBox::NoButtons) {
+                    ret = QRect(sb->rect.x() + fw, fw, sb->rect.width() - 2*fw, sb->rect.height() - 2*fw);
+                } else {
+                    ret = QRect(sb->rect.x() + fw, fw, x - fw, sb->rect.height() - 2*fw);
+                }
+                break;
+            default:
+                break;
+            }
+            return visualRect(sb->direction, sb->rect, ret);
+        }
+        break;
+    default:
+        break;
+    }
+
+    return QProxyStyle::subControlRect(cc, option, sc, widget);
+}
+
+#pragma GCC diagnostic pop
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/style/ThornStyle.h	Tue Apr 30 11:36:38 2019 +0100
@@ -0,0 +1,102 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Vect
+    An experimental audio player for plural recordings of a work
+    Centre for Digital Music, Queen Mary, University of London.
+
+    This file is taken from Rosegarden, a MIDI and audio sequencer and
+    musical notation editor. Copyright 2000-2018 the Rosegarden
+    development team. Thorn style developed in stylesheet form by
+    D. Michael McIntyre and reimplemented as a class by David Faure.
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef SV_THORN_STYLE_H
+#define SV_THORN_STYLE_H
+
+#include <QProxyStyle>
+#include <QIcon>
+
+class ThornStyle : public QProxyStyle
+{
+    Q_OBJECT
+
+public:
+    ThornStyle();
+    ~ThornStyle() override;
+
+    static void setEnabled(bool b);
+    static bool isEnabled();
+
+    QPalette standardPalette() const override;
+
+    int styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override;
+    int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget = nullptr) const override;
+
+    void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override;
+    void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override;
+    void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget = nullptr) const override;
+
+    QSize sizeFromContents(ContentsType type, const QStyleOption *option, const QSize &size, const QWidget *widget = nullptr) const override;
+
+    QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget = nullptr) const override;
+    QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget = nullptr) const override;
+
+    QIcon standardIcon(StandardPixmap standardIcon,
+                       const QStyleOption * option = nullptr,
+                       const QWidget * widget = nullptr) const override;
+
+private:
+    QSize pixmapSize(const QPixmap &pixmap) const;
+
+    QPalette m_standardPalette;
+
+    QPixmap m_horizontalToolbarSeparatorPixmap;
+    QPixmap m_verticalToolbarSeparatorPixmap;
+    QPixmap m_checkboxUncheckedPixmap;
+    QPixmap m_checkboxUncheckedHoverPixmap;
+    QPixmap m_checkboxUncheckedDisabledPixmap;
+    QPixmap m_checkboxUncheckedPressedPixmap;
+    QPixmap m_checkboxCheckedPixmap;
+    QPixmap m_checkboxCheckedHoverPixmap;
+    QPixmap m_checkboxCheckedDisabledPixmap;
+    QPixmap m_checkboxCheckedPressedPixmap;
+    QPixmap m_checkboxIndeterminatePixmap;
+    QPixmap m_checkboxIndeterminateHoverPixmap;
+    //QPixmap m_checkboxIndeterminateDisabledPixmap;
+    QPixmap m_checkboxIndeterminatePressedPixmap;
+    QPixmap m_radiobuttonUncheckedPixmap;
+    QPixmap m_radiobuttonUncheckedHoverPixmap;
+    QPixmap m_radiobuttonUncheckedDisabledPixmap;
+    QPixmap m_radiobuttonUncheckedPressedPixmap;
+    QPixmap m_radiobuttonCheckedPixmap;
+    QPixmap m_radiobuttonCheckedHoverPixmap;
+    QPixmap m_radiobuttonCheckedDisabledPixmap;
+    QPixmap m_radiobuttonCheckedPressedPixmap;
+    QPixmap m_arrowDownSmallPixmap;
+    QPixmap m_arrowDownSmallInvertedPixmap;
+    QPixmap m_arrowUpSmallPixmap;
+    QPixmap m_arrowUpSmallInvertedPixmap;
+    QPixmap m_arrowLeftPixmap;
+    QPixmap m_arrowRightPixmap;
+    QPixmap m_arrowUpPixmap;
+    QPixmap m_arrowDownPixmap;
+    QPixmap m_spinupPixmap;
+    QPixmap m_spinupHoverPixmap;
+    QPixmap m_spinupOffPixmap;
+    QPixmap m_spinupPressedPixmap;
+    QPixmap m_spindownPixmap;
+    QPixmap m_spindownHoverPixmap;
+    QPixmap m_spindownOffPixmap;
+    QPixmap m_spindownPressedPixmap;
+    QPixmap m_titleClosePixmap;
+    QPixmap m_titleUndockPixmap;
+};
+
+#endif