// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "qwindowsvistastyle_p.h"
#include "qwindowsvistastyle_p_p.h"
#include "qwindowsvistaanimation_p.h"
#include <qoperatingsystemversion.h>
#include <qscreen.h>
#include <qstylehints.h>
#include <qwindow.h>
#include <private/qstyleanimation_p.h>
#include <private/qstylehelper_p.h>
#include <qpa/qplatformnativeinterface.h>
#include <private/qapplication_p.h>
#include <private/qsystemlibrary_p.h>

#include "qdrawutil.h" // for now
#include <qbackingstore.h>


QT_BEGIN_NAMESPACE

static const int windowsItemFrame        =  2; // menu item frame width
static const int windowsItemHMargin      =  3; // menu item hor text margin
static const int windowsItemVMargin      =  4; // menu item ver text margin
static const int windowsArrowHMargin     =  6; // arrow horizontal margin
static const int windowsRightBorder      = 15; // right border on windows

#ifndef TMT_CONTENTMARGINS
#  define TMT_CONTENTMARGINS 3602
#endif
#ifndef TMT_SIZINGMARGINS
#  define TMT_SIZINGMARGINS 3601
#endif
#ifndef LISS_NORMAL
#  define LISS_NORMAL 1
#  define LISS_HOT 2
#  define LISS_SELECTED 3
#  define LISS_DISABLED 4
#  define LISS_SELECTEDNOTFOCUS 5
#  define LISS_HOTSELECTED 6
#endif
#ifndef BP_COMMANDLINK
#  define BP_COMMANDLINK 6
#  define BP_COMMANDLINKGLYPH 7
#  define CMDLGS_NORMAL 1
#  define CMDLGS_HOT 2
#  define CMDLGS_PRESSED 3
#  define CMDLGS_DISABLED 4
#endif

/* \internal
    Checks if we should use Vista style , or if we should
    fall back to Windows style.
*/
// Theme names matching the QWindowsVistaStylePrivate::Theme enumeration.
static const wchar_t *themeNames[QWindowsVistaStylePrivate::NThemes] =
{
    L"BUTTON",   L"COMBOBOX",   L"EDIT",    L"HEADER",    L"LISTVIEW",
    L"MENU",     L"PROGRESS",   L"REBAR",   L"SCROLLBAR", L"SPIN",
    L"TAB",      L"TASKDIALOG", L"TOOLBAR", L"TOOLTIP",   L"TRACKBAR",
    L"WINDOW",   L"STATUS",     L"TREEVIEW"
};

// QWindowsVistaStylePrivate -------------------------------------------------------------------------
// Static initializations
HWND QWindowsVistaStylePrivate::m_vistaTreeViewHelper = nullptr;
HTHEME QWindowsVistaStylePrivate::m_themes[NThemes];
bool QWindowsVistaStylePrivate::useVistaTheme = false;
Q_CONSTINIT QBasicAtomicInt QWindowsVistaStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting

static void qt_add_rect(HRGN &winRegion, QRect r)
{
    HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
    if (rgn) {
        HRGN dest = CreateRectRgn(0,0,0,0);
        int result = CombineRgn(dest, winRegion, rgn, RGN_OR);
        if (result) {
            DeleteObject(winRegion);
            winRegion = dest;
        }
        DeleteObject(rgn);
    }
}

static HRGN qt_hrgn_from_qregion(const QRegion &region)
{
    HRGN hRegion = CreateRectRgn(0,0,0,0);
    if (region.rectCount() == 1) {
        qt_add_rect(hRegion, region.boundingRect());
        return hRegion;
    }
    for (const QRect &rect : region)
        qt_add_rect(hRegion, rect);
    return hRegion;
}

static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = nullptr)
{
    if (const auto *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
        return pb->state & QStyle::State_Horizontal ? Qt::Horizontal : Qt::Vertical;
    return Qt::Horizontal;
}

/* In order to obtain the correct VistaTreeViewTheme (arrows for PE_IndicatorBranch),
 * we need to set the windows "explorer" theme explicitly on a native
 * window and open the "TREEVIEW" theme handle passing its window handle
 * in order to get Vista-style item view themes (particularly drawBackground()
 * for selected items needs this).
 * We invoke a service of the native Windows interface to create
 * a non-visible window handle, open the theme on it and insert it into
 * the cache so that it is found by QWindowsThemeData::handle() first.
 */
static inline HWND createTreeViewHelperWindow()
{
    using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;

    HWND result = nullptr;
    if (auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration()))
        result = nativeWindowsApp->createMessageWindow(QStringLiteral("QTreeViewThemeHelperWindowClass"),
                                                       QStringLiteral("QTreeViewThemeHelperWindow"));
    return result;
}

enum TransformType { SimpleTransform, HighDpiScalingTransform, ComplexTransform };

static inline TransformType transformType(const QTransform &transform, qreal devicePixelRatio)
{
    if (transform.type() <= QTransform::TxTranslate)
        return SimpleTransform;
    if (transform.type() > QTransform::TxScale)
        return ComplexTransform;
    return qFuzzyCompare(transform.m11(), devicePixelRatio)
            && qFuzzyCompare(transform.m22(), devicePixelRatio)
            ? HighDpiScalingTransform : ComplexTransform;
}

// QTBUG-60571: Exclude known fully opaque theme parts which produce values
// invalid in ARGB32_Premultiplied (for example, 0x00ffffff).
static inline bool isFullyOpaque(const QWindowsThemeData &themeData)
{
    return themeData.theme == QWindowsVistaStylePrivate::TaskDialogTheme && themeData.partId == TDLG_PRIMARYPANEL;
}

static inline QRectF scaleRect(const QRectF &r, qreal factor)
{
    return r.isValid() && factor > 1
            ? QRectF(r.topLeft() * factor, r.size() * factor) : r;
}

static QRegion scaleRegion(const QRegion &region, qreal factor)
{
    if (region.isEmpty() || qFuzzyCompare(factor, qreal(1)))
        return region;
    QRegion result;
    for (const QRect &rect : region)
        result += QRectF(QPointF(rect.topLeft()) * factor, QSizeF(rect.size() * factor)).toRect();
    return result;
}


/* \internal
    Checks if the theme engine can/should be used, or if we should fall back
    to Windows style. For Windows 10, this will still return false for the
    High Contrast themes.
*/
bool QWindowsVistaStylePrivate::useVista(bool update)
{
    if (update)
        useVistaTheme = IsThemeActive() && (IsAppThemed() || !QCoreApplication::instance());
    return useVistaTheme;
}

/* \internal
    Handles refcounting, and queries the theme engine for usage.
*/
void QWindowsVistaStylePrivate::init(bool force)
{
    if (ref.ref() && !force)
        return;
    if (!force) // -1 based atomic refcounting
        ref.ref();

    useVista(true);
    std::fill(m_themes, m_themes + NThemes, nullptr);
}

/* \internal
    Cleans up all static data.
*/
void QWindowsVistaStylePrivate::cleanup(bool force)
{
    if (bufferBitmap) {
        if (bufferDC && nullBitmap)
            SelectObject(bufferDC, nullBitmap);
        DeleteObject(bufferBitmap);
        bufferBitmap = nullptr;
    }

    if (bufferDC)
        DeleteDC(bufferDC);
    bufferDC = nullptr;

    if (ref.deref() && !force)
        return;
    if (!force)  // -1 based atomic refcounting
        ref.deref();

    useVistaTheme = false;
    cleanupHandleMap();
}

bool QWindowsVistaStylePrivate::transitionsEnabled() const
{
    BOOL animEnabled = false;
    if (SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &animEnabled, 0))
    {
        if (animEnabled)
            return true;
    }
    return false;
}

HTHEME QWindowsVistaStylePrivate::openThemeForPrimaryScreenDpi(HWND hwnd, const wchar_t *name)
{
    // We want to call OpenThemeDataForDpi, but it won't link with MinGW (11.2.0), so we
    // dynamically load this.
    using FuncThemeDpi = decltype(&::OpenThemeDataForDpi);

    // Only try to initialize openThemeForDpiFunc once. If it fails, it will likely keep failing.
    const FuncThemeDpi uninitializedFunction = reinterpret_cast<FuncThemeDpi>(1);
    static FuncThemeDpi openThemeForDpiFunc = uninitializedFunction;
    if (openThemeForDpiFunc == uninitializedFunction) {
        QSystemLibrary uxthemeLib(L"uxtheme.dll");
        openThemeForDpiFunc = reinterpret_cast<FuncThemeDpi>(uxthemeLib.resolve("OpenThemeDataForDpi"));
        if (!openThemeForDpiFunc) {
            qWarning() << "QWindowsVistaStylePrivate: Load OpenThemeDataForDpi in uxtheme.dll failed";
        }
    }

    // If we have screens and the openThemeDataForDpi function then use it :).
    if (openThemeForDpiFunc && QGuiApplication::primaryScreen()) {
        const int dpi = qRound(QGuiApplication::primaryScreen()->handle()->logicalDpi().first);
        return openThemeForDpiFunc(hwnd, name, dpi);
    }

    // In case of any issues we fall back to use the plain/old OpenThemeData.
    return OpenThemeData(hwnd, name);
}

int QWindowsVistaStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option, const QWidget *widget)
{
    switch (pm) {
    case QStyle::PM_IndicatorWidth:
        return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width();
    case QStyle::PM_IndicatorHeight:
        return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height();
    case QStyle::PM_ExclusiveIndicatorWidth:
        return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width();
    case QStyle::PM_ExclusiveIndicatorHeight:
        return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height();
    case QStyle::PM_ProgressBarChunkWidth:
        return progressBarOrientation(option) == Qt::Horizontal
                ? QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ProgressTheme, PP_CHUNK).width()
                : QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ProgressTheme, PP_CHUNKVERT).height();
    case QStyle::PM_SliderThickness:
        return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::TrackBarTheme, TKP_THUMB).height();
    case QStyle::PM_TitleBarHeight:
        return QWindowsStylePrivate::pixelMetricFromSystemDp(QStyle::PM_TitleBarHeight, option, widget);
    case QStyle::PM_MdiSubWindowFrameWidth:
        return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width();
    case QStyle::PM_DockWidgetFrameWidth:
        return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::WindowTheme, WP_SMALLFRAMERIGHT, FS_ACTIVE).width();
    default:
        break;
    }
    return QWindowsVistaStylePrivate::InvalidMetric;
}

int QWindowsVistaStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm)
{
    switch (pm) {
    case QStyle::PM_DockWidgetTitleBarButtonMargin:
        return 5;
    case QStyle::PM_ScrollBarSliderMin:
        return 18;
    case QStyle::PM_MenuHMargin:
    case QStyle::PM_MenuVMargin:
        return 0;
    case QStyle::PM_MenuPanelWidth:
        return 3;
    default:
        break;
    }

    return QWindowsVistaStylePrivate::InvalidMetric;
}

bool QWindowsVistaStylePrivate::initVistaTreeViewTheming()
{
    if (m_vistaTreeViewHelper)
        return true;

    m_vistaTreeViewHelper = createTreeViewHelperWindow();
    if (!m_vistaTreeViewHelper) {
        qWarning("Unable to create the treeview helper window.");
        return false;
    }
    if (FAILED(SetWindowTheme(m_vistaTreeViewHelper, L"explorer", nullptr))) {
        qErrnoWarning("SetWindowTheme() failed.");
        cleanupVistaTreeViewTheming();
        return false;
    }
    return true;
}

void QWindowsVistaStylePrivate::cleanupVistaTreeViewTheming()
{
    if (m_vistaTreeViewHelper) {
        DestroyWindow(m_vistaTreeViewHelper);
        m_vistaTreeViewHelper = nullptr;
    }
}

/* \internal
    Closes all open theme data handles to ensure that we don't leak
    resources, and that we don't refer to old handles when for
    example the user changes the theme style.
*/
void QWindowsVistaStylePrivate::cleanupHandleMap()
{
    for (auto &theme : m_themes) {
        if (theme) {
            CloseThemeData(theme);
            theme = nullptr;
        }
    }
    QWindowsVistaStylePrivate::cleanupVistaTreeViewTheming();
}

HTHEME QWindowsVistaStylePrivate::createTheme(int theme, HWND hwnd)
{
    if (Q_UNLIKELY(theme < 0 || theme >= NThemes || !hwnd)) {
        qWarning("Invalid parameters #%d, %p", theme, hwnd);
        return nullptr;
    }
    if (!m_themes[theme]) {
        const wchar_t *name = themeNames[theme];
        if (theme == VistaTreeViewTheme && QWindowsVistaStylePrivate::initVistaTreeViewTheming())
            hwnd = QWindowsVistaStylePrivate::m_vistaTreeViewHelper;
        // Use dpi from primary screen in theme.
        m_themes[theme] = openThemeForPrimaryScreenDpi(hwnd, name);
        if (Q_UNLIKELY(!m_themes[theme]))
            qErrnoWarning("OpenThemeData() failed for theme %d (%s).",
                          theme, qPrintable(themeName(theme)));
    }
    return m_themes[theme];
}

QBackingStore *QWindowsVistaStylePrivate::backingStoreForWidget(const QWidget *widget)
{
    if (QBackingStore *backingStore = widget->backingStore())
        return backingStore;
    if (const QWidget *topLevel = widget->nativeParentWidget())
        if (QBackingStore *topLevelBackingStore = topLevel->backingStore())
            return topLevelBackingStore;
    return nullptr;
}

HDC QWindowsVistaStylePrivate::hdcForWidgetBackingStore(const QWidget *widget)
{
    if (QBackingStore *backingStore = backingStoreForWidget(widget)) {
        QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
        if (nativeInterface)
            return static_cast<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore));
    }
    return nullptr;
}

QString QWindowsVistaStylePrivate::themeName(int theme)
{
    return theme >= 0 && theme < NThemes
            ? QString::fromWCharArray(themeNames[theme]) : QString();
}

bool QWindowsVistaStylePrivate::isItemViewDelegateLineEdit(const QWidget *widget)
{
    if (!widget)
        return false;
    const QWidget *parent1 = widget->parentWidget();
    // Exclude dialogs or other toplevels parented on item views.
    if (!parent1 || parent1->isWindow())
        return false;
    const QWidget *parent2 = parent1->parentWidget();
    return parent2 && widget->inherits("QLineEdit")
            && parent2->inherits("QAbstractItemView");
}

// Returns whether base color is set for this widget
bool QWindowsVistaStylePrivate::isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget)
{
    uint resolveMask = option->palette.resolveMask();
    if (widget) {
        // Since spin box includes a line edit we need to resolve the palette mask also from
        // the parent, as while the color is always correct on the palette supplied by panel,
        // the mask can still be empty. If either mask specifies custom base color, use that.
#if QT_CONFIG(spinbox)
        if (const QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget()))
            resolveMask |= spinbox->palette().resolveMask();
#endif // QT_CONFIG(spinbox)
    }
    return (resolveMask & (1 << QPalette::Base)) != 0;
}

/*! \internal
    This function will always return a valid window handle, and might
    create a limbo widget to do so.
    We often need a window handle to for example open theme data, so
    this function ensures that we get one.
*/
HWND QWindowsVistaStylePrivate::winId(const QWidget *widget)
{
    if (widget) {
        if (const HWND hwnd = QApplicationPrivate::getHWNDForWidget(const_cast<QWidget *>(widget)))
            return hwnd;
    }

    // Find top level with native window (there might be dialogs that do not have one).
    const auto allWindows = QGuiApplication::allWindows();
    for (const QWindow *window : allWindows) {
        if (window->isTopLevel() && window->type() != Qt::Desktop && window->handle() != nullptr)
            return reinterpret_cast<HWND>(window->winId());
    }

    return GetDesktopWindow();
}

/*! \internal
    Returns a native buffer (DIB section) of at least the size of
    ( \a x , \a y ). The buffer has a 32 bit depth, to not lose
    the alpha values on proper alpha-pixmaps.
*/
HBITMAP QWindowsVistaStylePrivate::buffer(int w, int h)
{
    // If we already have a HBITMAP which is of adequate size, just return that
    if (bufferBitmap) {
        if (bufferW >= w && bufferH >= h)
            return bufferBitmap;
        // Not big enough, discard the old one
        if (bufferDC && nullBitmap)
            SelectObject(bufferDC, nullBitmap);
        DeleteObject(bufferBitmap);
        bufferBitmap = nullptr;
    }

    w = qMax(bufferW, w);
    h = qMax(bufferH, h);

    if (!bufferDC) {
        HDC displayDC = GetDC(nullptr);
        bufferDC = CreateCompatibleDC(displayDC);
        ReleaseDC(nullptr, displayDC);
    }

    // Define the header
    BITMAPINFO bmi;
    memset(&bmi, 0, sizeof(bmi));
    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth       = w;
    bmi.bmiHeader.biHeight      = -h;
    bmi.bmiHeader.biPlanes      = 1;
    bmi.bmiHeader.biBitCount    = 32;
    bmi.bmiHeader.biCompression = BI_RGB;

    // Create the pixmap
    bufferPixels = nullptr;
    bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&bufferPixels), nullptr, 0);
    GdiFlush();
    nullBitmap = static_cast<HBITMAP>(SelectObject(bufferDC, bufferBitmap));

    if (Q_UNLIKELY(!bufferBitmap)) {
        qErrnoWarning("QWindowsVistaStylePrivate::buffer(%dx%d), CreateDIBSection() failed.", w, h);
        bufferW = 0;
        bufferH = 0;
        return nullptr;
    }
    if (Q_UNLIKELY(!bufferPixels)) {
        qErrnoWarning("QWindowsVistaStylePrivate::buffer(%dx%d), CreateDIBSection() did not allocate pixel data.", w, h);
        bufferW = 0;
        bufferH = 0;
        return nullptr;
    }
    bufferW = w;
    bufferH = h;
#ifdef DEBUG_XP_STYLE
    qDebug("Creating new dib section (%d, %d)", w, h);
#endif
    return bufferBitmap;
}

/*! \internal
    Returns \c true if the part contains any transparency at all. This does
    not indicate what kind of transparency we're dealing with. It can be
        - Alpha transparency
        - Masked transparency
*/
bool QWindowsVistaStylePrivate::isTransparent(QWindowsThemeData &themeData)
{
    return IsThemeBackgroundPartiallyTransparent(themeData.handle(), themeData.partId,
                                                 themeData.stateId);
}


/*! \internal
    Returns a QRegion of the region of the part
*/
QRegion QWindowsVistaStylePrivate::region(QWindowsThemeData &themeData)
{
    HRGN hRgn = nullptr;
    const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(themeData.widget);
    RECT rect = themeData.toRECT(QRect(themeData.rect.topLeft() / factor, themeData.rect.size() / factor));
    if (!SUCCEEDED(GetThemeBackgroundRegion(themeData.handle(), bufferHDC(), themeData.partId,
                                            themeData.stateId, &rect, &hRgn))) {
        return QRegion();
    }

    HRGN dest = CreateRectRgn(0, 0, 0, 0);
    const bool success = CombineRgn(dest, hRgn, nullptr, RGN_COPY) != ERROR;

    QRegion region;

    if (success) {
        QVarLengthArray<char> buf(256);
        RGNDATA *rd = reinterpret_cast<RGNDATA *>(buf.data());
        if (GetRegionData(dest, buf.size(), rd) == 0) {
            const auto numBytes = GetRegionData(dest, 0, nullptr);
            if (numBytes > 0) {
                buf.resize(numBytes);
                rd = reinterpret_cast<RGNDATA *>(buf.data());
                if (GetRegionData(dest, numBytes, rd) == 0)
                    rd = nullptr;
            } else {
                rd = nullptr;
            }
        }
        if (rd) {
            RECT *r = reinterpret_cast<RECT *>(rd->Buffer);
            for (uint i = 0; i < rd->rdh.nCount; ++i) {
                QRect rect;
                rect.setCoords(int(r->left * factor), int(r->top * factor),
                               int((r->right - 1) * factor), int((r->bottom - 1) * factor));
                ++r;
                region |= rect;
            }
        }
    }

    DeleteObject(hRgn);
    DeleteObject(dest);

    return region;
}

/*! \internal
    Returns \c true if the native doublebuffer contains pixels with
    varying alpha value.
*/
bool QWindowsVistaStylePrivate::hasAlphaChannel(const QRect &rect)
{
    const int startX = rect.left();
    const int startY = rect.top();
    const int w = rect.width();
    const int h = rect.height();

    int firstAlpha = -1;
    for (int y = startY; y < h/2; ++y) {
        auto buffer = reinterpret_cast<const DWORD *>(bufferPixels) + (y * bufferW);
        for (int x = startX; x < w; ++x, ++buffer) {
            int alpha = (*buffer) >> 24;
            if (firstAlpha == -1)
                firstAlpha = alpha;
            else if (alpha != firstAlpha)
                return true;
        }
    }
    return false;
}

/*! \internal
    When the theme engine paints both a true alpha pixmap and a glyph
    into our buffer, the glyph might not contain a proper alpha value.
    The rule of thumb for premultiplied pixmaps is that the color
    values of a pixel can never be higher than the alpha values, so
    we use this to our advantage here, and fix all instances where
    this occurs.
*/
bool QWindowsVistaStylePrivate::fixAlphaChannel(const QRect &rect)
{
    const int startX = rect.left();
    const int startY = rect.top();
    const int w = rect.width();
    const int h = rect.height();
    bool hasFixedAlphaValue = false;

    for (int y = startY; y < h; ++y) {
        auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW);
        for (int x = startX; x < w; ++x, ++buffer) {
            uint pixel = *buffer;
            int alpha = qAlpha(pixel);
            if (qRed(pixel) > alpha || qGreen(pixel) > alpha || qBlue(pixel) > alpha) {
                *buffer |= 0xff000000;
                hasFixedAlphaValue = true;
            }
        }
    }
    return hasFixedAlphaValue;
}

/*! \internal
    Swaps the alpha values on certain pixels:
        0xFF?????? -> 0x00??????
        0x00?????? -> 0xFF??????
    Used to determine the mask of a non-alpha transparent pixmap in
    the native doublebuffer, and swap the alphas so we may paint
    the image as a Premultiplied QImage with drawImage(), and obtain
    the mask transparency.
*/
bool QWindowsVistaStylePrivate::swapAlphaChannel(const QRect &rect, bool allPixels)
{
    const int startX = rect.left();
    const int startY = rect.top();
    const int w = rect.width();
    const int h = rect.height();
    bool valueChange = false;

    // Flip the alphas, so that 255-alpha pixels are 0, and 0-alpha are 255.
    for (int y = startY; y < h; ++y) {
        auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW);
        for (int x = startX; x < w; ++x, ++buffer) {
            if (allPixels) {
                *buffer |= 0xFF000000;
                continue;
            }
            unsigned int alphaValue = (*buffer) & 0xFF000000;
            if (alphaValue == 0xFF000000) {
                *buffer = 0;
                valueChange = true;
            } else if (alphaValue == 0) {
                *buffer |= 0xFF000000;
                valueChange = true;
            }
        }
    }
    return valueChange;
}

/*! \internal
    Main theme drawing function.
    Determines the correct lowlevel drawing method depending on several
    factors.
        Use drawBackgroundThruNativeBuffer() if:
            - Painter does not have an HDC
            - Theme part is flipped (mirrored horizontally)
        else use drawBackgroundDirectly().
    \note drawBackgroundThruNativeBuffer() can return false for large
    sizes due to buffer()/CreateDIBSection() failing.
*/
bool QWindowsVistaStylePrivate::drawBackground(QWindowsThemeData &themeData, qreal correctionFactor)
{
    if (themeData.rect.isEmpty())
        return true;

    QPainter *painter = themeData.painter;
    Q_ASSERT_X(painter != nullptr, "QWindowsVistaStylePrivate::drawBackground()", "Trying to draw a theme part without a painter");
    if (!painter || !painter->isActive())
        return false;

    painter->save();

    // Access paintDevice via engine since the painter may
    // return the clip device which can still be a widget device in case of grabWidget().

    bool translucentToplevel = false;
    const QPaintDevice *paintDevice = painter->device();
    const qreal aditionalDevicePixelRatio = themeData.widget ? themeData.widget->devicePixelRatio() : qreal(1);
    if (paintDevice->devType() == QInternal::Widget) {
        const QWidget *window = static_cast<const QWidget *>(paintDevice)->window();
        translucentToplevel = window->testAttribute(Qt::WA_TranslucentBackground);
    }

    const TransformType tt = transformType(painter->deviceTransform(), aditionalDevicePixelRatio);

    bool canDrawDirectly = false;
    if (themeData.widget && painter->opacity() == 1.0 && !themeData.rotate
            && !isFullyOpaque(themeData)
            && tt != ComplexTransform && !themeData.mirrorVertically && !themeData.invertPixels
            && !translucentToplevel) {
        // Draw on backing store DC only for real widgets or backing store images.
        const QPaintDevice *enginePaintDevice = painter->paintEngine()->paintDevice();
        switch (enginePaintDevice->devType()) {
        case QInternal::Widget:
            canDrawDirectly = true;
            break;
        case QInternal::Image:
            // Ensure the backing store has received as resize and is initialized.
            if (QBackingStore *bs = backingStoreForWidget(themeData.widget)) {
                if (bs->size().isValid() && bs->paintDevice() == enginePaintDevice)
                    canDrawDirectly = true;
            }
            break;
        }
    }

    const HDC dc = canDrawDirectly ? hdcForWidgetBackingStore(themeData.widget) : nullptr;
    const bool result = dc && qFuzzyCompare(correctionFactor, qreal(1))
            ? drawBackgroundDirectly(dc, themeData, aditionalDevicePixelRatio)
            : drawBackgroundThruNativeBuffer(themeData, aditionalDevicePixelRatio, correctionFactor);
    painter->restore();
    return result;
}

/*! \internal
    This function draws the theme parts directly to the paintengines HDC.
    Do not use this if you need to perform other transformations on the
    resulting data.
*/
bool QWindowsVistaStylePrivate::drawBackgroundDirectly(HDC dc, QWindowsThemeData &themeData, qreal additionalDevicePixelRatio)
{
    QPainter *painter = themeData.painter;

    const auto &deviceTransform = painter->deviceTransform();
    const QPointF redirectionDelta(deviceTransform.dx(), deviceTransform.dy());
    const QRect area = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio).translated(redirectionDelta).toRect();

    QRegion sysRgn = painter->paintEngine()->systemClip();
    if (sysRgn.isEmpty())
        sysRgn = area;
    else
        sysRgn &= area;
    if (painter->hasClipping())
        sysRgn &= scaleRegion(painter->clipRegion(), additionalDevicePixelRatio).translated(redirectionDelta.toPoint());
    HRGN hrgn = qt_hrgn_from_qregion(sysRgn);
    SelectClipRgn(dc, hrgn);

#ifdef DEBUG_XP_STYLE
    printf("---[ DIRECT PAINTING ]------------------> Name(%-10s) Part(%d) State(%d)\n",
           qPrintable(themeData.name), themeData.partId, themeData.stateId);
    showProperties(themeData);
#endif

    RECT drawRECT = themeData.toRECT(area);
    DTBGOPTS drawOptions;
    memset(&drawOptions, 0, sizeof(drawOptions));
    drawOptions.dwSize = sizeof(drawOptions);
    drawOptions.rcClip = themeData.toRECT(sysRgn.boundingRect());
    drawOptions.dwFlags = DTBG_CLIPRECT
            | (themeData.noBorder ? DTBG_OMITBORDER : 0)
            | (themeData.noContent ? DTBG_OMITCONTENT : 0)
            | (themeData.mirrorHorizontally ? DTBG_MIRRORDC : 0);

    const HRESULT result = DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &drawOptions);
    SelectClipRgn(dc, nullptr);
    DeleteObject(hrgn);
    return SUCCEEDED(result);
}

/*! \internal
    This function uses a secondary Native doublebuffer for painting parts.
    It should only be used when the painteengine doesn't provide a proper
    HDC for direct painting (e.g. when doing a grabWidget(), painting to
    other pixmaps etc), or when special transformations are needed (e.g.
    flips (horizontal mirroring only, vertical are handled by the theme
    engine).

    \a correctionFactor is an additional factor used to scale up controls
    that are too small on High DPI screens, as has been observed for
    WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON (QTBUG-75927).
*/
bool QWindowsVistaStylePrivate::drawBackgroundThruNativeBuffer(QWindowsThemeData &themeData,
                                                               qreal additionalDevicePixelRatio,
                                                               qreal correctionFactor)
{
    QPainter *painter = themeData.painter;
    QRectF rectF = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio);

    if ((themeData.rotate + 90) % 180 == 0) { // Catch 90,270,etc.. degree flips.
        rectF = QRectF(0, 0, rectF.height(), rectF.width());
    }
    rectF.moveTo(0, 0);

    const bool hasCorrectionFactor = !qFuzzyCompare(correctionFactor, qreal(1));
    QRect rect = rectF.toRect();
    const QRect drawRect = hasCorrectionFactor
            ? QRectF(rectF.topLeft() / correctionFactor, rectF.size() / correctionFactor).toRect()
            : rect;
    int partId = themeData.partId;
    int stateId = themeData.stateId;
    int w = rect.width();
    int h = rect.height();

    // Values initialized later, either from cached values, or from function calls
    AlphaChannelType alphaType = UnknownAlpha;
    bool stateHasData = true; // We assume so;
    bool hasAlpha = false;
    bool partIsTransparent;
    bool potentialInvalidAlpha;

    QString pixmapCacheKey = QStringLiteral("$qt_xp_");
    pixmapCacheKey.append(themeName(themeData.theme));
    pixmapCacheKey.append(QLatin1Char('p'));
    pixmapCacheKey.append(QString::number(partId));
    pixmapCacheKey.append(QLatin1Char('s'));
    pixmapCacheKey.append(QString::number(stateId));
    pixmapCacheKey.append(QLatin1Char('s'));
    pixmapCacheKey.append(themeData.noBorder ? QLatin1Char('0') : QLatin1Char('1'));
    pixmapCacheKey.append(QLatin1Char('b'));
    pixmapCacheKey.append(themeData.noContent ? QLatin1Char('0') : QLatin1Char('1'));
    pixmapCacheKey.append(QString::number(w));
    pixmapCacheKey.append(QLatin1Char('w'));
    pixmapCacheKey.append(QString::number(h));
    pixmapCacheKey.append(QLatin1Char('h'));
    pixmapCacheKey.append(QString::number(additionalDevicePixelRatio));
    pixmapCacheKey.append(QLatin1Char('d'));
    if (hasCorrectionFactor) {
        pixmapCacheKey.append(QLatin1Char('c'));
        pixmapCacheKey.append(QString::number(correctionFactor));
    }

    QPixmap cachedPixmap;
    ThemeMapKey key(themeData);
    ThemeMapData data = alphaCache.value(key);

    bool haveCachedPixmap = false;
    bool isCached = data.dataValid;
    if (isCached) {
        partIsTransparent = data.partIsTransparent;
        hasAlpha = data.hasAlphaChannel;
        alphaType = data.alphaType;
        potentialInvalidAlpha = data.hadInvalidAlpha;

        haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, &cachedPixmap);

#ifdef DEBUG_XP_STYLE
        char buf[25];
        ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h);
        printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n",
               haveCachedPixmap ? buf : "]-------------------",
               qPrintable(themeData.name), themeData.partId, themeData.stateId);
#endif
    } else {
        // Not cached, so get values from Theme Engine
        BOOL tmt_borderonly = false;
        COLORREF tmt_transparentcolor = 0x0;
        PROPERTYORIGIN proporigin = PO_NOTFOUND;
        GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERONLY, &tmt_borderonly);
        GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, TMT_TRANSPARENTCOLOR, &tmt_transparentcolor);
        GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_CAPTIONMARGINS, &proporigin);

        partIsTransparent = isTransparent(themeData);

        potentialInvalidAlpha = false;
        GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &proporigin);
        if (proporigin == PO_PART || proporigin == PO_STATE) {
            int tmt_glyphtype = GT_NONE;
            GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &tmt_glyphtype);
            potentialInvalidAlpha = partIsTransparent && tmt_glyphtype == GT_IMAGEGLYPH;
        }

#ifdef DEBUG_XP_STYLE
        printf("---[ NOT CACHED ]-----------------------> Name(%-10s) Part(%d) State(%d)\n",
               qPrintable(themeData.name), themeData.partId, themeData.stateId);
        printf("-->partIsTransparen      = %d\n", partIsTransparent);
        printf("-->potentialInvalidAlpha = %d\n", potentialInvalidAlpha);
        showProperties(themeData);
#endif
    }
    bool wasAlphaSwapped = false;
    bool wasAlphaFixed = false;

    // OLD PSDK Workaround ------------------------------------------------------------------------
    // See if we need extra clipping for the older PSDK, which does
    // not have a DrawThemeBackgroundEx function for DTGB_OMITBORDER
    // and DTGB_OMITCONTENT
    bool addBorderContentClipping = false;
    QRegion extraClip;
    QRect area = drawRect;
    if (themeData.noBorder || themeData.noContent) {
        extraClip = area;
        // We are running on a system where the uxtheme.dll does not have
        // the DrawThemeBackgroundEx function, so we need to clip away
        // borders or contents manually.

        int borderSize = 0;
        PROPERTYORIGIN origin = PO_NOTFOUND;
        GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin);
        GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize);
        borderSize *= additionalDevicePixelRatio;

        // Clip away border region
        if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) {
            if (themeData.noBorder) {
                extraClip &= area;
                area = area.adjusted(-borderSize, -borderSize, borderSize, borderSize);
            }

            // Clip away content region
            if (themeData.noContent) {
                QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize);
                extraClip ^= content;
            }
        }
        addBorderContentClipping = (themeData.noBorder | themeData.noContent);
    }

    QImage img;
    if (!haveCachedPixmap) { // If the pixmap is not cached, generate it! -------------------------
        if (!buffer(drawRect.width(), drawRect.height())) // Ensure a buffer of at least (w, h) in size
            return false;
        HDC dc = bufferHDC();

        // Clear the buffer
        if (alphaType != NoAlpha) {
            // Consider have separate "memset" function for small chunks for more speedup
            memset(bufferPixels, 0x00, bufferW * drawRect.height() * 4);
        }

        // Difference between area and rect
        int dx = area.x() - drawRect.x();
        int dy = area.y() - drawRect.y();

        // Adjust so painting rect starts from Origo
        rect.moveTo(0,0);
        area.moveTo(dx,dy);
        DTBGOPTS drawOptions;
        drawOptions.dwSize = sizeof(drawOptions);
        drawOptions.rcClip = themeData.toRECT(rect);
        drawOptions.dwFlags = DTBG_CLIPRECT
                | (themeData.noBorder ? DTBG_OMITBORDER : 0)
                | (themeData.noContent ? DTBG_OMITCONTENT : 0);

        // Drawing the part into the backing store
        RECT wRect(themeData.toRECT(area));
        DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &wRect, &drawOptions);

        // If not cached, analyze the buffer data to figure
        // out alpha type, and if it contains data
        if (!isCached) {
            // SHORTCUT: If the part's state has no data, cache it for NOOP later
            if (!stateHasData) {
                memset(static_cast<void *>(&data), 0, sizeof(data));
                data.dataValid = true;
                alphaCache.insert(key, data);
                return true;
            }
            hasAlpha = hasAlphaChannel(rect);
            if (!hasAlpha && partIsTransparent)
                potentialInvalidAlpha = true;
#if defined(DEBUG_XP_STYLE) && 1
            dumpNativeDIB(drawRect.width(), drawRect.height());
#endif
        }

        // Fix alpha values, if needed
        if (potentialInvalidAlpha)
            wasAlphaFixed = fixAlphaChannel(drawRect);

        QImage::Format format;
        if ((partIsTransparent && !wasAlphaSwapped) || (!partIsTransparent && hasAlpha)) {
            format = QImage::Format_ARGB32_Premultiplied;
            alphaType = RealAlpha;
        } else if (wasAlphaSwapped) {
            format = QImage::Format_ARGB32_Premultiplied;
            alphaType = MaskAlpha;
        } else {
            format = QImage::Format_RGB32;
            // The image data we got from the theme engine does not have any transparency,
            // thus the alpha channel is set to 0.
            // However, Format_RGB32 requires the alpha part to be set to 0xff, thus
            // we must flip it from 0x00 to 0xff
            swapAlphaChannel(rect, true);
            alphaType = NoAlpha;
        }
#if defined(DEBUG_XP_STYLE) && 1
        printf("Image format is: %s\n", alphaType == RealAlpha ? "Real Alpha" : alphaType == MaskAlpha ? "Masked Alpha" : "No Alpha");
#endif
        img = QImage(bufferPixels, bufferW, bufferH, format);
        if (themeData.invertPixels)
            img.invertPixels();

        if (hasCorrectionFactor)
            img = img.scaled(img.size() * correctionFactor, Qt::KeepAspectRatio, Qt::SmoothTransformation);
        img.setDevicePixelRatio(additionalDevicePixelRatio);
    }

    // Blitting backing store
    bool useRegion = partIsTransparent && !hasAlpha && !wasAlphaSwapped;

    QRegion newRegion;
    QRegion oldRegion;
    if (useRegion) {
        newRegion = region(themeData);
        oldRegion = painter->clipRegion();
        painter->setClipRegion(newRegion);
#if defined(DEBUG_XP_STYLE) && 0
        printf("Using region:\n");
        for (const QRect &r : newRegion)
            printf("    (%d, %d, %d, %d)\n", r.x(), r.y(), r.right(), r.bottom());
#endif
    }

    if (addBorderContentClipping)
        painter->setClipRegion(scaleRegion(extraClip, 1.0 / additionalDevicePixelRatio), Qt::IntersectClip);

    if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) {
        if (!haveCachedPixmap)
            painter->drawImage(themeData.rect, img, rect);
        else
            painter->drawPixmap(themeData.rect, cachedPixmap);
    } else {
        // This is _slow_!
        // Make a copy containing only the necessary data, and mirror
        // on all wanted axes. Then draw the copy.
        // If cached, the normal pixmap is cached, instead of caching
        // all possible orientations for each part and state.
        QImage imgCopy;
        if (!haveCachedPixmap)
            imgCopy = img.copy(rect);
        else
            imgCopy = cachedPixmap.toImage();

        if (themeData.rotate) {
            QTransform rotMatrix;
            rotMatrix.rotate(themeData.rotate);
            imgCopy = imgCopy.transformed(rotMatrix);
        }
        if (themeData.mirrorHorizontally || themeData.mirrorVertically)
            imgCopy = imgCopy.mirrored(themeData.mirrorHorizontally, themeData.mirrorVertically);
        painter->drawImage(themeData.rect, imgCopy);
    }

    if (useRegion || addBorderContentClipping) {
        if (oldRegion.isEmpty())
            painter->setClipping(false);
        else
            painter->setClipRegion(oldRegion);
    }

    // Cache the pixmap to avoid expensive swapAlphaChannel() calls
    if (!haveCachedPixmap && w && h) {
        QPixmap pix = QPixmap::fromImage(img).copy(rect);
        QPixmapCache::insert(pixmapCacheKey, pix);
#ifdef DEBUG_XP_STYLE
        printf("+++Adding pixmap to cache, size(%d, %d), wasAlphaSwapped(%d), wasAlphaFixed(%d), name(%s)\n",
               w, h, wasAlphaSwapped, wasAlphaFixed, qPrintable(pixmapCacheKey));
#endif
    }

    // Add to theme part cache
    if (!isCached) {
        memset(static_cast<void *>(&data), 0, sizeof(data));
        data.dataValid = true;
        data.partIsTransparent = partIsTransparent;
        data.alphaType = alphaType;
        data.hasAlphaChannel = hasAlpha;
        data.wasAlphaSwapped = wasAlphaSwapped;
        data.hadInvalidAlpha = wasAlphaFixed;
        alphaCache.insert(key, data);
    }
    return true;
}

/*!
    \internal

    Animations are started at a frame that is based on the current time,
    which makes it impossible to run baseline tests with this style. Allow
    overriding through a dynamic property.
*/
QTime QWindowsVistaStylePrivate::animationTime() const
{
    Q_Q(const QWindowsVistaStyle);
    static bool animationTimeOverride = q->dynamicPropertyNames().contains("_qt_animation_time");
    if (animationTimeOverride)
        return q->property("_qt_animation_time").toTime();
    return QTime::currentTime();
}

/* \internal
    Checks and returns the style object
*/
inline QObject *styleObject(const QStyleOption *option) {
    return option ? option->styleObject : nullptr;
}

/* \internal
    Checks if we can animate on a style option
*/
bool canAnimate(const QStyleOption *option) {
    return option
            && option->styleObject
            && !option->styleObject->property("_q_no_animation").toBool();
}

static inline QImage createAnimationBuffer(const QStyleOption *option, const QWidget *widget)
{
    const qreal devicePixelRatio = widget
            ? widget->devicePixelRatioF() : qApp->devicePixelRatio();
    QImage result(option->rect.size() * devicePixelRatio, QImage::Format_ARGB32_Premultiplied);
    result.setDevicePixelRatio(devicePixelRatio);
    result.fill(0);
    return result;
}

/* \internal
    Used by animations to clone a styleoption and shift its offset
*/
QStyleOption *clonedAnimationStyleOption(const QStyleOption*option) {
    QStyleOption *styleOption = nullptr;
    if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option))
        styleOption = new QStyleOptionSlider(*slider);
    else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option))
        styleOption = new QStyleOptionSpinBox(*spinbox);
    else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option))
        styleOption = new QStyleOptionGroupBox(*groupBox);
    else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option))
        styleOption = new QStyleOptionComboBox(*combo);
    else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option))
        styleOption = new QStyleOptionButton(*button);
    else
        styleOption = new QStyleOption(*option);
    styleOption->rect = QRect(QPoint(0,0), option->rect.size());
    return styleOption;
}

/* \internal
    Used by animations to delete cloned styleoption
*/
void deleteClonedAnimationStyleOption(const QStyleOption *option)
{
    if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option))
        delete slider;
    else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option))
        delete spinbox;
    else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option))
        delete groupBox;
    else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option))
        delete combo;
    else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option))
        delete button;
    else
        delete option;
}

static void populateTitleBarButtonTheme(const QStyle *proxy, const QWidget *widget,
                                        const QStyleOptionComplex *option,
                                        QStyle::SubControl subControl,
                                        bool isTitleBarActive, int part,
                                        QWindowsThemeData *theme)
{
    theme->rect = proxy->subControlRect(QStyle::CC_TitleBar, option, subControl, widget);
    theme->partId = part;
    if (widget && !widget->isEnabled())
        theme->stateId = RBS_DISABLED;
    else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_Sunken))
        theme->stateId = RBS_PUSHED;
    else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_MouseOver))
        theme->stateId = RBS_HOT;
    else if (!isTitleBarActive)
        theme->stateId = RBS_INACTIVE;
    else
        theme->stateId = RBS_NORMAL;
}

#if QT_CONFIG(mdiarea)
// Helper for drawing MDI buttons into the corner widget of QMenuBar in case a
// QMdiSubWindow is maximized.
static void populateMdiButtonTheme(const QStyle *proxy, const QWidget *widget,
                                   const QStyleOptionComplex *option,
                                   QStyle::SubControl subControl, int part,
                                   QWindowsThemeData *theme)
{
    theme->partId = part;
    theme->rect = proxy->subControlRect(QStyle::CC_MdiControls, option, subControl, widget);
    if (!option->state.testFlag(QStyle::State_Enabled))
        theme->stateId = CBS_INACTIVE;
    else if (option->state.testFlag(QStyle::State_Sunken) && option->activeSubControls.testFlag(subControl))
        theme->stateId = CBS_PUSHED;
    else if (option->state.testFlag(QStyle::State_MouseOver) && option->activeSubControls.testFlag(subControl))
        theme->stateId = CBS_HOT;
    else
        theme->stateId = CBS_NORMAL;
}

// Calculate an small (max 2), empirical correction factor for scaling up
// WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON, which are too
// small on High DPI screens (QTBUG-75927).
static qreal mdiButtonCorrectionFactor(QWindowsThemeData &theme, const QPaintDevice *pd = nullptr)
{
    const auto dpr = pd ? pd->devicePixelRatio() : qApp->devicePixelRatio();
    const QSizeF nativeSize = QSizeF(theme.size()) / dpr;
    const QSizeF requestedSize(theme.rect.size());
    const auto rawFactor = qMin(requestedSize.width() / nativeSize.width(),
                                requestedSize.height() / nativeSize.height());
    const auto factor = rawFactor >= qreal(2) ? qreal(2) : qreal(1);
    return factor;
}
#endif // QT_CONFIG(mdiarea)

/*
  This function is used by subControlRect to check if a button
  should be drawn for the given subControl given a set of window flags.
*/
static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){

    bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
    bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
    const auto flags = tb->titleBarFlags;
    bool retVal = false;
    switch (sc) {
    case QStyle::SC_TitleBarContextHelpButton:
        if (flags & Qt::WindowContextHelpButtonHint)
            retVal = true;
        break;
    case QStyle::SC_TitleBarMinButton:
        if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint))
            retVal = true;
        break;
    case QStyle::SC_TitleBarNormalButton:
        if (isMinimized && (flags & Qt::WindowMinimizeButtonHint))
            retVal = true;
        else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint))
            retVal = true;
        break;
    case QStyle::SC_TitleBarMaxButton:
        if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint))
            retVal = true;
        break;
    case QStyle::SC_TitleBarShadeButton:
        if (!isMinimized &&  flags & Qt::WindowShadeButtonHint)
            retVal = true;
        break;
    case QStyle::SC_TitleBarUnshadeButton:
        if (isMinimized && flags & Qt::WindowShadeButtonHint)
            retVal = true;
        break;
    case QStyle::SC_TitleBarCloseButton:
        if (flags & Qt::WindowSystemMenuHint)
            retVal = true;
        break;
    case QStyle::SC_TitleBarSysMenu:
        if (flags & Qt::WindowSystemMenuHint)
            retVal = true;
        break;
    default :
        retVal = true;
    }
    return retVal;
}

//convert Qt state flags to uxtheme button states
static int buttonStateId(int flags, int partId)
{
    int stateId = 0;
    if (partId == BP_RADIOBUTTON || partId == BP_CHECKBOX) {
        if (!(flags & QStyle::State_Enabled))
            stateId = RBS_UNCHECKEDDISABLED;
        else if (flags & QStyle::State_Sunken)
            stateId = RBS_UNCHECKEDPRESSED;
        else if (flags & QStyle::State_MouseOver)
            stateId = RBS_UNCHECKEDHOT;
        else
            stateId = RBS_UNCHECKEDNORMAL;

        if (flags & QStyle::State_On)
            stateId += RBS_CHECKEDNORMAL-1;

    } else if (partId == BP_PUSHBUTTON) {
        if (!(flags & QStyle::State_Enabled))
            stateId = PBS_DISABLED;
        else if (flags & (QStyle::State_Sunken | QStyle::State_On))
            stateId = PBS_PRESSED;
        else if (flags & QStyle::State_MouseOver)
            stateId = PBS_HOT;
        else
            stateId = PBS_NORMAL;
    } else {
        Q_ASSERT(1);
    }
    return stateId;
}

static inline bool supportsStateTransition(QStyle::PrimitiveElement element,
                                           const QStyleOption *option,
                                           const QWidget *widget)
{
    bool result = false;
    switch (element) {
    case QStyle::PE_IndicatorRadioButton:
    case QStyle::PE_IndicatorCheckBox:
        result = true;
        break;
        // QTBUG-40634, do not animate when color is set in palette for PE_PanelLineEdit.
    case QStyle::PE_FrameLineEdit:
        result = !QWindowsVistaStylePrivate::isLineEditBaseColorSet(option, widget);
        break;
    default:
        break;
    }
    return result;
}

/*!
  \class QWindowsVistaStyle
  \brief The QWindowsVistaStyle class provides a look and feel suitable for applications on Microsoft Windows Vista.
  \since 4.3
  \ingroup appearance
  \inmodule QtWidgets
  \internal

  \warning This style is only available on the Windows Vista platform
  because it makes use of Windows Vista's style engine.

  \sa QMacStyle, QFusionStyle
*/

/*!
  Constructs a QWindowsVistaStyle object.
*/
QWindowsVistaStyle::QWindowsVistaStyle() : QWindowsStyle(*new QWindowsVistaStylePrivate)
{
}

/*!
  Destructor.
*/
QWindowsVistaStyle::~QWindowsVistaStyle() = default;


/*!
 \internal

  Animations are used for some state transitions on specific widgets.

  Only one running animation can exist for a widget at any specific
  time.  Animations can be added through
  QWindowsVistaStylePrivate::startAnimation(Animation *) and any
  existing animation on a widget can be retrieved with
  QWindowsVistaStylePrivate::widgetAnimation(Widget *).

  Once an animation has been started,
  QWindowsVistaStylePrivate::timerEvent(QTimerEvent *) will
  continuously call update() on the widget until it is stopped,
  meaning that drawPrimitive will be called many times until the
  transition has completed. During this time, the result will be
  retrieved by the Animation::paint(...) function and not by the style
  itself.

  To determine if a transition should occur, the style needs to know
  the previous state of the widget as well as the current one. This is
  solved by updating dynamic properties on the widget every time the
  function is called.

  Transitions interrupting existing transitions should always be
  smooth, so whenever a hover-transition is started on a pulsating
  button, it uses the current frame of the pulse-animation as the
  starting image for the hover transition.

 */
void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,
                                       QPainter *painter, const QWidget *widget) const
{
    if (!QWindowsVistaStylePrivate::useVista()) {
        QWindowsStyle::drawPrimitive(element, option, painter, widget);
        return;
    }

    QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());

    int state = option->state;
    QRect rect = option->rect;

    if ((state & State_Enabled) && d->transitionsEnabled() && canAnimate(option)) {
        if (supportsStateTransition(element, option, widget)) {
            // Retrieve and update the dynamic properties tracking
            // the previous state of the widget:
            QObject *styleObject = option->styleObject;
            styleObject->setProperty("_q_no_animation", true);
            int oldState = styleObject->property("_q_stylestate").toInt();
            QRect oldRect = styleObject->property("_q_stylerect").toRect();
            QRect newRect = rect;
            styleObject->setProperty("_q_stylestate", int(option->state));
            styleObject->setProperty("_q_stylerect", option->rect);

            bool doTransition = oldState &&
                    ((state & State_Sunken)    != (oldState & State_Sunken) ||
                    (state & State_On)         != (oldState & State_On)     ||
                    (state & State_MouseOver)  != (oldState & State_MouseOver));

            if (oldRect != newRect ||
                    (state & State_Enabled) != (oldState & State_Enabled) ||
                    (state & State_Active)  != (oldState & State_Active))
                d->stopAnimation(styleObject);

            if (state & State_ReadOnly && element == PE_FrameLineEdit) // Do not animate read only line edits
                doTransition = false;

            if (doTransition) {
                QStyleOption *styleOption = clonedAnimationStyleOption(option);
                styleOption->state = QStyle::State(oldState);

                QWindowsVistaAnimation *animate = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
                QWindowsVistaTransition *transition = new QWindowsVistaTransition(styleObject);

                // We create separate images for the initial and final transition states and store them in the
                // Transition object.
                QImage startImage = createAnimationBuffer(option, widget);
                QPainter startPainter(&startImage);

                QImage endImage = createAnimationBuffer(option, widget);
                QPainter endPainter(&endImage);

                // If we have a running animation on the widget already, we will use that to paint the initial
                // state of the new transition, this ensures a smooth transition from a current animation such as a
                // pulsating default button into the intended target state.
                if (!animate)
                    proxy()->drawPrimitive(element, styleOption, &startPainter, widget);
                else
                    animate->paint(&startPainter, styleOption);

                transition->setStartImage(startImage);

                // The end state of the transition is simply the result we would have painted
                // if the style was not animated.
                styleOption->styleObject = nullptr;
                styleOption->state = option->state;
                proxy()->drawPrimitive(element, styleOption, &endPainter, widget);

                transition->setEndImage(endImage);

                HTHEME theme;
                int partId;
                DWORD duration;
                int fromState = 0;
                int toState = 0;

                //translate state flags to UXTHEME states :
                if (element == PE_FrameLineEdit) {
                    theme = OpenThemeData(nullptr, L"Edit");
                    partId = EP_EDITBORDER_NOSCROLL;

                    if (oldState & State_HasFocus)
                        fromState = ETS_SELECTED;
                    else if (oldState & State_MouseOver)
                        fromState = ETS_HOT;
                    else
                        fromState = ETS_NORMAL;

                    if (state & State_HasFocus)
                        toState = ETS_SELECTED;
                    else if (state & State_MouseOver)
                        toState = ETS_HOT;
                    else
                        toState = ETS_NORMAL;

                } else {
                    theme = OpenThemeData(nullptr, L"Button");
                    if (element == PE_IndicatorRadioButton)
                        partId = BP_RADIOBUTTON;
                    else if (element == PE_IndicatorCheckBox)
                        partId = BP_CHECKBOX;
                    else
                        partId = BP_PUSHBUTTON;

                    fromState = buttonStateId(oldState, partId);
                    toState = buttonStateId(option->state, partId);
                }

                // Retrieve the transition time between the states from the system.
                if (theme
                        && SUCCEEDED(GetThemeTransitionDuration(theme, partId, fromState, toState,
                                                                TMT_TRANSITIONDURATIONS, &duration))) {
                    transition->setDuration(int(duration));
                }
                transition->setStartTime(d->animationTime());

                deleteClonedAnimationStyleOption(styleOption);
                d->startAnimation(transition);
            }
            styleObject->setProperty("_q_no_animation", false);
        }
    }

    int themeNumber = -1;
    int partId = 0;
    int stateId = 0;
    bool hMirrored = false;
    bool vMirrored = false;
    bool noBorder = false;
    bool noContent = false;
    int  rotate = 0;

    switch (element) {
    case PE_PanelButtonCommand:
        if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
            QBrush fill;
            if (!(state & State_Sunken) && (state & State_On))
                fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern);
            else
                fill = option->palette.brush(QPalette::Button);
            if (btn->features & QStyleOptionButton::DefaultButton && state & State_Sunken) {
                painter->setPen(option->palette.dark().color());
                painter->setBrush(fill);
                painter->drawRect(rect.adjusted(0, 0, -1, -1));
            } else if (state & (State_Raised | State_On | State_Sunken)) {
                qDrawWinButton(painter, rect, option->palette, state & (State_Sunken | State_On),
                               &fill);
            } else {
                painter->fillRect(rect, fill);
            }
        }
        break;

    case PE_PanelButtonTool:
#if QT_CONFIG(dockwidget)
        if (widget && widget->inherits("QDockWidgetTitleButton")) {
            if (const QWidget *dw = widget->parentWidget())
                if (dw->isWindow()) {
                    return;
                }
        }
#endif // QT_CONFIG(dockwidget)
        themeNumber = QWindowsVistaStylePrivate::ToolBarTheme;
        partId = TP_BUTTON;
        if (!(option->state & State_Enabled))
            stateId = TS_DISABLED;
        else if (option->state & State_Sunken)
            stateId = TS_PRESSED;
        else if (option->state & State_MouseOver)
            stateId = option->state & State_On ? TS_HOTCHECKED : TS_HOT;
        else if (option->state & State_On)
            stateId = TS_CHECKED;
        else if (!(option->state & State_AutoRaise))
            stateId = TS_HOT;
        else
            stateId = TS_NORMAL;

        break;

    case PE_IndicatorHeaderArrow:
        if (const auto *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
            int stateId = HSAS_SORTEDDOWN;
            if (header->sortIndicator & QStyleOptionHeader::SortDown)
                stateId = HSAS_SORTEDUP; //note that the uxtheme sort down indicator is the inverse of ours
            QWindowsThemeData theme(widget, painter,
                            QWindowsVistaStylePrivate::HeaderTheme,
                            HP_HEADERSORTARROW, stateId, option->rect);
            d->drawBackground(theme);
            return;
        }
        break;

    case PE_IndicatorCheckBox:
        if (auto *animate =
                qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) {
            animate->paint(painter, option);
            return;
        } else {
            themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
            partId = BP_CHECKBOX;

            if (!(option->state & State_Enabled))
                stateId = CBS_UNCHECKEDDISABLED;
            else if (option->state & State_Sunken)
                stateId = CBS_UNCHECKEDPRESSED;
            else if (option->state & State_MouseOver)
                stateId = CBS_UNCHECKEDHOT;
            else
                stateId = CBS_UNCHECKEDNORMAL;

            if (option->state & State_On)
                stateId += CBS_CHECKEDNORMAL-1;
            else if (option->state & State_NoChange)
                stateId += CBS_MIXEDNORMAL-1;
        }
        break;

    case PE_IndicatorItemViewItemCheck: {
        QStyleOptionButton button;
        button.QStyleOption::operator=(*option);
        button.state &= ~State_MouseOver;
        proxy()->drawPrimitive(PE_IndicatorCheckBox, &button, painter, widget);
        return;
    }

    case PE_IndicatorBranch: {
        QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::VistaTreeViewTheme);
        static int decoration_size = 0;
        if (!decoration_size && theme.isValid()) {
            QWindowsThemeData themeSize = theme;
            themeSize.partId = TVP_HOTGLYPH;
            themeSize.stateId = GLPS_OPENED;
            const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
            decoration_size = qRound(qMax(size.width(), size.height()));
        }
        int mid_h = option->rect.x() + option->rect.width() / 2;
        int mid_v = option->rect.y() + option->rect.height() / 2;
        if (option->state & State_Children) {
            int delta = decoration_size / 2;
            theme.rect = QRect(mid_h - delta, mid_v - delta, decoration_size, decoration_size);
            theme.partId = option->state & State_MouseOver ? TVP_HOTGLYPH : TVP_GLYPH;
            theme.stateId = option->state & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED;
            if (option->direction == Qt::RightToLeft)
                theme.mirrorHorizontally = true;
            d->drawBackground(theme);
        }
        return;
    }

    case PE_PanelButtonBevel:
        if (QWindowsVistaAnimation *animate =
                qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) {
            animate->paint(painter, option);
            return;
        }

        themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
        partId = BP_PUSHBUTTON;
        if (!(option->state & State_Enabled))
            stateId = PBS_DISABLED;
        else if ((option->state & State_Sunken) || (option->state & State_On))
            stateId = PBS_PRESSED;
        else if (option->state & State_MouseOver)
            stateId = PBS_HOT;
        else
            stateId = PBS_NORMAL;
        break;

    case PE_IndicatorRadioButton:
        if (QWindowsVistaAnimation *animate =
                qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) {
            animate->paint(painter, option);
            return;
        } else {
            themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
            partId = BP_RADIOBUTTON;

            if (!(option->state & State_Enabled))
                stateId = RBS_UNCHECKEDDISABLED;
            else if (option->state & State_Sunken)
                stateId = RBS_UNCHECKEDPRESSED;
            else if (option->state & State_MouseOver)
                stateId = RBS_UNCHECKEDHOT;
            else
                stateId = RBS_UNCHECKEDNORMAL;

            if (option->state & State_On)
                stateId += RBS_CHECKEDNORMAL-1;
        }
        break;

    case PE_Frame:
#if QT_CONFIG(accessibility)
        if (QStyleHelper::isInstanceOf(option->styleObject, QAccessible::EditableText)
             || QStyleHelper::isInstanceOf(option->styleObject, QAccessible::StaticText) ||
#else
        if (
#endif
                (widget && widget->inherits("QTextEdit"))) {
            painter->save();
            int stateId = ETS_NORMAL;
            if (!(state & State_Enabled))
                stateId = ETS_DISABLED;
            else if (state & State_ReadOnly)
                stateId = ETS_READONLY;
            else if (state & State_HasFocus)
                stateId = ETS_SELECTED;
            QWindowsThemeData theme(widget, painter,
                            QWindowsVistaStylePrivate::EditTheme,
                            EP_EDITBORDER_HVSCROLL, stateId, option->rect);
            // Since EP_EDITBORDER_HVSCROLL does not us borderfill, theme.noContent cannot be used for clipping
            int borderSize = 1;
            GetThemeInt(theme.handle(), theme.partId, theme.stateId, TMT_BORDERSIZE, &borderSize);
            QRegion clipRegion = option->rect;
            QRegion content = option->rect.adjusted(borderSize, borderSize, -borderSize, -borderSize);
            clipRegion ^= content;
            painter->setClipRegion(clipRegion);
            d->drawBackground(theme);
            painter->restore();
            return;
        } else {
            if (option->state & State_Raised)
                return;

            themeNumber = QWindowsVistaStylePrivate::ListViewTheme;
            partId = LVP_LISTGROUP;
            QWindowsThemeData theme(widget, nullptr, themeNumber, partId);

            if (!(option->state & State_Enabled))
                stateId = ETS_DISABLED;
            else
                stateId = ETS_NORMAL;

            int fillType;

            if (GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) {
                if (fillType == BT_BORDERFILL) {
                    COLORREF bcRef;
                    GetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef);
                    QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef)));
                    QPen oldPen = painter->pen();

                    // Inner white border
                    painter->setPen(QPen(option->palette.base().color(), 0));
                    const qreal dpi = QStyleHelper::dpi(option);
                    const auto topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi);
                    const auto bottomRightAdjustment = QStyleHelper::dpiScaled(-1, dpi);
                    painter->drawRect(QRectF(option->rect).adjusted(topLevelAdjustment, topLevelAdjustment,
                                                                    bottomRightAdjustment, bottomRightAdjustment));
                    // Outer dark border
                    painter->setPen(QPen(bordercolor, 0));
                    painter->drawRect(QRectF(option->rect).adjusted(0, 0, -topLevelAdjustment, -topLevelAdjustment));
                    painter->setPen(oldPen);
                }

                if (fillType == BT_BORDERFILL || fillType == BT_NONE)
                    return;
            }
        }
        break;

    case PE_FrameMenu: {
        int stateId = option->state & State_Active ? MB_ACTIVE : MB_INACTIVE;
        QWindowsThemeData theme(widget, painter,
                        QWindowsVistaStylePrivate::MenuTheme,
                        MENU_POPUPBORDERS, stateId, option->rect);
        d->drawBackground(theme);
        return;
    }

    case PE_PanelMenuBar:
        break;

#if QT_CONFIG(dockwidget)
    case PE_IndicatorDockWidgetResizeHandle:
        return;

    case PE_FrameDockWidget:
        if (const auto *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
            themeNumber = QWindowsVistaStylePrivate::WindowTheme;
            if (option->state & State_Active)
                stateId = FS_ACTIVE;
            else
                stateId = FS_INACTIVE;

            int fwidth = proxy()->pixelMetric(PM_DockWidgetFrameWidth, frm, widget);

            QWindowsThemeData theme(widget, painter, themeNumber, 0, stateId);

            if (!theme.isValid())
                break;

            theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth);
            theme.partId = WP_SMALLFRAMELEFT;
            d->drawBackground(theme);
            theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth);
            theme.partId = WP_SMALLFRAMERIGHT;
            d->drawBackground(theme);
            theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth);
            theme.partId = WP_SMALLFRAMEBOTTOM;
            d->drawBackground(theme);
            return;
        }
        break;
#endif // QT_CONFIG(dockwidget)

    case PE_FrameTabWidget:
        if (const auto *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) {
            themeNumber = QWindowsVistaStylePrivate::TabTheme;
            partId = TABP_PANE;

            if (widget) {
                bool useGradient = true;
                const int maxlength = 256;
                wchar_t themeFileName[maxlength];
                wchar_t themeColor[maxlength];
                // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it
                if (GetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, nullptr, 0) == S_OK) {
                    wchar_t *offset = nullptr;
                    if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != nullptr) {
                        offset++;
                        if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic"))
                            useGradient = false;
                    }
                }
                // This should work, but currently there's an error in the ::drawBackgroundDirectly()
                // code, when using the HDC directly..
                if (useGradient) {
                    QStyleOptionTabWidgetFrame frameOpt = *tab;
                    frameOpt.rect = widget->rect();
                    QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt, widget);
                    QRegion reg = option->rect;
                    reg -= contentsRect;
                    painter->setClipRegion(reg);
                    QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
                    theme.mirrorHorizontally = hMirrored;
                    theme.mirrorVertically = vMirrored;
                    d->drawBackground(theme);
                    painter->setClipRect(contentsRect);
                    partId = TABP_BODY;
                }
            }
            switch (tab->shape) {
            case QTabBar::RoundedNorth:
            case QTabBar::TriangularNorth:
                break;
            case QTabBar::RoundedSouth:
            case QTabBar::TriangularSouth:
                vMirrored = true;
                break;
            case QTabBar::RoundedEast:
            case QTabBar::TriangularEast:
                rotate = 90;
                break;
            case QTabBar::RoundedWest:
            case QTabBar::TriangularWest:
                rotate = 90;
                hMirrored = true;
                break;
            default:
                break;
            }
        }
        break;

    case PE_FrameStatusBarItem:
        themeNumber = QWindowsVistaStylePrivate::StatusTheme;
        partId = SP_PANE;
        break;

    case PE_FrameWindow:
        if (const auto *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
            themeNumber = QWindowsVistaStylePrivate::WindowTheme;
            if (option->state & State_Active)
                stateId = FS_ACTIVE;
            else
                stateId = FS_INACTIVE;

            int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget));

            QWindowsThemeData theme(widget, painter, themeNumber, 0, stateId);
            if (!theme.isValid())
                break;

            // May fail due to too-large buffers for large widgets, fall back to Windows style.
            theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth);
            theme.partId = WP_FRAMELEFT;
            if (!d->drawBackground(theme)) {
                QWindowsStyle::drawPrimitive(element, option, painter, widget);
                return;
            }
            theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth);
            theme.partId = WP_FRAMERIGHT;
            if (!d->drawBackground(theme)) {
                QWindowsStyle::drawPrimitive(element, option, painter, widget);
                return;
            }
            theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth);
            theme.partId = WP_FRAMEBOTTOM;
            if (!d->drawBackground(theme)) {
                QWindowsStyle::drawPrimitive(element, option, painter, widget);
                return;
            }
            theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth);
            theme.partId = WP_CAPTION;
            if (!d->drawBackground(theme))
                QWindowsStyle::drawPrimitive(element, option, painter, widget);
            return;
        }
        break;

    case PE_PanelLineEdit:
        if (const auto *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
            bool isEnabled = state & State_Enabled;
            if (QWindowsVistaStylePrivate::isLineEditBaseColorSet(option, widget)) {
                painter->fillRect(panel->rect, panel->palette.brush(QPalette::Base));
            } else {
                int partId = EP_BACKGROUND;
                int stateId = EBS_NORMAL;
                if (!isEnabled)
                    stateId = EBS_DISABLED;
                else if (option->state & State_ReadOnly)
                    stateId = EBS_READONLY;
                else if (option->state & State_MouseOver)
                    stateId = EBS_HOT;

                QWindowsThemeData theme(nullptr, painter, QWindowsVistaStylePrivate::EditTheme,
                                partId, stateId, rect);
                if (!theme.isValid()) {
                    QWindowsStyle::drawPrimitive(element, option, painter, widget);
                    return;
                }
                int bgType;
                GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType);
                if (bgType == BT_IMAGEFILE) {
                    d->drawBackground(theme);
                } else {
                    QBrush fillColor = option->palette.brush(QPalette::Base);
                    if (!isEnabled) {
                        PROPERTYORIGIN origin = PO_NOTFOUND;
                        GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin);
                        // Use only if the fill property comes from our part
                        if ((origin == PO_PART || origin == PO_STATE)) {
                            COLORREF bgRef;
                            GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef);
                            fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef)));
                        }
                    }
                    painter->fillRect(option->rect, fillColor);
                }
            }
            if (panel->lineWidth > 0)
                proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget);
        }
        return;

    case PE_IndicatorButtonDropDown:
        themeNumber = QWindowsVistaStylePrivate::ToolBarTheme;
        partId = TP_SPLITBUTTONDROPDOWN;
        if (!(option->state & State_Enabled))
            stateId = TS_DISABLED;
        else if (option->state & State_Sunken)
            stateId = TS_PRESSED;
        else if (option->state & State_MouseOver)
            stateId = option->state & State_On ? TS_HOTCHECKED : TS_HOT;
        else if (option->state & State_On)
            stateId = TS_CHECKED;
        else if (!(option->state & State_AutoRaise))
            stateId = TS_HOT;
        else
            stateId = TS_NORMAL;
        if (option->direction == Qt::RightToLeft)
            hMirrored = true;
        break;

    case PE_FrameLineEdit:
        if (QWindowsVistaAnimation *animate = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) {
            animate->paint(painter, option);
        } else {
            if (QWindowsVistaStylePrivate::isItemViewDelegateLineEdit(widget)) {
                // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class.
                QPen oldPen = painter->pen();
                // Inner white border
                painter->setPen(QPen(option->palette.base().color(), 1));
                painter->drawRect(option->rect.adjusted(1, 1, -2, -2));
                // Outer dark border
                painter->setPen(QPen(option->palette.shadow().color(), 1));
                painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
                painter->setPen(oldPen);
                return;
            }
            int stateId = ETS_NORMAL;
            if (!(state & State_Enabled))
                stateId = ETS_DISABLED;
            else if (state & State_ReadOnly)
                stateId = ETS_READONLY;
            else if (state & State_HasFocus)
                stateId = ETS_SELECTED;
            else if (state & State_MouseOver)
                stateId = ETS_HOT;
            QWindowsThemeData theme(widget, painter,
                            QWindowsVistaStylePrivate::EditTheme,
                            EP_EDITBORDER_NOSCROLL, stateId, option->rect);
            theme.noContent = true;
            painter->save();
            QRegion clipRegion = option->rect;
            clipRegion -= option->rect.adjusted(2, 2, -2, -2);
            painter->setClipRegion(clipRegion);
            d->drawBackground(theme);
            painter->restore();
        }
        return;

    case PE_FrameGroupBox:
        themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
        partId = BP_GROUPBOX;
        if (!(option->state & State_Enabled))
            stateId = GBS_DISABLED;
        else
            stateId = GBS_NORMAL;
        if (const auto *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
            if (frame->features & QStyleOptionFrame::Flat) {
                // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style
                QRect fr = frame->rect;
                QPoint p1(fr.x(), fr.y() + 1);
                QPoint p2(fr.x() + fr.width(), p1.y() + 1);
                rect = QRect(p1, p2);
                themeNumber = -1;
            }
        }
        break;

    case PE_IndicatorToolBarHandle: {
        QWindowsThemeData theme;
        QRect rect;
        if (option->state & State_Horizontal) {
            theme = QWindowsThemeData(widget, painter,
                              QWindowsVistaStylePrivate::RebarTheme,
                              RP_GRIPPER, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2));
            rect = option->rect.adjusted(0, 1, 0, -2);
            rect.setWidth(4);
        } else {
            theme = QWindowsThemeData(widget, painter, QWindowsVistaStylePrivate::RebarTheme,
                              RP_GRIPPERVERT, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2));
            rect = option->rect.adjusted(1, 0, -1, 0);
            rect.setHeight(4);
        }
        theme.rect = rect;
        d->drawBackground(theme);
        return;
    }

    case PE_IndicatorToolBarSeparator: {
        QPen pen = painter->pen();
        int margin = 3;
        painter->setPen(option->palette.window().color().darker(114));
        if (option->state & State_Horizontal) {
            int x1 = option->rect.center().x();
            painter->drawLine(QPoint(x1, option->rect.top() + margin), QPoint(x1, option->rect.bottom() - margin));
        } else {
            int y1 = option->rect.center().y();
            painter->drawLine(QPoint(option->rect.left() + margin, y1), QPoint(option->rect.right() - margin, y1));
        }
        painter->setPen(pen);
        return;
    }

    case PE_PanelTipLabel: {
        QWindowsThemeData theme(widget, painter,
                        QWindowsVistaStylePrivate::ToolTipTheme,
                        TTP_STANDARD, TTSS_NORMAL, option->rect);
        d->drawBackground(theme);
        return;
    }

    case PE_FrameTabBarBase:
        if (const auto *tbb = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
            painter->save();
            switch (tbb->shape) {
            case QTabBar::RoundedNorth:
                painter->setPen(QPen(tbb->palette.dark(), 0));
                painter->drawLine(tbb->rect.topLeft(), tbb->rect.topRight());
                break;
            case QTabBar::RoundedWest:
                painter->setPen(QPen(tbb->palette.dark(), 0));
                painter->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom());
                break;
            case QTabBar::RoundedSouth:
                painter->setPen(QPen(tbb->palette.dark(), 0));
                painter->drawLine(tbb->rect.left(), tbb->rect.top(),
                                  tbb->rect.right(), tbb->rect.top());
                break;
            case QTabBar::RoundedEast:
                painter->setPen(QPen(tbb->palette.dark(), 0));
                painter->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft());
                break;
            case QTabBar::TriangularNorth:
            case QTabBar::TriangularEast:
            case QTabBar::TriangularWest:
            case QTabBar::TriangularSouth:
                painter->restore();
                QWindowsStyle::drawPrimitive(element, option, painter, widget);
                return;
            }
            painter->restore();
        }
        return;

    case PE_Widget: {
#if QT_CONFIG(dialogbuttonbox)
        const QDialogButtonBox *buttonBox = nullptr;
        if (qobject_cast<const QMessageBox *> (widget))
            buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
#if QT_CONFIG(inputdialog)
        else if (qobject_cast<const QInputDialog *> (widget))
            buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox"));
#endif // QT_CONFIG(inputdialog)
        if (buttonBox) {
            //draw white panel part
            QWindowsThemeData theme(widget, painter,
                            QWindowsVistaStylePrivate::TaskDialogTheme,
                            TDLG_PRIMARYPANEL, 0, option->rect);
            QRect toprect = option->rect;
            toprect.setBottom(buttonBox->geometry().top());
            theme.rect = toprect;
            d->drawBackground(theme);

            //draw bottom panel part
            QRect buttonRect = option->rect;
            buttonRect.setTop(buttonBox->geometry().top());
            theme.rect = buttonRect;
            theme.partId = TDLG_SECONDARYPANEL;
            d->drawBackground(theme);
        }
#endif
        return;
    }

    case PE_PanelItemViewItem: {
        const QStyleOptionViewItem *vopt;
        bool newStyle = true;
        QAbstractItemView::SelectionBehavior selectionBehavior = QAbstractItemView::SelectRows;
        QAbstractItemView::SelectionMode selectionMode = QAbstractItemView::NoSelection;
        if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget)) {
            newStyle = !qobject_cast<const QTableView*>(view);
            selectionBehavior = view->selectionBehavior();
            selectionMode = view->selectionMode();
#if QT_CONFIG(accessibility)
        } else if (!widget) {
            newStyle = !QStyleHelper::hasAncestor(option->styleObject, QAccessible::MenuItem) ;
#endif
        }

        if (newStyle && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option))) {
            bool selected = vopt->state & QStyle::State_Selected;
            const bool hover = selectionMode != QAbstractItemView::NoSelection && (vopt->state & QStyle::State_MouseOver);
            bool active = vopt->state & QStyle::State_Active;

            if (vopt->features & QStyleOptionViewItem::Alternate)
                painter->fillRect(vopt->rect, vopt->palette.alternateBase());

            QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled
                    ? QPalette::Normal : QPalette::Disabled;
            if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active))
                cg = QPalette::Inactive;

            QRect itemRect = subElementRect(QStyle::SE_ItemViewItemFocusRect, option, widget).adjusted(-1, 0, 1, 0);
            itemRect.setTop(vopt->rect.top());
            itemRect.setBottom(vopt->rect.bottom());

            QSize sectionSize = itemRect.size();
            if (vopt->showDecorationSelected)
                sectionSize = vopt->rect.size();

            if (selectionBehavior == QAbstractItemView::SelectRows)
                sectionSize.setWidth(vopt->rect.width());
            QPixmap pixmap;

            if (vopt->backgroundBrush.style() != Qt::NoBrush) {
                const QPointF oldBrushOrigin = painter->brushOrigin();
                painter->setBrushOrigin(vopt->rect.topLeft());
                painter->fillRect(vopt->rect, vopt->backgroundBrush);
                painter->setBrushOrigin(oldBrushOrigin);
            }

            if (hover || selected) {
                if (sectionSize.width() > 0 && sectionSize.height() > 0) {
                    QString key = QString::fromLatin1("qvdelegate-%1-%2-%3-%4-%5").arg(sectionSize.width())
                            .arg(sectionSize.height()).arg(selected).arg(active).arg(hover);
                    if (!QPixmapCache::find(key, &pixmap)) {
                        pixmap = QPixmap(sectionSize);
                        pixmap.fill(Qt::transparent);

                        int state;
                        if (selected && hover)
                            state = LISS_HOTSELECTED;
                        else if (selected && !active)
                            state = LISS_SELECTEDNOTFOCUS;
                        else if (selected)
                            state = LISS_SELECTED;
                        else
                            state = LISS_HOT;

                        QPainter pixmapPainter(&pixmap);

                        QWindowsThemeData theme(widget, &pixmapPainter,
                                        QWindowsVistaStylePrivate::VistaTreeViewTheme,
                                        LVP_LISTITEM, state, QRect(0, 0, sectionSize.width(), sectionSize.height()));

                        if (!theme.isValid())
                            break;

                        d->drawBackground(theme);
                        QPixmapCache::insert(key, pixmap);
                    }
                }

                if (vopt->showDecorationSelected) {
                    const int frame = 2; //Assumes a 2 pixel pixmap border
                    QRect srcRect = QRect(0, 0, sectionSize.width(), sectionSize.height());
                    QRect pixmapRect = vopt->rect;
                    bool reverse = vopt->direction == Qt::RightToLeft;
                    bool leftSection = vopt->viewItemPosition == QStyleOptionViewItem::Beginning;
                    bool rightSection = vopt->viewItemPosition == QStyleOptionViewItem::End;
                    if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne
                            || vopt->viewItemPosition == QStyleOptionViewItem::Invalid)
                        painter->drawPixmap(pixmapRect.topLeft(), pixmap);
                    else if (reverse ? rightSection : leftSection){
                        painter->drawPixmap(QRect(pixmapRect.topLeft(),
                                                  QSize(frame, pixmapRect.height())), pixmap,
                                            QRect(QPoint(0, 0), QSize(frame, pixmapRect.height())));
                        painter->drawPixmap(pixmapRect.adjusted(frame, 0, 0, 0),
                                            pixmap, srcRect.adjusted(frame, 0, -frame, 0));
                    } else if (reverse ? leftSection : rightSection) {
                        painter->drawPixmap(QRect(pixmapRect.topRight() - QPoint(frame - 1, 0),
                                                  QSize(frame, pixmapRect.height())), pixmap,
                                            QRect(QPoint(pixmapRect.width() - frame, 0),
                                                  QSize(frame, pixmapRect.height())));
                        painter->drawPixmap(pixmapRect.adjusted(0, 0, -frame, 0),
                                            pixmap, srcRect.adjusted(frame, 0, -frame, 0));
                    } else if (vopt->viewItemPosition == QStyleOptionViewItem::Middle)
                        painter->drawPixmap(pixmapRect, pixmap,
                                            srcRect.adjusted(frame, 0, -frame, 0));
                } else {
                    if (vopt->text.isEmpty() && vopt->icon.isNull())
                        break;
                    painter->drawPixmap(itemRect.topLeft(), pixmap);
                }
            }
            return;
        }
        break;
    }

    default:
        break;
    }

    QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);

    if (!theme.isValid()) {
        QWindowsStyle::drawPrimitive(element, option, painter, widget);
        return;
    }

    theme.mirrorHorizontally = hMirrored;
    theme.mirrorVertically = vMirrored;
    theme.noBorder = noBorder;
    theme.noContent = noContent;
    theme.rotate = rotate;

    d->drawBackground(theme);
}

/*! \internal */
int QWindowsVistaStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
                                  QStyleHintReturn *returnData) const
{
    QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());

    int ret = 0;
    switch (hint) {
    case SH_EtchDisabledText:
        ret = (qobject_cast<const QLabel*>(widget) != 0);
        break;

    case SH_SpinControls_DisableOnBounds:
        ret = 0;
        break;

    case SH_TitleBar_AutoRaise:
    case SH_TitleBar_NoBorder:
        ret = 1;
        break;

    case SH_GroupBox_TextLabelColor:
        if (!widget || widget->isEnabled())
            ret = d->groupBoxTextColor;
        else
            ret = d->groupBoxTextColorDisabled;
        break;

    case SH_WindowFrame_Mask: {
        ret = 1;
        auto *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData);
        const auto *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option);
        if (mask && titlebar) {
            // Note certain themes will not return the whole window frame but only the titlebar part when
            // queried This function needs to return the entire window mask, hence we will only fetch the mask for the
            // titlebar itself and add the remaining part of the window rect at the bottom.
            int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option, widget);
            QRect titleBarRect = option->rect;
            titleBarRect.setHeight(tbHeight);
            QWindowsThemeData themeData;
            if (titlebar->titleBarState & Qt::WindowMinimized) {
                themeData = QWindowsThemeData(widget, nullptr,
                                      QWindowsVistaStylePrivate::WindowTheme,
                                      WP_MINCAPTION, CS_ACTIVE, titleBarRect);
            } else
                themeData = QWindowsThemeData(widget, nullptr,
                                      QWindowsVistaStylePrivate::WindowTheme,
                                      WP_CAPTION, CS_ACTIVE, titleBarRect);
            mask->region = d->region(themeData) +
                    QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight);
        }
        break;
    }

#if QT_CONFIG(rubberband)
    case SH_RubberBand_Mask:
        if (qstyleoption_cast<const QStyleOptionRubberBand *>(option))
            ret = 0;
        break;
#endif // QT_CONFIG(rubberband)

    case SH_MessageBox_CenterButtons:
        ret = false;
        break;

    case SH_ToolTip_Mask:
        if (option) {
            if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(returnData)) {
                ret = true;
                QWindowsThemeData themeData(widget, nullptr,
                                    QWindowsVistaStylePrivate::ToolTipTheme,
                                    TTP_STANDARD, TTSS_NORMAL, option->rect);
                mask->region = d->region(themeData);
            }
        }
        break;

    case SH_Table_GridLineColor:
        if (option)
            ret = int(option->palette.color(QPalette::Base).darker(118).rgba());
        else
            ret = -1;
        break;

    case SH_Header_ArrowAlignment:
        ret = Qt::AlignTop | Qt::AlignHCenter;
        break;

    case SH_ItemView_DrawDelegateFrame:
        ret = 1;
        break;

    default:
        ret = QWindowsStyle::styleHint(hint, option, widget, returnData);
        break;
    }

    return ret;
}


/*!
 \internal

 see drawPrimitive for comments on the animation support
 */
void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption *option,
                                     QPainter *painter, const QWidget *widget) const
{
    if (!QWindowsVistaStylePrivate::useVista()) {
        QWindowsStyle::drawControl(element, option, painter, widget);
        return;
    }

    QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());

    bool selected = option->state & State_Selected;
    bool pressed = option->state & State_Sunken;
    bool disabled = !(option->state & State_Enabled);

    int state = option->state;
    int themeNumber  = -1;

    QRect rect(option->rect);
    State flags = option->state;
    int partId = 0;
    int stateId = 0;

    if (d->transitionsEnabled() && canAnimate(option)) {
        if (element == CE_PushButtonBevel) {
            QRect oldRect;
            QRect newRect;

            QObject *styleObject = option->styleObject;

            int oldState = styleObject->property("_q_stylestate").toInt();
            oldRect = styleObject->property("_q_stylerect").toRect();
            newRect = option->rect;
            styleObject->setProperty("_q_stylestate", int(option->state));
            styleObject->setProperty("_q_stylerect", option->rect);

            bool wasDefault = false;
            bool isDefault = false;
            if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) {
                wasDefault = styleObject->property("_q_isdefault").toBool();
                isDefault = button->features & QStyleOptionButton::DefaultButton;
                styleObject->setProperty("_q_isdefault", isDefault);
            }

            bool doTransition = ((state & State_Sunken)     != (oldState & State_Sunken) ||
                    (state & State_On)         != (oldState & State_On)     ||
                    (state & State_MouseOver)  != (oldState & State_MouseOver));

            if (oldRect != newRect || (wasDefault && !isDefault)) {
                doTransition = false;
                d->stopAnimation(styleObject);
            }

            if (doTransition) {
                styleObject->setProperty("_q_no_animation", true);

                QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject);
                QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
                QStyleOption *styleOption = clonedAnimationStyleOption(option);
                styleOption->state = QStyle::State(oldState);

                QImage startImage = createAnimationBuffer(option, widget);
                QPainter startPainter(&startImage);

                // Use current state of existing animation if already one is running
                if (!anim) {
                    proxy()->drawControl(element, styleOption, &startPainter, widget);
                } else {
                    anim->paint(&startPainter, styleOption);
                    d->stopAnimation(styleObject);
                }

                t->setStartImage(startImage);
                QImage endImage = createAnimationBuffer(option, widget);
                QPainter endPainter(&endImage);
                styleOption->state = option->state;
                proxy()->drawControl(element, styleOption, &endPainter, widget);
                t->setEndImage(endImage);


                DWORD duration = 0;
                const HTHEME theme = OpenThemeData(nullptr, L"Button");

                int fromState = buttonStateId(oldState, BP_PUSHBUTTON);
                int toState = buttonStateId(option->state, BP_PUSHBUTTON);
                if (GetThemeTransitionDuration(theme, BP_PUSHBUTTON, fromState, toState, TMT_TRANSITIONDURATIONS, &duration) == S_OK)
                    t->setDuration(int(duration));
                else
                    t->setDuration(0);
                t->setStartTime(d->animationTime());
                styleObject->setProperty("_q_no_animation", false);

                deleteClonedAnimationStyleOption(styleOption);
                d->startAnimation(t);
            }

            QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
            if (anim) {
                anim->paint(painter, option);
                return;
            }

        }
    }

    bool hMirrored = false;
    bool vMirrored = false;
    int rotate = 0;

    switch (element) {
    case CE_PushButtonBevel:
        if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option))  {
            themeNumber = QWindowsVistaStylePrivate::ButtonTheme;
            partId = BP_PUSHBUTTON;
            if (btn->features & QStyleOptionButton::CommandLinkButton)
                partId = BP_COMMANDLINK;
            bool justFlat = (btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken));
            if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
                stateId = PBS_DISABLED;
            else if (justFlat)
                ;
            else if (flags & (State_Sunken | State_On))
                stateId = PBS_PRESSED;
            else if (flags & State_MouseOver)
                stateId = PBS_HOT;
            else if (btn->features & QStyleOptionButton::DefaultButton && (state & State_Active))
                stateId = PBS_DEFAULTED;
            else
                stateId = PBS_NORMAL;

            if (!justFlat) {

                if (d->transitionsEnabled() && (btn->features & QStyleOptionButton::DefaultButton) &&
                        !(state & (State_Sunken | State_On)) && !(state & State_MouseOver) &&
                        (state & State_Enabled) && (state & State_Active))
                {
                    QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)));

                    if (!anim) {
                        QImage startImage = createAnimationBuffer(option, widget);
                        QImage alternateImage = createAnimationBuffer(option, widget);

                        QWindowsVistaPulse *pulse = new QWindowsVistaPulse(styleObject(option));

                        QPainter startPainter(&startImage);
                        stateId = PBS_DEFAULTED;
                        QWindowsThemeData theme(widget, &startPainter, themeNumber, partId, stateId, rect);
                        d->drawBackground(theme);

                        QPainter alternatePainter(&alternateImage);
                        theme.stateId = PBS_DEFAULTED_ANIMATING;
                        theme.painter = &alternatePainter;
                        d->drawBackground(theme);

                        pulse->setStartImage(startImage);
                        pulse->setEndImage(alternateImage);
                        pulse->setStartTime(d->animationTime());
                        pulse->setDuration(2000);
                        d->startAnimation(pulse);
                        anim = pulse;
                    }

                    if (anim)
                        anim->paint(painter, option);
                    else {
                        QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
                        d->drawBackground(theme);
                    }
                }
                else {
                    QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);
                    d->drawBackground(theme);
                }
            }

            if (btn->features & QStyleOptionButton::HasMenu) {
                int mbiw = 0, mbih = 0;
                QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::ToolBarTheme,
                                TP_DROPDOWNBUTTON);
                if (theme.isValid()) {
                    const QSizeF size = theme.size() * QStyleHelper::dpiScaled(1, option);
                    if (!size.isEmpty()) {
                        mbiw = qRound(size.width());
                        mbih = qRound(size.height());
                    }
                }
                QRect ir = subElementRect(SE_PushButtonContents, option, nullptr);
                QStyleOptionButton newBtn = *btn;
                newBtn.rect = QStyle::visualRect(option->direction, option->rect,
                                                 QRect(ir.right() - mbiw - 2,
                                                       option->rect.top() + (option->rect.height()/2) - (mbih/2),
                                                       mbiw + 1, mbih + 1));
                proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
            }
        }
        return;

    case CE_SizeGrip: {
        themeNumber = QWindowsVistaStylePrivate::StatusTheme;
        partId = SP_GRIPPER;
        QWindowsThemeData theme(nullptr, painter, themeNumber, partId);
        QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
        size.rheight()--;
        if (const auto *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(option)) {
            switch (sg->corner) {
            case Qt::BottomRightCorner:
                rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size);
                break;
            case Qt::BottomLeftCorner:
                rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size);
                hMirrored = true;
                break;
            case Qt::TopRightCorner:
                rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size);
                vMirrored = true;
                break;
            case Qt::TopLeftCorner:
                rect = QRect(rect.topLeft() + QPoint(1, 1), size);
                hMirrored = vMirrored = true;
            }
        }
        break;
    }

    case CE_Splitter:
        painter->eraseRect(option->rect);
        return;

    case CE_TabBarTab:
        if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
            stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED;
        break;

    case CE_TabBarTabShape:
        if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
            themeNumber = QWindowsVistaStylePrivate::TabTheme;
            const bool isDisabled = !(tab->state & State_Enabled);
            const bool hasFocus = tab->state & State_HasFocus;
            const bool isHot = tab->state & State_MouseOver;
            const bool selected = tab->state & State_Selected;
            bool lastTab = tab->position == QStyleOptionTab::End;
            bool firstTab = tab->position == QStyleOptionTab::Beginning;
            const bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
            const bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft;
            const bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter;
            const int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
            const int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option, widget);

            if (isDisabled)
                stateId = TIS_DISABLED;
            else if (selected)
                stateId = TIS_SELECTED;
            else if (hasFocus)
                stateId = TIS_FOCUSED;
            else if (isHot)
                stateId = TIS_HOT;
            else
                stateId = TIS_NORMAL;

            // Selecting proper part depending on position
            if (firstTab || onlyOne) {
                if (leftAligned)
                    partId = TABP_TABITEMLEFTEDGE;
                else if (centerAligned)
                    partId = TABP_TABITEM;
                else // rightAligned
                    partId = TABP_TABITEMRIGHTEDGE;
            } else {
                partId = TABP_TABITEM;
            }

            if (tab->direction == Qt::RightToLeft
                    && (tab->shape == QTabBar::RoundedNorth || tab->shape == QTabBar::RoundedSouth)) {
                bool temp = firstTab;
                firstTab = lastTab;
                lastTab = temp;
            }

            const bool begin = firstTab || onlyOne;
            const bool end = lastTab || onlyOne;

            switch (tab->shape) {
            case QTabBar::RoundedNorth:
                if (selected)
                    rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness);
                else
                    rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0);
                break;
            case QTabBar::RoundedSouth:
                //vMirrored = true;
                rotate = 180; // Not 100% correct, but works
                if (selected)
                    rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0);
                else
                    rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap);
                break;
            case QTabBar::RoundedEast:
                rotate = 90;
                if (selected)
                    rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap);
                else
                    rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0);
                break;
            case QTabBar::RoundedWest:
                hMirrored = true;
                rotate = 90;
                if (selected)
                    rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap);
                else
                    rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0);
                break;
            default:
                themeNumber = -1; // Do our own painting for triangular
                break;
            }

            if (!selected) {
                switch (tab->shape) {
                case QTabBar::RoundedNorth:
                    rect.adjust(0,0, 0,-1);
                    break;
                case QTabBar::RoundedSouth:
                    rect.adjust(0,1, 0,0);
                    break;
                case QTabBar::RoundedEast:
                    rect.adjust( 1,0, 0,0);
                    break;
                case QTabBar::RoundedWest:
                    rect.adjust(0,0, -1,0);
                    break;
                default:
                    break;
                }
            }
        }
        break;

    case CE_ProgressBarGroove: {
        Qt::Orientation orient = Qt::Horizontal;
        if (const auto *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
            if (!(pb->state & QStyle::State_Horizontal))
                orient = Qt::Vertical;

        partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT;
        themeNumber = QWindowsVistaStylePrivate::ProgressTheme;
        stateId = 1;
        break;
    }

    case CE_ProgressBarContents:
        if (const auto *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
            bool isIndeterminate = (bar->minimum == 0 && bar->maximum == 0);
            const bool vertical = !(bar->state & QStyle::State_Horizontal);
            const bool inverted = bar->invertedAppearance;

            if (isIndeterminate || (bar->progress > 0 && (bar->progress < bar->maximum) && d->transitionsEnabled())) {
                if (!d->animation(styleObject(option)))
                    d->startAnimation(new QProgressStyleAnimation(d->animationFps, styleObject(option)));
            } else {
                d->stopAnimation(styleObject(option));
            }

            QWindowsThemeData theme(widget, painter,
                            QWindowsVistaStylePrivate::ProgressTheme,
                            vertical ? PP_FILLVERT : PP_FILL);
            theme.rect = option->rect;
            bool reverse = (bar->direction == Qt::LeftToRight && inverted) || (bar->direction == Qt::RightToLeft && !inverted);
            QTime current = d->animationTime();

            if (isIndeterminate) {
                if (auto *progressAnimation = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) {
                    int glowSize = 120;
                    int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width());
                    int animOffset = progressAnimation->startTime().msecsTo(current) / 4;
                    if (animOffset > animationWidth)
                        progressAnimation->setStartTime(d->animationTime());
                    painter->save();
                    painter->setClipRect(theme.rect);
                    QRect animRect;
                    QSize pixmapSize(14, 14);
                    if (vertical) {
                        animRect = QRect(theme.rect.left(),
                                         inverted ? rect.top() - glowSize + animOffset :
                                                    rect.bottom() + glowSize - animOffset,
                                         rect.width(), glowSize);
                        pixmapSize.setHeight(animRect.height());
                    } else {
                        animRect = QRect(rect.left() - glowSize + animOffset,
                                         rect.top(), glowSize, rect.height());
                        animRect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight,
                                                      option->rect, animRect);
                        pixmapSize.setWidth(animRect.width());
                    }
                    QString name = QString::fromLatin1("qiprogress-%1-%2").arg(pixmapSize.width()).arg(pixmapSize.height());
                    QPixmap pixmap;
                    if (!QPixmapCache::find(name, &pixmap)) {
                        QImage image(pixmapSize, QImage::Format_ARGB32);
                        image.fill(Qt::transparent);
                        QPainter imagePainter(&image);
                        theme.painter = &imagePainter;
                        theme.partId = vertical ? PP_FILLVERT : PP_FILL;
                        theme.rect = QRect(QPoint(0,0), animRect.size());
                        QLinearGradient alphaGradient(0, 0, vertical ? 0 : image.width(),
                                                      vertical ? image.height() : 0);
                        alphaGradient.setColorAt(0, QColor(0, 0, 0, 0));
                        alphaGradient.setColorAt(0.5, QColor(0, 0, 0, 220));
                        alphaGradient.setColorAt(1, QColor(0, 0, 0, 0));
                        imagePainter.fillRect(image.rect(), alphaGradient);
                        imagePainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
                        d->drawBackground(theme);
                        imagePainter.end();
                        pixmap = QPixmap::fromImage(image);
                        QPixmapCache::insert(name, pixmap);
                    }
                    painter->drawPixmap(animRect, pixmap);
                    painter->restore();
                }
            } else {
                qint64 progress = qMax<qint64>(bar->progress, bar->minimum); // workaround for bug in QProgressBar

                if (vertical) {
                    int maxHeight = option->rect.height();
                    int minHeight = 0;
                    double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxHeight);
                    int height = isIndeterminate ? maxHeight: qMax(int(vc6_workaround), minHeight);
                    theme.rect.setHeight(height);
                    if (!inverted)
                        theme.rect.moveTop(rect.height() - theme.rect.height());
                } else {
                    int maxWidth = option->rect.width();
                    int minWidth = 0;
                    double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth);
                    int width = isIndeterminate ? maxWidth : qMax(int(vc6_workaround), minWidth);
                    theme.rect.setWidth(width);
                    theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight,
                                                    option->rect, theme.rect);
                }
                d->drawBackground(theme);

                if (QProgressStyleAnimation *a = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) {
                    int glowSize = 140;
                    int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width());
                    int animOffset = a->startTime().msecsTo(current) / 4;
                    theme.partId = vertical ? PP_MOVEOVERLAYVERT : PP_MOVEOVERLAY;
                    if (animOffset > animationWidth) {
                        if (bar->progress < bar->maximum)
                            a->setStartTime(d->animationTime());
                        else
                            d->stopAnimation(styleObject(option)); //we stop the glow motion only after it has
                        //moved out of view
                    }
                    painter->save();
                    painter->setClipRect(theme.rect);
                    if (vertical) {
                        theme.rect = QRect(theme.rect.left(),
                                           inverted ? rect.top() - glowSize + animOffset :
                                                      rect.bottom() + glowSize - animOffset,
                                           rect.width(), glowSize);
                    } else {
                        theme.rect = QRect(rect.left() - glowSize + animOffset,rect.top(), glowSize, rect.height());
                        theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight, option->rect, theme.rect);
                    }
                    d->drawBackground(theme);
                    painter->restore();
                }
            }
        }
        return;

    case CE_MenuBarItem:
        if (const auto *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option))  {
            if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem)
                break;

            QPalette::ColorRole textRole = disabled ? QPalette::Text : QPalette::ButtonText;
            QPixmap pix = mbi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal);

            int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
            if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget))
                alignment |= Qt::TextHideMnemonic;

            if (widget && mbi->palette.color(QPalette::Window) != Qt::transparent) { // Not needed for QtQuick Controls
                //The rect adjustment is a workaround for the menu not really filling its background.
                QWindowsThemeData theme(widget, painter,
                                QWindowsVistaStylePrivate::MenuTheme,
                                MENU_BARBACKGROUND, 0, option->rect.adjusted(-1, 0, 2, 1));
                d->drawBackground(theme);

                int stateId = MBI_NORMAL;
                if (disabled)
                    stateId = MBI_DISABLED;
                else if (pressed)
                    stateId = MBI_PUSHED;
                else if (selected)
                    stateId = MBI_HOT;

                QWindowsThemeData theme2(widget, painter,
                                 QWindowsVistaStylePrivate::MenuTheme,
                                 MENU_BARITEM, stateId, option->rect);
                d->drawBackground(theme2);
            }

            if (!pix.isNull())
                drawItemPixmap(painter, mbi->rect, alignment, pix);
            else
                drawItemText(painter, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
        }
        return;

#if QT_CONFIG(menu)
    case CE_MenuEmptyArea:
        if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
            QBrush fill = menuitem->palette.brush((menuitem->state & State_Selected) ?
                                                      QPalette::Highlight : QPalette::Button);
            painter->fillRect(rect, fill);
            break;
        }
        return;

    case CE_MenuItem:
        if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
            // windows always has a check column, regardless whether we have an icon or not
            const qreal factor = QWindowsVistaStylePrivate::nativeMetricScaleFactor(widget);
            int checkcol = qRound(qreal(25) * factor);
            const int gutterWidth = qRound(qreal(3) * factor);
            {
                QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::MenuTheme,
                                MENU_POPUPCHECKBACKGROUND, MBI_HOT);
                QWindowsThemeData themeSize = theme;
                themeSize.partId = MENU_POPUPCHECK;
                themeSize.stateId = 0;
                const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
                const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
                checkcol = qMax(menuitem->maxIconWidth, qRound(gutterWidth + size.width() + margins.left() + margins.right()));
            }
            QRect rect = option->rect;

            //draw vertical menu line
            if (option->direction == Qt::LeftToRight)
                checkcol += rect.x();
            QPoint p1 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.top()));
            QPoint p2 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.bottom()));
            QRect gutterRect(p1.x(), p1.y(), gutterWidth, p2.y() - p1.y() + 1);
            QWindowsThemeData theme2(widget, painter, QWindowsVistaStylePrivate::MenuTheme,
                             MENU_POPUPGUTTER, stateId, gutterRect);
            d->drawBackground(theme2);

            int x, y, w, h;
            menuitem->rect.getRect(&x, &y, &w, &h);
            int tab = menuitem->reservedShortcutWidth;
            bool dis = !(menuitem->state & State_Enabled);
            bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable
                    ? menuitem->checked : false;
            bool act = menuitem->state & State_Selected;

            if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
                int yoff = y-2 + h / 2;
                const int separatorSize = qRound(qreal(6) * QWindowsStylePrivate::nativeMetricScaleFactor(widget));
                QPoint p1 = QPoint(x + checkcol, yoff);
                QPoint p2 = QPoint(x + w + separatorSize, yoff);
                stateId = MBI_HOT;
                QRect subRect(p1.x() + (gutterWidth - menuitem->rect.x()), p1.y(),
                              p2.x() - p1.x(), separatorSize);
                subRect  = QStyle::visualRect(option->direction, option->rect, subRect );
                QWindowsThemeData theme2(widget, painter,
                                 QWindowsVistaStylePrivate::MenuTheme,
                                 MENU_POPUPSEPARATOR, stateId, subRect);
                d->drawBackground(theme2);
                return;
            }

            QRect vCheckRect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x(),
                                                                                   menuitem->rect.y(), checkcol - (gutterWidth + menuitem->rect.x()), menuitem->rect.height()));

            if (act) {
                stateId = dis ? MBI_DISABLED : MBI_HOT;
                QWindowsThemeData theme2(widget, painter,
                                 QWindowsVistaStylePrivate::MenuTheme,
                                 MENU_POPUPITEM, stateId, option->rect);
                d->drawBackground(theme2);
            }

            if (checked) {
                QWindowsThemeData theme(widget, painter,
                                QWindowsVistaStylePrivate::MenuTheme,
                                MENU_POPUPCHECKBACKGROUND,
                                menuitem->icon.isNull() ? MBI_HOT : MBI_PUSHED, vCheckRect);
                QWindowsThemeData themeSize = theme;
                themeSize.partId = MENU_POPUPCHECK;
                themeSize.stateId = 0;
                const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
                const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
                QRect checkRect(0, 0, qRound(size.width() + margins.left() + margins.right()),
                                qRound(size.height() + margins.bottom() + margins.top()));
                checkRect.moveCenter(vCheckRect.center());
                theme.rect = checkRect;

                d->drawBackground(theme);

                if (menuitem->icon.isNull()) {
                    checkRect = QRect(QPoint(0, 0), size.toSize());
                    checkRect.moveCenter(theme.rect.center());
                    theme.rect = checkRect;

                    theme.partId = MENU_POPUPCHECK;
                    bool bullet = menuitem->checkType & QStyleOptionMenuItem::Exclusive;
                    if (dis)
                        theme.stateId = bullet ? MC_BULLETDISABLED: MC_CHECKMARKDISABLED;
                    else
                        theme.stateId = bullet ? MC_BULLETNORMAL: MC_CHECKMARKNORMAL;
                    d->drawBackground(theme);
                } else if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11
                           && !act) {
                    painter->fillRect(checkRect, menuitem->palette.highlight().color().lighter(200));
                }
            }

            if (!menuitem->icon.isNull()) {
                QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
                if (act && !dis)
                    mode = QIcon::Active;
                const auto size = proxy()->pixelMetric(PM_SmallIconSize, option, widget);
                const auto dpr = painter->device()->devicePixelRatio();
                const auto pixmap = menuitem->icon.pixmap({size, size}, dpr, mode,
                                                          checked ? QIcon::On : QIcon::Off);
                QRect pmr(QPoint(0, 0), pixmap.deviceIndependentSize().toSize());
                pmr.moveCenter(vCheckRect.center());
                painter->setPen(menuitem->palette.text().color());
                painter->drawPixmap(pmr.topLeft(), pixmap);
            }

            painter->setPen(menuitem->palette.buttonText().color());

            const QColor textColor = menuitem->palette.text().color();
            if (dis)
                painter->setPen(textColor);

            int xm = windowsItemFrame + checkcol + windowsItemHMargin + (gutterWidth - menuitem->rect.x()) - 1;
            int xpos = menuitem->rect.x() + xm;
            QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
            QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect);
            QString s = menuitem->text;
            if (!s.isEmpty()) {    // draw text
                painter->save();
                int t = s.indexOf(QLatin1Char('\t'));
                int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
                if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
                    text_flags |= Qt::TextHideMnemonic;
                text_flags |= Qt::AlignLeft;
                if (t >= 0) {
                    QRect vShortcutRect = visualRect(option->direction, menuitem->rect,
                                                     QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom())));
                    painter->drawText(vShortcutRect, text_flags, s.mid(t + 1));
                    s = s.left(t);
                }
                QFont font = menuitem->font;
                if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
                    font.setBold(true);
                painter->setFont(font);
                painter->setPen(textColor);
                painter->drawText(vTextRect, text_flags, s.left(t));
                painter->restore();
            }
            if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
                int dim = (h - 2 * windowsItemFrame) / 2;
                PrimitiveElement arrow;
                arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
                xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
                QRect  vSubMenuRect = visualRect(option->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
                QStyleOptionMenuItem newMI = *menuitem;
                newMI.rect = vSubMenuRect;
                newMI.state = dis ? State_None : State_Enabled;
                proxy()->drawPrimitive(arrow, &newMI, painter, widget);
            }
        }
        return;
#endif // QT_CONFIG(menu)

    case CE_HeaderSection:
        if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
            partId = HP_HEADERITEM;
            if (flags & State_Sunken)
                stateId = HIS_PRESSED;
            else if (flags & State_MouseOver)
                stateId = HIS_HOT;
            else
                stateId = HIS_NORMAL;

            if (header->sortIndicator != QStyleOptionHeader::None)
                stateId += 3;

            QWindowsThemeData theme(widget, painter,
                            QWindowsVistaStylePrivate::HeaderTheme,
                            partId, stateId, option->rect);
            d->drawBackground(theme);
        }
        return;

    case CE_MenuBarEmptyArea: {
        stateId = MBI_NORMAL;
        if (!(state & State_Enabled))
            stateId = MBI_DISABLED;
        QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::MenuTheme,
                        MENU_BARBACKGROUND, stateId, option->rect);
        d->drawBackground(theme);
        return;
    }

    case CE_ToolBar:
        if (const auto *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
            QPalette pal = option->palette;
            pal.setColor(QPalette::Dark, option->palette.window().color().darker(130));
            QStyleOptionToolBar copyOpt = *toolbar;
            copyOpt.palette = pal;
            QWindowsStyle::drawControl(element, &copyOpt, painter, widget);
        }
        return;

#if QT_CONFIG(dockwidget)
    case CE_DockWidgetTitle:
        if (const auto *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
            QRect rect = option->rect;
            const QDockWidget *dw = qobject_cast<const QDockWidget *>(widget);
            bool isFloating = dw && dw->isFloating();
            int buttonMargin = 4;
            int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget);
            int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget);

            const bool verticalTitleBar = dwOpt->verticalTitleBar;

            if (verticalTitleBar) {
                rect = rect.transposed();

                painter->translate(rect.left() - 1, rect.top() + rect.width());
                painter->rotate(-90);
                painter->translate(-rect.left() + 1, -rect.top());
            }

            QRect r = option->rect.adjusted(0, 2, -1, -3);
            QRect titleRect = r;

            if (dwOpt->closable) {
                QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10));
                titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
            }

            if (dwOpt->floatable) {
                QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10));
                titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
            }

            if (isFloating) {
                titleRect.adjust(0, -fw, 0, 0);
                if (widget && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey())
                    titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
            } else {
                titleRect.adjust(mw, 0, 0, 0);
                if (!dwOpt->floatable && !dwOpt->closable)
                    titleRect.adjust(0, 0, -mw, 0);
            }

            if (!verticalTitleBar)
                titleRect = visualRect(dwOpt->direction, r, titleRect);

            if (isFloating) {
                const bool isActive = dwOpt->state & State_Active;
                themeNumber = QWindowsVistaStylePrivate::WindowTheme;
                if (isActive)
                    stateId = CS_ACTIVE;
                else
                    stateId = CS_INACTIVE;

                int titleHeight = rect.height() - 2;
                rect = rect.adjusted(-fw, -fw, fw, 0);

                QWindowsThemeData theme(widget, painter, themeNumber, 0, stateId);
                if (!theme.isValid())
                    break;

                // Draw small type title bar
                theme.rect = rect;
                theme.partId = WP_SMALLCAPTION;
                d->drawBackground(theme);

                // Figure out maximal button space on title bar

                QIcon ico = widget->windowIcon();
                bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey());
                if (hasIcon) {
                    QPixmap pxIco = ico.pixmap(titleHeight);
                    if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft)
                        painter->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco);
                    else
                        painter->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco);
                }
                if (!dwOpt->title.isEmpty()) {
                    QPen oldPen = painter->pen();
                    QFont oldFont = painter->font();
                    QFont titleFont = oldFont;
                    titleFont.setBold(true);
                    painter->setFont(titleFont);
                    QString titleText
                            = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());

                    int result = TST_NONE;
                    GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
                    if (result != TST_NONE) {
                        COLORREF textShadowRef;
                        GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
                        QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
                        painter->setPen(textShadow);
                        drawItemText(painter, titleRect.adjusted(1, 1, 1, 1),
                                     Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
                                     dwOpt->state & State_Enabled, titleText);
                    }

                    COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
                    QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
                    painter->setPen(textColor);
                    drawItemText(painter, titleRect,
                                 Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
                                 dwOpt->state & State_Enabled, titleText);
                    painter->setFont(oldFont);
                    painter->setPen(oldPen);
                }
            }  else {
                painter->setBrush(option->palette.window().color().darker(110));
                painter->setPen(option->palette.window().color().darker(130));
                painter->drawRect(rect.adjusted(0, 1, -1, -3));

                if (!dwOpt->title.isEmpty()) {
                    QString titleText = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight,
                                                                          verticalTitleBar ? titleRect.height() : titleRect.width());
                    const int indent = 4;
                    drawItemText(painter, rect.adjusted(indent + 1, 1, -indent - 1, -1),
                                 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic,
                                 dwOpt->palette,
                                 dwOpt->state & State_Enabled, titleText,
                                 QPalette::WindowText);
                }
            }
        }
        return;
#endif // QT_CONFIG(dockwidget)

#if QT_CONFIG(rubberband)
    case CE_RubberBand:
        if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) {
            QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight);
            painter->save();
            painter->setPen(highlight.darker(120));
            QColor dimHighlight(qMin(highlight.red()/2 + 110, 255),
                                qMin(highlight.green()/2 + 110, 255),
                                qMin(highlight.blue()/2 + 110, 255),
                                (widget && widget->isWindow())? 255 : 127);
            painter->setBrush(dimHighlight);
            painter->drawRect(option->rect.adjusted(0, 0, -1, -1));
            painter->restore();
            return;
        }
        break;
#endif // QT_CONFIG(rubberband)

    case CE_HeaderEmptyArea:
        if (option->state & State_Horizontal) {
            themeNumber = QWindowsVistaStylePrivate::HeaderTheme;
            stateId = HIS_NORMAL;
        } else {
            QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, painter, widget);
            return;
        }
        break;

#if QT_CONFIG(itemviews)
    case CE_ItemViewItem: {
        const QStyleOptionViewItem *vopt;
        const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget);
        bool newStyle = true;

        if (qobject_cast<const QTableView*>(widget))
            newStyle = false;

        QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);

        if (newStyle && view && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option))) {
            /*
                    // We cannot currently get the correct selection color for "explorer style" views
                    COLORREF cref = 0;
                    QWindowsThemeData theme(d->treeViewHelper(), 0, QLatin1String("LISTVIEW"), 0, 0);
                    unsigned int res = GetThemeColor(theme.handle(), LVP_LISTITEM, LISS_SELECTED, TMT_TEXTCOLOR, &cref);
                    QColor textColor(GetRValue(cref), GetGValue(cref), GetBValue(cref));
                    */
            QPalette palette = vopt->palette;
            palette.setColor(QPalette::All, QPalette::HighlightedText, palette.color(QPalette::Active, QPalette::Text));
            // Note that setting a saturated color here results in ugly XOR colors in the focus rect
            palette.setColor(QPalette::All, QPalette::Highlight, palette.base().color().darker(108));
            QStyleOptionViewItem adjustedOption = *vopt;
            adjustedOption.palette = palette;
            // We hide the  focusrect in singleselection as it is not required
            if ((view->selectionMode() == QAbstractItemView::SingleSelection)
                    && !(vopt->state & State_KeyboardFocusChange))
                adjustedOption.state &= ~State_HasFocus;
            if (!theme.isValid()) {
                QWindowsStyle::drawControl(element, &adjustedOption, painter, widget);
                return;
            }
        } else {
            if (!theme.isValid()) {
                QWindowsStyle::drawControl(element, option, painter, widget);
                return;
            }
        }

        theme.rotate = rotate;
        theme.mirrorHorizontally = hMirrored;
        theme.mirrorVertically = vMirrored;
        d->drawBackground(theme);
        return;
    }
#endif // QT_CONFIG(itemviews)

#if QT_CONFIG(combobox)
    case CE_ComboBoxLabel:
        QCommonStyle::drawControl(element, option, painter, widget);
        return;
#endif // QT_CONFIG(combobox)

    default:
        break;
    }

    QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect);

    if (!theme.isValid()) {
        QWindowsStyle::drawControl(element, option, painter, widget);
        return;
    }

    theme.rotate = rotate;
    theme.mirrorHorizontally = hMirrored;
    theme.mirrorVertically = vMirrored;

    d->drawBackground(theme);
}

/*!
  \internal
  see drawPrimitive for comments on the animation support

 */
void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
                                            QPainter *painter, const QWidget *widget) const
{
    QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func());

    if (!QWindowsVistaStylePrivate::useVista()) {
        QWindowsStyle::drawComplexControl(control, option, painter, widget);
        return;
    }

    State state = option->state;
    SubControls sub = option->subControls;
    QRect r = option->rect;

    int partId = 0;
    int stateId = 0;

    State flags = option->state;
    if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
        flags |= State_MouseOver;

    if (d->transitionsEnabled() && canAnimate(option))
    {
        if (control == CC_ScrollBar || control == CC_SpinBox || control == CC_ComboBox) {
            QObject *styleObject = option->styleObject; // Can be widget or qquickitem

            int oldState = styleObject->property("_q_stylestate").toInt();
            int oldActiveControls = styleObject->property("_q_stylecontrols").toInt();

            QRect oldRect = styleObject->property("_q_stylerect").toRect();
            styleObject->setProperty("_q_stylestate", int(option->state));
            styleObject->setProperty("_q_stylecontrols", int(option->activeSubControls));
            styleObject->setProperty("_q_stylerect", option->rect);

            bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken)
                    || (state & State_On) != (oldState & State_On)
                    || (state & State_MouseOver) != (oldState & State_MouseOver)
                    || oldActiveControls != int(option->activeSubControls));

            if (qstyleoption_cast<const QStyleOptionSlider *>(option)) {
                QRect oldSliderPos = styleObject->property("_q_stylesliderpos").toRect();
                QRect currentPos = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
                styleObject->setProperty("_q_stylesliderpos", currentPos);
                if (oldSliderPos != currentPos) {
                    doTransition = false;
                    d->stopAnimation(styleObject);
                }
            } else if (control == CC_SpinBox) {
                //spinboxes have a transition when focus changes
                if (!doTransition)
                    doTransition = (state & State_HasFocus) != (oldState & State_HasFocus);
            }

            if (oldRect != option->rect) {
                doTransition = false;
                d->stopAnimation(styleObject);
            }

            if (doTransition) {
                QImage startImage = createAnimationBuffer(option, widget);
                QPainter startPainter(&startImage);

                QImage endImage = createAnimationBuffer(option, widget);
                QPainter endPainter(&endImage);

                QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject));
                QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject);

                // Draw the image that ends the animation by using the current styleoption
                QStyleOptionComplex *styleOption = qstyleoption_cast<QStyleOptionComplex*>(clonedAnimationStyleOption(option));

                styleObject->setProperty("_q_no_animation", true);

                // Draw transition source
                if (!anim) {
                    styleOption->state = QStyle::State(oldState);
                    styleOption->activeSubControls = QStyle::SubControl(oldActiveControls);
                    proxy()->drawComplexControl(control, styleOption, &startPainter, widget);
                } else {
                    anim->paint(&startPainter, option);
                }
                t->setStartImage(startImage);

                // Draw transition target
                styleOption->state = option->state;
                styleOption->activeSubControls = option->activeSubControls;
                proxy()->drawComplexControl(control, styleOption, &endPainter, widget);

                styleObject->setProperty("_q_no_animation", false);

                t->setEndImage(endImage);
                t->setStartTime(d->animationTime());

                if (option->state & State_MouseOver || option->state & State_Sunken)
                    t->setDuration(150);
                else
                    t->setDuration(500);

                deleteClonedAnimationStyleOption(styleOption);
                d->startAnimation(t);
            }
            if (QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject))) {
                anim->paint(painter, option);
                return;
            }
        }
    }

    switch (control) {

#if QT_CONFIG(slider)
    case CC_Slider:
        if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
            QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::TrackBarTheme);
            QRect slrect = slider->rect;
            QRegion tickreg = slrect;
            if (sub & SC_SliderGroove) {
                theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
                if (slider->orientation == Qt::Horizontal) {
                    partId = TKP_TRACK;
                    stateId = TRS_NORMAL;
                    theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4);
                } else {
                    partId = TKP_TRACKVERT;
                    stateId = TRVS_NORMAL;
                    theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height());
                }
                theme.partId = partId;
                theme.stateId = stateId;
                d->drawBackground(theme);
                tickreg -= theme.rect;
            }
            if (sub & SC_SliderTickmarks) {
                int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget);
                int ticks = slider->tickPosition;
                int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget);
                int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
                int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
                int interval = slider->tickInterval;
                if (interval <= 0) {
                    interval = slider->singleStep;
                    if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
                                                        available)
                            - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
                                                              0, available) < 3)
                        interval = slider->pageStep;
                }
                if (!interval)
                    interval = 1;
                int fudge = len / 2;
                int pos;
                int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0;
                painter->setPen(d->sliderTickColor);
                QVarLengthArray<QLine, 32> lines;
                int v = slider->minimum;
                while (v <= slider->maximum + 1) {
                    if (v == slider->maximum + 1 && interval == 1)
                        break;
                    const int v_ = qMin(v, slider->maximum);
                    int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3;
                    pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
                                                          v_, available) + fudge;
                    if (slider->orientation == Qt::Horizontal) {
                        if (ticks & QSlider::TicksAbove) {
                            lines.append(QLine(pos, tickOffset - 1 - bothOffset,
                                               pos, tickOffset - 1 - bothOffset - tickLength));
                        }

                        if (ticks & QSlider::TicksBelow) {
                            lines.append(QLine(pos, tickOffset + thickness + bothOffset,
                                               pos, tickOffset + thickness + bothOffset + tickLength));
                        }
                    } else {
                        if (ticks & QSlider::TicksAbove) {
                            lines.append(QLine(tickOffset - 1 - bothOffset, pos,
                                               tickOffset - 1 - bothOffset - tickLength, pos));
                        }

                        if (ticks & QSlider::TicksBelow) {
                            lines.append(QLine(tickOffset + thickness + bothOffset, pos,
                                               tickOffset + thickness + bothOffset + tickLength, pos));
                        }
                    }
                    // in the case where maximum is max int
                    int nextInterval = v + interval;
                    if (nextInterval < v)
                        break;
                    v = nextInterval;
                }
                if (!lines.isEmpty()) {
                    painter->save();
                    painter->translate(slrect.topLeft());
                    painter->drawLines(lines.constData(), lines.size());
                    painter->restore();
                }
            }
            if (sub & SC_SliderHandle) {
                theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
                if (slider->orientation == Qt::Horizontal) {
                    if (slider->tickPosition == QSlider::TicksAbove)
                        partId = TKP_THUMBTOP;
                    else if (slider->tickPosition == QSlider::TicksBelow)
                        partId = TKP_THUMBBOTTOM;
                    else
                        partId = TKP_THUMB;

                    if (!(slider->state & State_Enabled))
                        stateId = TUS_DISABLED;
                    else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
                        stateId = TUS_PRESSED;
                    else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
                        stateId = TUS_HOT;
                    else if (flags & State_HasFocus)
                        stateId = TUS_FOCUSED;
                    else
                        stateId = TUS_NORMAL;
                } else {
                    if (slider->tickPosition == QSlider::TicksLeft)
                        partId = TKP_THUMBLEFT;
                    else if (slider->tickPosition == QSlider::TicksRight)
                        partId = TKP_THUMBRIGHT;
                    else
                        partId = TKP_THUMBVERT;

                    if (!(slider->state & State_Enabled))
                        stateId = TUVS_DISABLED;
                    else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
                        stateId = TUVS_PRESSED;
                    else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
                        stateId = TUVS_HOT;
                    else if (flags & State_HasFocus)
                        stateId = TUVS_FOCUSED;
                    else
                        stateId = TUVS_NORMAL;
                }
                theme.partId = partId;
                theme.stateId = stateId;
                d->drawBackground(theme);
            }
            if (slider->state & State_HasFocus) {
                QStyleOptionFocusRect fropt;
                fropt.QStyleOption::operator=(*slider);
                fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
                proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
            }
        }
        break;
#endif

#if QT_CONFIG(toolbutton)
    case CC_ToolButton:
        if (const auto *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
            QRect button, menuarea;
            button = proxy()->subControlRect(control, toolbutton, SC_ToolButton, widget);
            menuarea = proxy()->subControlRect(control, toolbutton, SC_ToolButtonMenu, widget);

            State bflags = toolbutton->state & ~State_Sunken;
            State mflags = bflags;
            bool autoRaise = flags & State_AutoRaise;
            if (autoRaise) {
                if (!(bflags & State_MouseOver) || !(bflags & State_Enabled))
                    bflags &= ~State_Raised;
            }

            if (toolbutton->state & State_Sunken) {
                if (toolbutton->activeSubControls & SC_ToolButton) {
                    bflags |= State_Sunken;
                    mflags |= State_MouseOver | State_Sunken;
                } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) {
                    mflags |= State_Sunken;
                    bflags |= State_MouseOver;
                }
            }

            QStyleOption tool = *toolbutton;
            if (toolbutton->subControls & SC_ToolButton) {
                if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) {
                    if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) {
                        QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ToolBarTheme);
                        theme.partId = TP_SPLITBUTTON;
                        theme.rect = button;
                        if (!(bflags & State_Enabled))
                            stateId = TS_DISABLED;
                        else if (bflags & State_Sunken)
                            stateId = TS_PRESSED;
                        else if (bflags & State_MouseOver || !(flags & State_AutoRaise))
                            stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
                        else if (bflags & State_On)
                            stateId = TS_CHECKED;
                        else
                            stateId = TS_NORMAL;
                        if (option->direction == Qt::RightToLeft)
                            theme.mirrorHorizontally = true;
                        theme.stateId = stateId;
                        d->drawBackground(theme);
                    } else {
                        tool.rect = option->rect;
                        tool.state = bflags;
                        if (autoRaise) // for tool bars
                            proxy()->drawPrimitive(PE_PanelButtonTool, &tool, painter, widget);
                        else
                            proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, painter, widget);
                    }
                }
            }

            if (toolbutton->state & State_HasFocus) {
                QStyleOptionFocusRect fr;
                fr.QStyleOption::operator=(*toolbutton);
                fr.rect.adjust(3, 3, -3, -3);
                if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup)
                    fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator,
                                                               toolbutton, widget), 0);
                proxy()->drawPrimitive(PE_FrameFocusRect, &fr, painter, widget);
            }
            QStyleOptionToolButton label = *toolbutton;
            label.state = bflags;
            int fw = 2;
            if (!autoRaise)
                label.state &= ~State_Sunken;
            label.rect = button.adjusted(fw, fw, -fw, -fw);
            proxy()->drawControl(CE_ToolButtonLabel, &label, painter, widget);

            if (toolbutton->subControls & SC_ToolButtonMenu) {
                tool.rect = menuarea;
                tool.state = mflags;
                if (autoRaise) {
                    proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, painter, widget);
                } else {
                    tool.state = mflags;
                    menuarea.adjust(-2, 0, 0, 0);
                    // Draw menu button
                    if ((bflags & State_Sunken) != (mflags & State_Sunken)){
                        painter->save();
                        painter->setClipRect(menuarea);
                        tool.rect = option->rect;
                        proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, painter, nullptr);
                        painter->restore();
                    }
                    // Draw arrow
                    painter->save();
                    painter->setPen(option->palette.dark().color());
                    painter->drawLine(menuarea.left(), menuarea.top() + 3,
                                      menuarea.left(), menuarea.bottom() - 3);
                    painter->setPen(option->palette.light().color());
                    painter->drawLine(menuarea.left() - 1, menuarea.top() + 3,
                                      menuarea.left() - 1, menuarea.bottom() - 3);

                    tool.rect = menuarea.adjusted(2, 3, -2, -1);
                    proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget);
                    painter->restore();
                }
            } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) {
                int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget);
                QRect ir = toolbutton->rect;
                QStyleOptionToolButton newBtn = *toolbutton;
                newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5);
                proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget);
            }
        }
        break;
#endif // QT_CONFIG(toolbutton)

    case CC_TitleBar:
        if (const auto *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
            const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
            bool isActive = tb->titleBarState & QStyle::State_Active;
            QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::WindowTheme);
            if (sub & SC_TitleBarLabel) {
                partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION;
                theme.rect = option->rect;
                if (widget && !widget->isEnabled())
                    stateId = CS_DISABLED;
                else if (isActive)
                    stateId = CS_ACTIVE;
                else
                    stateId = CS_INACTIVE;

                theme.partId = partId;
                theme.stateId = stateId;
                d->drawBackground(theme);

                QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget);

                int result = TST_NONE;
                GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
                if (result != TST_NONE) {
                    COLORREF textShadowRef;
                    GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
                    QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
                    painter->setPen(textShadow);
                    painter->drawText(int(ir.x() + 3 * factor), int(ir.y() + 2 * factor),
                                      int(ir.width() - 1 * factor), ir.height(),
                                      Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
                }
                COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
                QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
                painter->setPen(textColor);
                painter->drawText(int(ir.x() + 2 * factor), int(ir.y() + 1 * factor),
                                  int(ir.width() - 2 * factor), ir.height(),
                                  Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
            }
            if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
                theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget);
                partId = WP_SYSBUTTON;
                if ((widget && !widget->isEnabled()) || !isActive)
                    stateId = SBS_DISABLED;
                else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken))
                    stateId = SBS_PUSHED;
                else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver))
                    stateId = SBS_HOT;
                else
                    stateId = SBS_NORMAL;
                if (!tb->icon.isNull()) {
                    tb->icon.paint(painter, theme.rect);
                } else {
                    theme.partId = partId;
                    theme.stateId = stateId;
                    if (theme.size().isEmpty()) {
                        int iconSize = proxy()->pixelMetric(PM_SmallIconSize, tb, widget);
                        QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, tb, widget).pixmap(iconSize, iconSize);
                        painter->save();
                        drawItemPixmap(painter, theme.rect, Qt::AlignCenter, pm);
                        painter->restore();
                    } else {
                        d->drawBackground(theme);
                    }
                }
            }

            if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint
                    && !(tb->titleBarState & Qt::WindowMinimized)) {
                populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMinButton, isActive, WP_MINBUTTON, &theme);
                d->drawBackground(theme);
            }
            if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint
                    && !(tb->titleBarState & Qt::WindowMaximized)) {
                populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMaxButton, isActive, WP_MAXBUTTON, &theme);
                d->drawBackground(theme);
            }
            if (sub & SC_TitleBarContextHelpButton
                    && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) {
                populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarContextHelpButton, isActive, WP_HELPBUTTON, &theme);
                d->drawBackground(theme);
            }
            bool drawNormalButton = (sub & SC_TitleBarNormalButton)
                    && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
                         && (tb->titleBarState & Qt::WindowMinimized))
                        || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
                            && (tb->titleBarState & Qt::WindowMaximized)));
            if (drawNormalButton) {
                populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarNormalButton, isActive, WP_RESTOREBUTTON, &theme);
                d->drawBackground(theme);
            }
            if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
                    && !(tb->titleBarState & Qt::WindowMinimized)) {
                populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarShadeButton, isActive, WP_MINBUTTON, &theme);
                d->drawBackground(theme);
            }
            if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
                    && tb->titleBarState & Qt::WindowMinimized) {
                populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarUnshadeButton, isActive, WP_RESTOREBUTTON, &theme);
                d->drawBackground(theme);
            }
            if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
                populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarCloseButton, isActive, WP_CLOSEBUTTON, &theme);
                d->drawBackground(theme);
            }
        }
        break;

#if QT_CONFIG(mdiarea)
    case CC_MdiControls: {
        QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL);
        if (Q_UNLIKELY(!theme.isValid()))
            return;

        if (option->subControls.testFlag(SC_MdiCloseButton)) {
            populateMdiButtonTheme(proxy(), widget, option, SC_MdiCloseButton, WP_MDICLOSEBUTTON, &theme);
            d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
        }
        if (option->subControls.testFlag(SC_MdiNormalButton)) {
            populateMdiButtonTheme(proxy(), widget, option, SC_MdiNormalButton, WP_MDIRESTOREBUTTON, &theme);
            d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
        }
        if (option->subControls.testFlag(QStyle::SC_MdiMinButton)) {
            populateMdiButtonTheme(proxy(), widget, option, SC_MdiMinButton, WP_MDIMINBUTTON, &theme);
            d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
        }
        break;
    }
#endif // QT_CONFIG(mdiarea)

#if QT_CONFIG(dial)
    case CC_Dial:
        if (const auto *dial = qstyleoption_cast<const QStyleOptionSlider *>(option))
            QStyleHelper::drawDial(dial, painter);
        break;
#endif // QT_CONFIG(dial)

    case CC_ComboBox:
        if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
            if (cmb->editable) {
                if (sub & SC_ComboBoxEditField) {
                    partId = EP_EDITBORDER_NOSCROLL;
                    if (!(flags & State_Enabled))
                        stateId = ETS_DISABLED;
                    else if (flags & State_MouseOver)
                        stateId = ETS_HOT;
                    else if (flags & State_HasFocus)
                        stateId = ETS_FOCUSED;
                    else
                        stateId = ETS_NORMAL;

                    QWindowsThemeData theme(widget, painter,
                                    QWindowsVistaStylePrivate::EditTheme,
                                    partId, stateId, r);

                    d->drawBackground(theme);
                }
                if (sub & SC_ComboBoxArrow) {
                    QRect subRect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
                    QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme);
                    theme.rect = subRect;
                    partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;

                    if (!(cmb->state & State_Enabled))
                        stateId = CBXS_DISABLED;
                    else if (cmb->state & State_Sunken || cmb->state & State_On)
                        stateId = CBXS_PRESSED;
                    else if (cmb->state & State_MouseOver && option->activeSubControls & SC_ComboBoxArrow)
                        stateId = CBXS_HOT;
                    else
                        stateId = CBXS_NORMAL;

                    theme.partId = partId;
                    theme.stateId = stateId;
                    d->drawBackground(theme);
                }

            } else {
                if (sub & SC_ComboBoxFrame) {
                    QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme);
                    theme.rect = option->rect;
                    theme.partId = CP_READONLY;
                    if (!(cmb->state & State_Enabled))
                        theme.stateId = CBXS_DISABLED;
                    else if (cmb->state & State_Sunken || cmb->state & State_On)
                        theme.stateId = CBXS_PRESSED;
                    else if (cmb->state & State_MouseOver)
                        theme.stateId = CBXS_HOT;
                    else
                        theme.stateId = CBXS_NORMAL;
                    d->drawBackground(theme);
                }
                if (sub & SC_ComboBoxArrow) {
                    QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme);
                    theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
                    theme.partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;
                    if (!(cmb->state & State_Enabled))
                        theme.stateId = CBXS_DISABLED;
                    else
                        theme.stateId = CBXS_NORMAL;
                    d->drawBackground(theme);
                }
                if ((sub & SC_ComboBoxEditField) && (flags & State_HasFocus)) {
                    QStyleOptionFocusRect fropt;
                    fropt.QStyleOption::operator=(*cmb);
                    fropt.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget);
                    proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget);
                }
            }
        }
        break;

    case CC_ScrollBar:
        if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
            QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ScrollBarTheme);
            bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
            if (maxedOut)
                flags &= ~State_Enabled;

            bool isHorz = flags & State_Horizontal;
            bool isRTL  = option->direction == Qt::RightToLeft;
            if (sub & SC_ScrollBarAddLine) {
                theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
                partId = SBP_ARROWBTN;
                if (!(flags & State_Enabled))
                    stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED);
                else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken))
                    stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED);
                else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver))
                    stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT);
                else if (scrollbar->state & State_MouseOver)
                    stateId = (isHorz ? (isRTL ? ABS_LEFTHOVER : ABS_RIGHTHOVER) : ABS_DOWNHOVER);
                else
                    stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL);
                theme.partId = partId;
                theme.stateId = stateId;
                d->drawBackground(theme);
            }
            if (sub & SC_ScrollBarSubLine) {
                theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
                partId = SBP_ARROWBTN;
                if (!(flags & State_Enabled))
                    stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED);
                else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken))
                    stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED);
                else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver))
                    stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT);
                else if (scrollbar->state & State_MouseOver)
                    stateId = (isHorz ? (isRTL ? ABS_RIGHTHOVER : ABS_LEFTHOVER) : ABS_UPHOVER);
                else
                    stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL);
                theme.partId = partId;
                theme.stateId = stateId;
                d->drawBackground(theme);
            }
            if (maxedOut) {
                theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
                theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget));
                theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget));
                partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
                stateId = SCRBS_DISABLED;
                theme.partId = partId;
                theme.stateId = stateId;
                d->drawBackground(theme);
            } else {
                if (sub & SC_ScrollBarSubPage) {
                    theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget);
                    partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
                    if (!(flags & State_Enabled))
                        stateId = SCRBS_DISABLED;
                    else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken))
                        stateId = SCRBS_PRESSED;
                    else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver))
                        stateId = SCRBS_HOT;
                    else
                        stateId = SCRBS_NORMAL;
                    theme.partId = partId;
                    theme.stateId = stateId;
                    d->drawBackground(theme);
                }
                if (sub & SC_ScrollBarAddPage) {
                    theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget);
                    partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
                    if (!(flags & State_Enabled))
                        stateId = SCRBS_DISABLED;
                    else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken))
                        stateId = SCRBS_PRESSED;
                    else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver))
                        stateId = SCRBS_HOT;
                    else
                        stateId = SCRBS_NORMAL;
                    theme.partId = partId;
                    theme.stateId = stateId;
                    d->drawBackground(theme);
                }
                if (sub & SC_ScrollBarSlider) {
                    theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
                    if (!(flags & State_Enabled))
                        stateId = SCRBS_DISABLED;
                    else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken))
                        stateId = SCRBS_PRESSED;
                    else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver))
                        stateId = SCRBS_HOT;
                    else if (option->state & State_MouseOver)
                        stateId = SCRBS_HOVER;
                    else
                        stateId = SCRBS_NORMAL;

                    // Draw handle
                    theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
                    theme.stateId = stateId;
                    d->drawBackground(theme);
                }
            }
        }
        break;

#if QT_CONFIG(spinbox)
    case CC_SpinBox:
        if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) {
            QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::SpinTheme);
            if (sb->frame && (sub & SC_SpinBoxFrame)) {
                partId = EP_EDITBORDER_NOSCROLL;
                if (!(flags & State_Enabled))
                    stateId = ETS_DISABLED;
                else if (flags & State_MouseOver)
                    stateId = ETS_HOT;
                else if (flags & State_HasFocus)
                    stateId = ETS_SELECTED;
                else
                    stateId = ETS_NORMAL;

                QWindowsThemeData ftheme(widget, painter,
                                 QWindowsVistaStylePrivate::EditTheme,
                                 partId, stateId, r);
                // The spinbox in Windows QStyle is drawn with frameless QLineEdit inside it
                // That however breaks with QtQuickControls where this results in transparent
                // spinbox background, so if there's no "widget" passed (QtQuickControls case),
                // let ftheme.noContent be false, which fixes the spinbox rendering in QQC
                ftheme.noContent = (widget != nullptr);
                d->drawBackground(ftheme);
            }
            if (sub & SC_SpinBoxUp) {
                theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget).adjusted(0, 0, 0, 1);
                partId = SPNP_UP;
                if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled))
                    stateId = UPS_DISABLED;
                else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken))
                    stateId = UPS_PRESSED;
                else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver))
                    stateId = UPS_HOT;
                else
                    stateId = UPS_NORMAL;
                theme.partId = partId;
                theme.stateId = stateId;
                d->drawBackground(theme);
            }
            if (sub & SC_SpinBoxDown) {
                theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
                partId = SPNP_DOWN;
                if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled))
                    stateId = DNS_DISABLED;
                else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken))
                    stateId = DNS_PRESSED;
                else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver))
                    stateId = DNS_HOT;
                else
                    stateId = DNS_NORMAL;
                theme.partId = partId;
                theme.stateId = stateId;
                d->drawBackground(theme);
            }
        }
        break;
#endif // QT_CONFIG(spinbox)

    default:
        QWindowsStyle::drawComplexControl(control, option, painter, widget);
        break;
    }
}

/*!
 \internal
 */
QSize QWindowsVistaStyle::sizeFromContents(ContentsType type, const QStyleOption *option,
                                           const QSize &size, const QWidget *widget) const
{
    if (!QWindowsVistaStylePrivate::useVista())
        return QWindowsStyle::sizeFromContents(type, option, size, widget);

    QSize contentSize(size);

    switch (type) {
    case CT_LineEdit:
    case CT_ComboBox: {
        QWindowsThemeData buttontheme(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL);
        if (buttontheme.isValid()) {
            const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
            const QMarginsF borderSize = buttontheme.margins() * factor;
            if (!borderSize.isNull()) {
                const qreal margin = qreal(2) * factor;
                contentSize.rwidth() += qRound(borderSize.left() + borderSize.right() - margin);
                contentSize.rheight() += int(borderSize.bottom() + borderSize.top() - margin
                                             + qreal(1) / factor - 1);
            }
            const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, option) + 1);
            contentSize += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget)
                                      + textMargins, 23), 0); //arrow button
        }
        break;
    }

    case CT_TabWidget:
        contentSize += QSize(6, 6);
        break;

    case CT_Menu:
        contentSize += QSize(1, 0);
        break;

#if QT_CONFIG(menubar)
    case CT_MenuBarItem:
        if (!contentSize.isEmpty())
            contentSize += QSize(windowsItemHMargin * 5 + 1, 5);
        break;
#endif

    case CT_MenuItem: {
        contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
        QWindowsThemeData theme(widget, nullptr,
                                QWindowsVistaStylePrivate::MenuTheme,
                                MENU_POPUPCHECKBACKGROUND, MBI_HOT);
        QWindowsThemeData themeSize = theme;
        themeSize.partId = MENU_POPUPCHECK;
        themeSize.stateId = 0;
        const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
        const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
        int minimumHeight = qMax(qRound(size.height() + margins.bottom() + margins.top()), contentSize.height());
        contentSize.rwidth() += qRound(size.width() + margins.left() + margins.right());
        if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) {
            if (menuitem->menuItemType != QStyleOptionMenuItem::Separator)
                contentSize.setHeight(minimumHeight);
        }
        break;
    }

    case CT_MdiControls: {
        contentSize.setHeight(int(QStyleHelper::dpiScaled(19, option)));
        int width = 54;
        if (const auto *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) {
            width = 0;
            if (styleOpt->subControls & SC_MdiMinButton)
                width += 17 + 1;
            if (styleOpt->subControls & SC_MdiNormalButton)
                width += 17 + 1;
            if (styleOpt->subControls & SC_MdiCloseButton)
                width += 17 + 1;
        }
        contentSize.setWidth(int(QStyleHelper::dpiScaled(width, option)));
        break;
    }

    case CT_ItemViewItem:
        contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
        contentSize.rheight() += 2;
        break;

    case CT_SpinBox: {
        //Spinbox adds frame twice
        contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
        int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget);
        contentSize -= QSize(2*border, 2*border);
        break;
    }

    case CT_HeaderSection:
        // When there is a sort indicator it adds to the width but it is shown
        // above the text natively and not on the side
        if (QStyleOptionHeader *hdr = qstyleoption_cast<QStyleOptionHeader *>(const_cast<QStyleOption *>(option))) {
            QStyleOptionHeader::SortIndicator sortInd = hdr->sortIndicator;
            hdr->sortIndicator = QStyleOptionHeader::None;
            contentSize = QWindowsStyle::sizeFromContents(type, hdr, size, widget);
            hdr->sortIndicator = sortInd;
        }
        break;

    default:
        contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget);
        break;
    }

    return contentSize;
}

/*!
 \internal
 */
QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const
{
    if (!QWindowsVistaStylePrivate::useVista())
        return QWindowsStyle::subElementRect(element, option, widget);

    QRect rect(option->rect);

    switch (element) {
    case SE_PushButtonContents:
        if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
            MARGINS borderSize;
            const HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"Button");
            if (theme) {
                int stateId = PBS_NORMAL;
                if (!(option->state & State_Enabled))
                    stateId = PBS_DISABLED;
                else if (option->state & State_Sunken)
                    stateId = PBS_PRESSED;
                else if (option->state & State_MouseOver)
                    stateId = PBS_HOT;
                else if (btn->features & QStyleOptionButton::DefaultButton)
                    stateId = PBS_DEFAULTED;

                int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget);
                rect = option->rect.adjusted(border, border, -border, -border);

                if (SUCCEEDED(GetThemeMargins(theme, nullptr, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, nullptr, &borderSize))) {
                    rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight,
                                -borderSize.cxRightWidth, -borderSize.cyBottomHeight);
                    rect = visualRect(option->direction, option->rect, rect);
                }
            }
        }
        break;

    case SE_DockWidgetCloseButton:
    case SE_DockWidgetFloatButton:
        rect = QWindowsStyle::subElementRect(element, option, widget);
        return rect.translated(0, 1);

    case SE_TabWidgetTabContents:
        rect = QWindowsStyle::subElementRect(element, option, widget);
        if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))  {
            rect = QWindowsStyle::subElementRect(element, option, widget);
            if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) {
                if (tabWidget->documentMode())
                    break;
                rect.adjust(0, 0, -2, -2);
            }
        }
        break;

    case SE_TabWidgetTabBar: {
        rect = QWindowsStyle::subElementRect(element, option, widget);
        const auto *twfOption = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option);
        if (twfOption && twfOption->direction == Qt::RightToLeft
                && (twfOption->shape == QTabBar::RoundedNorth
                    || twfOption->shape == QTabBar::RoundedSouth))
        {
            QStyleOptionTab otherOption;
            otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth
                                 ? QTabBar::RoundedEast : QTabBar::RoundedSouth);
            int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget);
            int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
            rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0);
        }
        break;
    }

    case SE_HeaderArrow: {
        rect = QWindowsStyle::subElementRect(element, option, widget);
        QRect r = rect;
        int h = option->rect.height();
        int w = option->rect.width();
        int x = option->rect.x();
        int y = option->rect.y();
        int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget);

        QWindowsThemeData theme(widget, nullptr,
                        QWindowsVistaStylePrivate::HeaderTheme,
                        HP_HEADERSORTARROW, HSAS_SORTEDDOWN, option->rect);

        int arrowWidth = 13;
        int arrowHeight = 5;
        if (theme.isValid()) {
            const QSizeF size = theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
            if (!size.isEmpty()) {
                arrowWidth = qRound(size.width());
                arrowHeight = qRound(size.height());
            }
        }
        if (option->state & State_Horizontal) {
            r.setRect(x + w/2 - arrowWidth/2, y , arrowWidth, arrowHeight);
        } else {
            int vert_size = w / 2;
            r.setRect(x + 5, y + h - margin * 2 - vert_size,
                      w - margin * 2 - 5, vert_size);
        }
        rect = visualRect(option->direction, option->rect, r);
        break;
    }

    case SE_HeaderLabel: {
        rect = QWindowsStyle::subElementRect(element, option, widget);
        int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget);
        QRect r = option->rect;
        r.setRect(option->rect.x() + margin, option->rect.y() + margin,
                  option->rect.width() - margin * 2, option->rect.height() - margin * 2);
        if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
            // Subtract width needed for arrow, if there is one
            if (header->sortIndicator != QStyleOptionHeader::None) {
                if (!(option->state & State_Horizontal)) //horizontal arrows are positioned on top
                    r.setHeight(r.height() - (option->rect.width() / 2) - (margin * 2));
            }
        }
        rect = visualRect(option->direction, option->rect, r);
        break;
    }

    case SE_ProgressBarContents:
        rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget);
        break;

    case SE_ItemViewItemDecoration:
        rect = QWindowsStyle::subElementRect(element, option, widget);
        if (qstyleoption_cast<const QStyleOptionViewItem *>(option))
            rect.adjust(-2, 0, 2, 0);
        break;

    case SE_ItemViewItemFocusRect:
        rect = QWindowsStyle::subElementRect(element, option, widget);
        if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) {
            QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, option, widget);
            QRect displayRect = subElementRect(QStyle::SE_ItemViewItemDecoration, option, widget);
            if (!vopt->icon.isNull())
                rect = textRect.united(displayRect);
            else
                rect = textRect;
            rect = rect.adjusted(1, 0, -1, 0);
        }
        break;

    default:
        rect = QWindowsStyle::subElementRect(element, option, widget);
        break;
    }

    return rect;
}

/*!
 \internal
 */
QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option,
                                         SubControl subControl, const QWidget *widget) const
{
    if (!QWindowsVistaStylePrivate::useVista())
        return QWindowsStyle::subControlRect(control, option, subControl, widget);

    QRect rect;

    switch (control) {
#if QT_CONFIG(combobox)
    case CC_ComboBox:
        if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
            const int x = cb->rect.x(), y = cb->rect.y(), wi = cb->rect.width(), he = cb->rect.height();
            const int margin = cb->frame ? 3 : 0;
            const int bmarg = cb->frame ? 2 : 0;
            const int arrowWidth = qRound(QStyleHelper::dpiScaled(16, option));
            const int arrowButtonWidth = bmarg + arrowWidth;
            const int xpos = x + wi - arrowButtonWidth;

            switch (subControl) {
            case SC_ComboBoxFrame:
            case SC_ComboBoxListBoxPopup:
                rect = cb->rect;
                break;

            case SC_ComboBoxArrow:  {
                rect.setRect(xpos, y , arrowButtonWidth, he);
            }
                break;

            case SC_ComboBoxEditField:  {
                rect.setRect(x + margin, y + margin, wi - 2 * margin - arrowWidth, he - 2 * margin);
            }
                break;

            default:
                break;
            }
        }
        break;
#endif // QT_CONFIG(combobox)

    case CC_TitleBar:
        if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
            if (!buttonVisible(subControl, tb))
                return rect;
            const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
            const bool isToolTitle = false;
            const int height = tb->rect.height();
            const int width = tb->rect.width();

            const int buttonMargin = int(QStyleHelper::dpiScaled(4, option));
            int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor)
                    - buttonMargin;
            const int buttonWidth =
                    qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor - QStyleHelper::dpiScaled(4, option));

            const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget);
            const bool sysmenuHint  = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0;
            const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0;
            const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0;
            const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0;
            const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0;

            bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
            bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
            int offset = 0;
            const int delta = buttonWidth + 2;
            int controlTop = option->rect.bottom() - buttonHeight - 2;

            switch (subControl) {
            case SC_TitleBarLabel:  {
                rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height);
                if (isToolTitle) {
                    if (sysmenuHint) {
                        rect.adjust(0, 0, int(-buttonWidth - 3 * factor), 0);
                    }
                    if (minimizeHint || maximizeHint)
                        rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
                } else {
                    if (sysmenuHint) {
                        const int leftOffset = int(height - 8 * factor);
                        rect.adjust(leftOffset, 0, 0, int(4 * factor));
                    }
                    if (minimizeHint)
                        rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
                    if (maximizeHint)
                        rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
                    if (contextHint)
                        rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
                    if (shadeHint)
                        rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0);
                }
                rect.translate(0, int(2 * factor));
            }
                break;

            case SC_TitleBarContextHelpButton:
                if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
                    offset += delta;
                Q_FALLTHROUGH();
            case SC_TitleBarMinButton:
                if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
                    offset += delta;
                else if (subControl == SC_TitleBarMinButton)
                    break;
                Q_FALLTHROUGH();
            case SC_TitleBarNormalButton:
                if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
                    offset += delta;
                else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
                    offset += delta;
                else if (subControl == SC_TitleBarNormalButton)
                    break;
                Q_FALLTHROUGH();
            case SC_TitleBarMaxButton:
                if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
                    offset += delta;
                else if (subControl == SC_TitleBarMaxButton)
                    break;
                Q_FALLTHROUGH();
            case SC_TitleBarShadeButton:
                if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
                    offset += delta;
                else if (subControl == SC_TitleBarShadeButton)
                    break;
                Q_FALLTHROUGH();
            case SC_TitleBarUnshadeButton:
                if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
                    offset += delta;
                else if (subControl == SC_TitleBarUnshadeButton)
                    break;
                Q_FALLTHROUGH();
            case SC_TitleBarCloseButton:
                if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
                    offset += delta;
                else if (subControl == SC_TitleBarCloseButton)
                    break;

                rect.setRect(width - offset - controlTop + 1, controlTop,
                             buttonWidth, buttonHeight);
                break;

            case SC_TitleBarSysMenu:    {
                const int controlTop = int(6 * factor);
                const int controlHeight = int(height - controlTop - 3 * factor);
                int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option);
                QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent));
                if (tb->icon.isNull())
                    iconSize = QSize(controlHeight, controlHeight);
                int hPad = (controlHeight - iconSize.height())/2;
                int vPad = (controlHeight - iconSize.width())/2;
                rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height());
                rect.translate(0, int(3 * factor));
            }
                break;

            default:
                break;
            }
        }
        break;

#if QT_CONFIG(mdiarea)
    case CC_MdiControls: {
        int numSubControls = 0;
        if (option->subControls & SC_MdiCloseButton)
            ++numSubControls;
        if (option->subControls & SC_MdiMinButton)
            ++numSubControls;
        if (option->subControls & SC_MdiNormalButton)
            ++numSubControls;
        if (numSubControls == 0)
            break;

        int buttonWidth = option->rect.width() / numSubControls;
        int offset = 0;

        switch (subControl) {
        case SC_MdiCloseButton:
            // Only one sub control, no offset needed.
            if (numSubControls == 1)
                break;
            offset += buttonWidth;
            Q_FALLTHROUGH();
        case SC_MdiNormalButton:
            // No offset needed if
            // 1) There's only one sub control
            // 2) We have a close button and a normal button (offset already added in SC_MdiClose)
            if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton)))
                break;
            if (option->subControls & SC_MdiNormalButton)
                offset += buttonWidth;
            break;
        default:
            break;
        }

        rect = QRect(offset, 0, buttonWidth, option->rect.height());
        break;
    }
#endif // QT_CONFIG(mdiarea)

    default:
        return QWindowsStyle::subControlRect(control, option, subControl, widget);
    }

    return visualRect(option->direction, option->rect, rect);
}

/*!
 \internal
 */
QStyle::SubControl QWindowsVistaStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
                                                             const QPoint &pos, const QWidget *widget) const
{
    return QWindowsStyle::hitTestComplexControl(control, option, pos, widget);
}

/*!
 \internal
 */
int QWindowsVistaStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
{
    if (!QWindowsVistaStylePrivate::useVista())
        return QWindowsStyle::pixelMetric(metric, option, widget);

    int ret = QWindowsVistaStylePrivate::fixedPixelMetric(metric);
    if (ret != QWindowsStylePrivate::InvalidMetric)
        return int(QStyleHelper::dpiScaled(ret, option));

    int res = QWindowsVistaStylePrivate::pixelMetricFromSystemDp(metric, option, widget);
    if (res != QWindowsStylePrivate::InvalidMetric)
        return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(widget));

    res = 0;

    switch (metric) {
    case PM_MenuBarPanelWidth:
    case PM_ButtonDefaultIndicator:
        res = 0;
        break;

    case PM_DefaultFrameWidth:
        res = qobject_cast<const QListView*>(widget) ? 2 : 1;
        break;
    case PM_MenuPanelWidth:
    case PM_SpinBoxFrameWidth:
        res = 1;
        break;

    case PM_TabBarTabOverlap:
    case PM_MenuHMargin:
    case PM_MenuVMargin:
        res = 2;
        break;

    case PM_TabBarBaseOverlap:
        if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
            switch (tab->shape) {
            case QTabBar::RoundedNorth:
            case QTabBar::TriangularNorth:
            case QTabBar::RoundedWest:
            case QTabBar::TriangularWest:
                res = 1;
                break;
            case QTabBar::RoundedSouth:
            case QTabBar::TriangularSouth:
                res = 2;
                break;
            case QTabBar::RoundedEast:
            case QTabBar::TriangularEast:
                res = 3;
                break;
            }
        }
        break;

    case PM_SplitterWidth:
        res = QStyleHelper::dpiScaled(5., option);
        break;

    case PM_MdiSubWindowMinimizedWidth:
        res = 160;
        break;

#if QT_CONFIG(toolbar)
    case PM_ToolBarHandleExtent:
        res = int(QStyleHelper::dpiScaled(8., option));
        break;

#endif // QT_CONFIG(toolbar)
    case PM_DockWidgetSeparatorExtent:
    case PM_DockWidgetTitleMargin:
        res = int(QStyleHelper::dpiScaled(4., option));
        break;

    case PM_ButtonShiftHorizontal:
    case PM_ButtonShiftVertical:
        res = qstyleoption_cast<const QStyleOptionToolButton *>(option) ? 1 : 0;
        break;

    default:
        res = QWindowsStyle::pixelMetric(metric, option, widget);
    }

    return res;
}

/*!
 \internal
 */
void QWindowsVistaStyle::polish(QWidget *widget)
{
    QWindowsStyle::polish(widget);
    if (!QWindowsVistaStylePrivate::useVista())
        return;

    if (false
#if QT_CONFIG(abstractbutton)
        || qobject_cast<QAbstractButton*>(widget)
#endif // QT_CONFIG(abstractbutton)
        || qobject_cast<QToolButton*>(widget)
        || qobject_cast<QTabBar*>(widget)
#if QT_CONFIG(combobox)
        || qobject_cast<QComboBox*>(widget)
#endif // QT_CONFIG(combobox)
        || qobject_cast<QScrollBar*>(widget)
        || qobject_cast<QSlider*>(widget)
        || qobject_cast<QHeaderView*>(widget)
#if QT_CONFIG(spinbox)
        || qobject_cast<QAbstractSpinBox*>(widget)
        || qobject_cast<QSpinBox*>(widget)
#endif // QT_CONFIG(spinbox)
            ) {
        widget->setAttribute(Qt::WA_Hover);
    }

#if QT_CONFIG(rubberband)
    if (qobject_cast<QRubberBand*>(widget))
        widget->setWindowOpacity(0.6);
#endif

#if QT_CONFIG(lineedit)
    if (qobject_cast<QLineEdit*>(widget))
        widget->setAttribute(Qt::WA_Hover);
    else
#endif // QT_CONFIG(lineedit)
        if (qobject_cast<QGroupBox*>(widget))
            widget->setAttribute(Qt::WA_Hover);
#if QT_CONFIG(commandlinkbutton)
        else if (qobject_cast<QCommandLinkButton*>(widget)) {
            widget->setProperty("_qt_usingVistaStyle", true);
            QFont buttonFont = widget->font();
            buttonFont.setFamilies(QStringList{QLatin1String("Segoe UI")});
            widget->setFont(buttonFont);
            QPalette pal = widget->palette();
            pal.setColor(QPalette::Active, QPalette::ButtonText, QColor(21, 28, 85));
            pal.setColor(QPalette::Active, QPalette::BrightText, QColor(7, 64, 229));
            widget->setPalette(pal);
        }
#endif // QT_CONFIG(commandlinkbutton)
        else if (widget->inherits("QTipLabel")) {
            //note that since tooltips are not reused
            //we do not have to care about unpolishing
            widget->setContentsMargins(3, 0, 4, 0);
            COLORREF bgRef;
            HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"TOOLTIP");
            if (theme && SUCCEEDED(GetThemeColor(theme, TTP_STANDARD, TTSS_NORMAL, TMT_TEXTCOLOR, &bgRef))) {
                QColor textColor = QColor::fromRgb(bgRef);
                QPalette pal;
                pal.setColor(QPalette::All, QPalette::ToolTipText, textColor);
                pal.setResolveMask(0);
                widget->setPalette(pal);
            }
        } else if (qobject_cast<QMessageBox *> (widget)) {
            widget->setAttribute(Qt::WA_StyledBackground);
#if QT_CONFIG(dialogbuttonbox)
            QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
            if (buttonBox)
                buttonBox->setContentsMargins(0, 9, 0, 0);
#endif
        }
#if QT_CONFIG(inputdialog)
        else if (qobject_cast<QInputDialog *> (widget)) {
            widget->setAttribute(Qt::WA_StyledBackground);
#if QT_CONFIG(dialogbuttonbox)
            QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox"));
            if (buttonBox)
                buttonBox->setContentsMargins(0, 9, 0, 0);
#endif
        }
#endif // QT_CONFIG(inputdialog)
        else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
            tree->viewport()->setAttribute(Qt::WA_Hover);
        }
        else if (QListView *list = qobject_cast<QListView *> (widget)) {
            list->viewport()->setAttribute(Qt::WA_Hover);
        }
}

/*!
 \internal
 */
void QWindowsVistaStyle::unpolish(QWidget *widget)
{
    Q_D(QWindowsVistaStyle);

#if QT_CONFIG(rubberband)
    if (qobject_cast<QRubberBand*>(widget))
        widget->setWindowOpacity(1.0);
#endif

    // Unpolish of widgets is the first thing that
    // happens when a theme changes, or the theme
    // engine is turned off. So we detect it here.
    bool oldState = QWindowsVistaStylePrivate::useVista();
    bool newState = QWindowsVistaStylePrivate::useVista(true);
    if ((oldState != newState) && newState) {
        d->cleanup(true);
        d->init(true);
    } else {
        // Cleanup handle map, if just changing style,
        // or turning it on. In both cases the values
        // already in the map might be old (other style).
        d->cleanupHandleMap();
    }
    if (false
        #if QT_CONFIG(abstractbutton)
            || qobject_cast<QAbstractButton*>(widget)
        #endif
            || qobject_cast<QToolButton*>(widget)
            || qobject_cast<QTabBar*>(widget)
        #if QT_CONFIG(combobox)
            || qobject_cast<QComboBox*>(widget)
        #endif // QT_CONFIG(combobox)
            || qobject_cast<QScrollBar*>(widget)
            || qobject_cast<QSlider*>(widget)
            || qobject_cast<QHeaderView*>(widget)
        #if QT_CONFIG(spinbox)
            || qobject_cast<QAbstractSpinBox*>(widget)
            || qobject_cast<QSpinBox*>(widget)
        #endif // QT_CONFIG(spinbox)
            ) {
        widget->setAttribute(Qt::WA_Hover, false);
    }

    QWindowsStyle::unpolish(widget);

    d->stopAnimation(widget);

#if QT_CONFIG(lineedit)
    if (qobject_cast<QLineEdit*>(widget))
        widget->setAttribute(Qt::WA_Hover, false);
    else {
#endif // QT_CONFIG(lineedit)
        if (qobject_cast<QGroupBox*>(widget))
            widget->setAttribute(Qt::WA_Hover, false);
        else if (qobject_cast<QMessageBox *> (widget)) {
            widget->setAttribute(Qt::WA_StyledBackground, false);
#if QT_CONFIG(dialogbuttonbox)
            QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
            if (buttonBox)
                buttonBox->setContentsMargins(0, 0, 0, 0);
#endif
        }
#if QT_CONFIG(inputdialog)
        else if (qobject_cast<QInputDialog *> (widget)) {
            widget->setAttribute(Qt::WA_StyledBackground, false);
#if QT_CONFIG(dialogbuttonbox)
            QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox"));
            if (buttonBox)
                buttonBox->setContentsMargins(0, 0, 0, 0);
#endif
        }
#endif // QT_CONFIG(inputdialog)
        else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
            tree->viewport()->setAttribute(Qt::WA_Hover, false);
        }
#if QT_CONFIG(commandlinkbutton)
        else if (qobject_cast<QCommandLinkButton*>(widget)) {
            QFont font = QApplication::font("QCommandLinkButton");
            QFont widgetFont = widget->font();
            widgetFont.setFamilies(font.families()); //Only family set by polish
            widget->setFont(widgetFont);
        }
#endif // QT_CONFIG(commandlinkbutton)
    }
}

/*!
 \internal
 */
void QWindowsVistaStyle::polish(QPalette &pal)
{
    Q_D(QWindowsVistaStyle);

    if (QGuiApplicationPrivate::colorScheme() == Qt::ColorScheme::Dark) {
        // System runs in dark mode, but the Vista style cannot use a dark palette.
        // Overwrite with the light system palette.
        using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
        if (auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration()))
            nativeWindowsApp->populateLightSystemPalette(pal);
    }

    QPixmapCache::clear();
    d->alphaCache.clear();
    d->hasInitColors = false;

    if (!d->hasInitColors) {
        // Get text color for group box labels
        QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::ButtonTheme, 0, 0);
        COLORREF cref;
        GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref);
        d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
        GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref);
        d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
        // Where does this color come from?
        //GetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref);
        d->sliderTickColor = qRgb(165, 162, 148);
        d->hasInitColors = true;
    }

    QWindowsStyle::polish(pal);
    pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(104));
}

/*!
 \internal
 */
void QWindowsVistaStyle::polish(QApplication *app)
{
    // Override windows theme palettes to light
    if (qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark) {
        static const char* themedWidgets[] = {
            "QToolButton",
            "QAbstractButton",
            "QCheckBox",
            "QRadioButton",
            "QHeaderView",
            "QAbstractItemView",
            "QMessageBoxLabel",
            "QTabBar",
            "QLabel",
            "QGroupBox",
            "QMenu",
            "QMenuBar",
            "QTextEdit",
            "QTextControl",
            "QLineEdit"
        };
        for (const auto& themedWidget : std::as_const(themedWidgets)) {
            auto defaultResolveMask = QApplication::palette().resolveMask();
            auto widgetResolveMask = QApplication::palette(themedWidget).resolveMask();
            if (widgetResolveMask != defaultResolveMask)
                QApplication::setPalette(QApplication::palette(), themedWidget);
        }
    }

    QWindowsStyle::polish(app);
}

/*!
 \internal
 */
QPixmap QWindowsVistaStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option,
                                           const QWidget *widget) const
{
    if (!QWindowsVistaStylePrivate::useVista()) {
        return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
    }

    switch (standardPixmap) {
    case SP_TitleBarMaxButton:
    case SP_TitleBarCloseButton:
        if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
            if (widget && widget->isWindow()) {
                QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL);
                if (theme.isValid()) {
                    const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
                    return QIcon(QWindowsStyle::standardPixmap(standardPixmap, option, widget)).pixmap(size);
                }
            }
        }
        break;

    default:
        break;
    }

    return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
}

/*!
\reimp
*/
QIcon QWindowsVistaStyle::standardIcon(StandardPixmap standardIcon,
                                       const QStyleOption *option,
                                       const QWidget *widget) const
{
    if (!QWindowsVistaStylePrivate::useVista()) {
        return QWindowsStyle::standardIcon(standardIcon, option, widget);
    }

    auto *d = const_cast<QWindowsVistaStylePrivate*>(d_func());

    switch (standardIcon) {
    case SP_TitleBarMaxButton:
        if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
            if (d->dockFloat.isNull()) {
                QWindowsThemeData themeSize(nullptr, nullptr, QWindowsVistaStylePrivate::WindowTheme,
                                    WP_SMALLCLOSEBUTTON, CBS_NORMAL);
                QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::WindowTheme,
                                WP_MAXBUTTON, MAXBS_NORMAL);
                if (theme.isValid()) {
                    const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
                    QPixmap pm(size);
                    pm.fill(Qt::transparent);
                    QPainter p(&pm);
                    theme.painter = &p;
                    theme.rect = QRect(QPoint(0, 0), size);
                    d->drawBackground(theme);
                    d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off);    // Normal
                    pm.fill(Qt::transparent);
                    theme.stateId = MAXBS_PUSHED;
                    d->drawBackground(theme);
                    d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On);     // Pressed
                    pm.fill(Qt::transparent);
                    theme.stateId = MAXBS_HOT;
                    d->drawBackground(theme);
                    d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off);    // Hover
                    pm.fill(Qt::transparent);
                    theme.stateId = MAXBS_INACTIVE;
                    d->drawBackground(theme);
                    d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off);  // Disabled
                }
            }
            if (widget && widget->isWindow())
                return d->dockFloat;
        }
        break;

    case SP_TitleBarCloseButton:
        if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
            if (d->dockClose.isNull()) {
                QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::WindowTheme,
                                WP_SMALLCLOSEBUTTON, CBS_NORMAL);
                if (theme.isValid()) {
                    const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
                    QPixmap pm(size);
                    pm.fill(Qt::transparent);
                    QPainter p(&pm);
                    theme.painter = &p;
                    theme.partId = WP_CLOSEBUTTON; // ####
                    theme.rect = QRect(QPoint(0, 0), size);
                    d->drawBackground(theme);
                    d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::Off);    // Normal
                    pm.fill(Qt::transparent);
                    theme.stateId = CBS_PUSHED;
                    d->drawBackground(theme);
                    d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::On);     // Pressed
                    pm.fill(Qt::transparent);
                    theme.stateId = CBS_HOT;
                    d->drawBackground(theme);
                    d->dockClose.addPixmap(pm, QIcon::Active, QIcon::Off);    // Hover
                    pm.fill(Qt::transparent);
                    theme.stateId = CBS_INACTIVE;
                    d->drawBackground(theme);
                    d->dockClose.addPixmap(pm, QIcon::Disabled, QIcon::Off);  // Disabled
                }
            }
            if (widget && widget->isWindow())
                return d->dockClose;
        }
        break;

    case SP_TitleBarNormalButton:
        if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) {
            if (d->dockFloat.isNull()) {
                QWindowsThemeData themeSize(nullptr, nullptr, QWindowsVistaStylePrivate::WindowTheme,
                                    WP_SMALLCLOSEBUTTON, CBS_NORMAL);
                QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::WindowTheme,
                                WP_RESTOREBUTTON, RBS_NORMAL);
                if (theme.isValid()) {
                    const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
                    QPixmap pm(size);
                    pm.fill(Qt::transparent);
                    QPainter p(&pm);
                    theme.painter = &p;
                    theme.rect = QRect(QPoint(0, 0), size);
                    d->drawBackground(theme);
                    d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off);    // Normal
                    pm.fill(Qt::transparent);
                    theme.stateId = RBS_PUSHED;
                    d->drawBackground(theme);
                    d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On);     // Pressed
                    pm.fill(Qt::transparent);
                    theme.stateId = RBS_HOT;
                    d->drawBackground(theme);
                    d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off);    // Hover
                    pm.fill(Qt::transparent);
                    theme.stateId = RBS_INACTIVE;
                    d->drawBackground(theme);
                    d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off);  // Disabled
                }
            }
            if (widget && widget->isWindow())
                return d->dockFloat;
        }
        break;

    case SP_CommandLink: {
        QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::ButtonTheme,
                        BP_COMMANDLINKGLYPH, CMDLGS_NORMAL);
        if (theme.isValid()) {
            const QSize size = theme.size().toSize();
            QIcon linkGlyph;
            QPixmap pm(size);
            pm.fill(Qt::transparent);
            QPainter p(&pm);
            theme.painter = &p;
            theme.rect = QRect(QPoint(0, 0), size);
            d->drawBackground(theme);
            linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::Off);    // Normal
            pm.fill(Qt::transparent);

            theme.stateId = CMDLGS_PRESSED;
            d->drawBackground(theme);
            linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::On);     // Pressed
            pm.fill(Qt::transparent);

            theme.stateId = CMDLGS_HOT;
            d->drawBackground(theme);
            linkGlyph.addPixmap(pm, QIcon::Active, QIcon::Off);    // Hover
            pm.fill(Qt::transparent);

            theme.stateId = CMDLGS_DISABLED;
            d->drawBackground(theme);
            linkGlyph.addPixmap(pm, QIcon::Disabled, QIcon::Off);  // Disabled
            return linkGlyph;
        }
        break;
    }

    default:
        break;
    }

    return QWindowsStyle::standardIcon(standardIcon, option, widget);
}

QT_END_NAMESPACE
