1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 #include "qwindowsxpstyle_p.h"
40 #include "qwindowsxpstyle_p_p.h"
41 
42 #include <private/qobject_p.h>
43 #include <private/qpaintengine_raster_p.h>
44 #include <private/qapplication_p.h>
45 #include <qpa/qplatformnativeinterface.h>
46 #include <private/qstylehelper_p.h>
47 #include <private/qwidget_p.h>
48 #include <qpainter.h>
49 #include <qpaintengine.h>
50 #include <qwidget.h>
51 #include <qbackingstore.h>
52 #include <qapplication.h>
53 #include <qpixmapcache.h>
54 #include <private/qapplication_p.h>
55 #include <qpa/qplatformnativeinterface.h>
56 
57 #if QT_CONFIG(toolbutton)
58 #include <qtoolbutton.h>
59 #endif
60 #if QT_CONFIG(tabbar)
61 #include <qtabbar.h>
62 #endif
63 #if QT_CONFIG(combobox)
64 #include <qcombobox.h>
65 #endif
66 #if QT_CONFIG(scrollbar)
67 #include <qscrollbar.h>
68 #endif
69 #include <qheaderview.h>
70 #if QT_CONFIG(spinbox)
71 #include <qspinbox.h>
72 #endif
73 #if QT_CONFIG(listview)
74 #include <qlistview.h>
75 #endif
76 #if QT_CONFIG(stackedwidget)
77 #include <qstackedwidget.h>
78 #endif
79 #if QT_CONFIG(pushbutton)
80 #include <qpushbutton.h>
81 #endif
82 #if QT_CONFIG(toolbar)
83 #include <qtoolbar.h>
84 #endif
85 #include <qlabel.h>
86 #include <qvarlengtharray.h>
87 #include <qdebug.h>
88 
89 #include <algorithm>
90 
91 QT_BEGIN_NAMESPACE
92 
93 // General const values
94 static const int windowsItemFrame        =  2; // menu item frame width
95 static const int windowsItemHMargin      =  3; // menu item hor text margin
96 static const int windowsItemVMargin      =  0; // menu item ver text margin
97 static const int windowsArrowHMargin     =  6; // arrow horizontal margin
98 static const int windowsRightBorder      = 12; // right border on windows
99 
100 // External function calls
101 extern Q_WIDGETS_EXPORT HDC qt_win_display_dc();
102 extern QRegion qt_region_from_HRGN(HRGN rgn);
103 
104 // Theme names matching the QWindowsXPStylePrivate::Theme enumeration.
105 static const wchar_t *themeNames[QWindowsXPStylePrivate::NThemes] =
106 {
107     L"BUTTON",   L"COMBOBOX",   L"EDIT",    L"HEADER",    L"LISTVIEW",
108     L"MENU",     L"PROGRESS",   L"REBAR",   L"SCROLLBAR", L"SPIN",
109     L"TAB",      L"TASKDIALOG", L"TOOLBAR", L"TOOLTIP",   L"TRACKBAR",
110     L"TREEVIEW", L"WINDOW",     L"STATUS",  L"TREEVIEW"
111 };
112 
backingStoreForWidget(const QWidget * widget)113 static inline QBackingStore *backingStoreForWidget(const QWidget *widget)
114 {
115     if (QBackingStore *backingStore = widget->backingStore())
116         return backingStore;
117     if (const QWidget *topLevel = widget->nativeParentWidget())
118         if (QBackingStore *topLevelBackingStore = topLevel->backingStore())
119             return topLevelBackingStore;
120     return nullptr;
121 }
122 
hdcForWidgetBackingStore(const QWidget * widget)123 static inline HDC hdcForWidgetBackingStore(const QWidget *widget)
124 {
125     if (QBackingStore *backingStore = backingStoreForWidget(widget)) {
126         QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
127         if (nativeInterface)
128             return static_cast<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore));
129     }
130     return nullptr;
131 }
132 
133 // Theme data helper ------------------------------------------------------------------------------
134 /* \internal
135     Returns \c true if the themedata is valid for use.
136 */
isValid()137 bool XPThemeData::isValid()
138 {
139     return QWindowsXPStylePrivate::useXP() && theme >= 0 && handle();
140 }
141 
142 
143 /* \internal
144     Returns the theme engine handle to the specific class.
145     If the handle hasn't been opened before, it opens the data, and
146     adds it to a static map, for caching.
147 */
handle()148 HTHEME XPThemeData::handle()
149 {
150     if (!QWindowsXPStylePrivate::useXP())
151         return nullptr;
152 
153     if (!htheme)
154         htheme = QWindowsXPStylePrivate::createTheme(theme, QWindowsXPStylePrivate::winId(widget));
155     return htheme;
156 }
157 
158 /* \internal
159     Converts a QRect to the native RECT structure.
160 */
toRECT(const QRect & qr)161 RECT XPThemeData::toRECT(const QRect &qr)
162 {
163     RECT r;
164     r.left = qr.x();
165     r.right = qr.x() + qr.width();
166     r.top = qr.y();
167     r.bottom = qr.y() + qr.height();
168     return r;
169 }
170 
171 /* \internal
172     Returns the native region of a part, if the part is considered
173     transparent. The region is scaled to the parts size (rect).
174 */
mask(QWidget * widget)175 HRGN XPThemeData::mask(QWidget *widget)
176 {
177     if (!IsThemeBackgroundPartiallyTransparent(handle(), partId, stateId))
178         return nullptr;
179 
180     HRGN hrgn;
181     HDC dc = nullptr;
182     if (widget)
183         dc = hdcForWidgetBackingStore(widget);
184     RECT nativeRect = toRECT(rect);
185     GetThemeBackgroundRegion(handle(), dc, partId, stateId, &nativeRect, &hrgn);
186     return hrgn;
187 }
188 
189 // QWindowsXPStylePrivate -------------------------------------------------------------------------
190 // Static initializations
191 HWND QWindowsXPStylePrivate::m_vistaTreeViewHelper = nullptr;
192 HTHEME QWindowsXPStylePrivate::m_themes[NThemes];
193 bool QWindowsXPStylePrivate::use_xp = false;
194 QBasicAtomicInt QWindowsXPStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting
195 
qt_add_rect(HRGN & winRegion,QRect r)196 static void qt_add_rect(HRGN &winRegion, QRect r)
197 {
198     HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
199     if (rgn) {
200         HRGN dest = CreateRectRgn(0,0,0,0);
201         int result = CombineRgn(dest, winRegion, rgn, RGN_OR);
202         if (result) {
203             DeleteObject(winRegion);
204             winRegion = dest;
205         }
206         DeleteObject(rgn);
207     }
208 }
209 
qt_hrgn_from_qregion(const QRegion & region)210 static HRGN qt_hrgn_from_qregion(const QRegion &region)
211 {
212     HRGN hRegion = CreateRectRgn(0,0,0,0);
213     if (region.rectCount() == 1) {
214         qt_add_rect(hRegion, region.boundingRect());
215         return hRegion;
216     }
217     for (const QRect &rect : region)
218         qt_add_rect(hRegion, rect);
219     return hRegion;
220 }
221 
222 /* \internal
223     Checks if the theme engine can/should be used, or if we should
224     fall back to Windows style.
225 */
useXP(bool update)226 bool QWindowsXPStylePrivate::useXP(bool update)
227 {
228     if (update) {
229         use_xp = IsThemeActive() && (IsAppThemed() || !QCoreApplication::instance())
230                  && !QWindowsStylePrivate::isDarkMode();
231     }
232     return use_xp;
233 }
234 
235 /* \internal
236     Handles refcounting, and queries the theme engine for usage.
237 */
init(bool force)238 void QWindowsXPStylePrivate::init(bool force)
239 {
240     if (ref.ref() && !force)
241         return;
242     if (!force) // -1 based atomic refcounting
243         ref.ref();
244 
245     useXP(true);
246     std::fill(m_themes, m_themes + NThemes, nullptr);
247 }
248 
249 /* \internal
250     Cleans up all static data.
251 */
cleanup(bool force)252 void QWindowsXPStylePrivate::cleanup(bool force)
253 {
254     if(bufferBitmap) {
255         if (bufferDC && nullBitmap)
256             SelectObject(bufferDC, nullBitmap);
257         DeleteObject(bufferBitmap);
258         bufferBitmap = nullptr;
259     }
260 
261     if(bufferDC)
262         DeleteDC(bufferDC);
263     bufferDC = nullptr;
264 
265     if (ref.deref() && !force)
266         return;
267     if (!force)  // -1 based atomic refcounting
268         ref.deref();
269 
270     use_xp = false;
271     cleanupHandleMap();
272 }
273 
274 /* In order to obtain the correct VistaTreeViewTheme (arrows for PE_IndicatorBranch),
275  * we need to set the windows "explorer" theme explicitly on a native
276  * window and open the "TREEVIEW" theme handle passing its window handle
277  * in order to get Vista-style item view themes (particulary drawBackground()
278  * for selected items needs this).
279  * We invoke a service of the native Windows interface to create
280  * a non-visible window handle, open the theme on it and insert it into
281  * the cache so that it is found by XPThemeData::handle() first.
282  */
283 
createTreeViewHelperWindow()284 static inline HWND createTreeViewHelperWindow()
285 {
286     if (QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface()) {
287         void *hwnd = nullptr;
288         void *wndProc = reinterpret_cast<void *>(DefWindowProc);
289         if (QMetaObject::invokeMethod(ni, "createMessageWindow", Qt::DirectConnection,
290                                   Q_RETURN_ARG(void*, hwnd),
291                                   Q_ARG(QString, QStringLiteral("QTreeViewThemeHelperWindowClass")),
292                                   Q_ARG(QString, QStringLiteral("QTreeViewThemeHelperWindow")),
293                                   Q_ARG(void*, wndProc)) && hwnd) {
294             return reinterpret_cast<HWND>(hwnd);
295         }
296     }
297     return nullptr;
298 }
299 
initVistaTreeViewTheming()300 bool QWindowsXPStylePrivate::initVistaTreeViewTheming()
301 {
302     if (m_vistaTreeViewHelper)
303         return true;
304 
305     m_vistaTreeViewHelper = createTreeViewHelperWindow();
306     if (!m_vistaTreeViewHelper) {
307         qWarning("Unable to create the treeview helper window.");
308         return false;
309     }
310     if (FAILED(SetWindowTheme(m_vistaTreeViewHelper, L"explorer", nullptr))) {
311         qErrnoWarning("SetWindowTheme() failed.");
312         cleanupVistaTreeViewTheming();
313         return false;
314     }
315     return true;
316 }
317 
cleanupVistaTreeViewTheming()318 void QWindowsXPStylePrivate::cleanupVistaTreeViewTheming()
319 {
320     if (m_vistaTreeViewHelper) {
321         DestroyWindow(m_vistaTreeViewHelper);
322         m_vistaTreeViewHelper = nullptr;
323     }
324 }
325 
326 /* \internal
327     Closes all open theme data handles to ensure that we don't leak
328     resources, and that we don't refere to old handles when for
329     example the user changes the theme style.
330 */
cleanupHandleMap()331 void QWindowsXPStylePrivate::cleanupHandleMap()
332 {
333     for (auto &theme : m_themes) {
334         if (theme) {
335             CloseThemeData(theme);
336             theme = nullptr;
337         }
338     }
339     QWindowsXPStylePrivate::cleanupVistaTreeViewTheming();
340 }
341 
createTheme(int theme,HWND hwnd)342 HTHEME QWindowsXPStylePrivate::createTheme(int theme, HWND hwnd)
343 {
344     if (Q_UNLIKELY(theme < 0 || theme >= NThemes || !hwnd)) {
345         qWarning("Invalid parameters #%d, %p", theme, hwnd);
346         return nullptr;
347     }
348     if (!m_themes[theme]) {
349         const wchar_t *name = themeNames[theme];
350         if (theme == VistaTreeViewTheme && QWindowsXPStylePrivate::initVistaTreeViewTheming())
351             hwnd = QWindowsXPStylePrivate::m_vistaTreeViewHelper;
352         m_themes[theme] = OpenThemeData(hwnd, name);
353         if (Q_UNLIKELY(!m_themes[theme]))
354             qErrnoWarning("OpenThemeData() failed for theme %d (%s).",
355                           theme, qPrintable(themeName(theme)));
356     }
357     return m_themes[theme];
358 }
359 
themeName(int theme)360 QString QWindowsXPStylePrivate::themeName(int theme)
361 {
362     return theme >= 0 && theme < NThemes ?
363            QString::fromWCharArray(themeNames[theme]) :
364            QString();
365 }
366 
isItemViewDelegateLineEdit(const QWidget * widget)367 bool QWindowsXPStylePrivate::isItemViewDelegateLineEdit(const QWidget *widget)
368 {
369     if (!widget)
370         return false;
371     const QWidget *parent1 = widget->parentWidget();
372     // Exlude dialogs or other toplevels parented on item views.
373     if (!parent1 || parent1->isWindow())
374         return false;
375     const QWidget *parent2 = parent1->parentWidget();
376     return parent2 && widget->inherits("QLineEdit")
377         && parent2->inherits("QAbstractItemView");
378 }
379 
380 // Returns whether base color is set for this widget
isLineEditBaseColorSet(const QStyleOption * option,const QWidget * widget)381 bool QWindowsXPStylePrivate::isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget)
382 {
383     uint resolveMask = option->palette.resolve();
384     if (widget) {
385         // Since spin box includes a line edit we need to resolve the palette mask also from
386         // the parent, as while the color is always correct on the palette supplied by panel,
387         // the mask can still be empty. If either mask specifies custom base color, use that.
388 #if QT_CONFIG(spinbox)
389         if (const QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget()))
390             resolveMask |= spinbox->palette().resolve();
391 #endif // QT_CONFIG(spinbox)
392     }
393     return (resolveMask & (1 << QPalette::Base)) != 0;
394 }
395 
396 /*! \internal
397     This function will always return a valid window handle, and might
398     create a limbo widget to do so.
399     We often need a window handle to for example open theme data, so
400     this function ensures that we get one.
401 */
winId(const QWidget * widget)402 HWND QWindowsXPStylePrivate::winId(const QWidget *widget)
403 {
404     if (widget)
405         if (const HWND hwnd = QApplicationPrivate::getHWNDForWidget(const_cast<QWidget *>(widget)))
406             return hwnd;
407 
408     // Find top level with native window (there might be dialogs that do not have one).
409     const auto allWindows = QGuiApplication::allWindows();
410     for (const QWindow *window : allWindows) {
411         if (window->isTopLevel() && window->type() != Qt::Desktop && window->handle() != nullptr)
412             return reinterpret_cast<HWND>(window->winId());
413     }
414 
415     return GetDesktopWindow();
416 }
417 
418 /*! \internal
419     Returns a native buffer (DIB section) of at least the size of
420     ( \a x , \a y ). The buffer has a 32 bit depth, to not lose
421     the alpha values on proper alpha-pixmaps.
422 */
buffer(int w,int h)423 HBITMAP QWindowsXPStylePrivate::buffer(int w, int h)
424 {
425     // If we already have a HBITMAP which is of adequate size, just return that
426     if (bufferBitmap) {
427         if (bufferW >= w && bufferH >= h)
428             return bufferBitmap;
429         // Not big enough, discard the old one
430         if (bufferDC && nullBitmap)
431             SelectObject(bufferDC, nullBitmap);
432         DeleteObject(bufferBitmap);
433         bufferBitmap = nullptr;
434     }
435 
436     w = qMax(bufferW, w);
437     h = qMax(bufferH, h);
438 
439     if (!bufferDC) {
440         HDC displayDC = GetDC(nullptr);
441         bufferDC = CreateCompatibleDC(displayDC);
442         ReleaseDC(nullptr, displayDC);
443     }
444 
445     // Define the header
446     BITMAPINFO bmi;
447     memset(&bmi, 0, sizeof(bmi));
448     bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
449     bmi.bmiHeader.biWidth       = w;
450     bmi.bmiHeader.biHeight      = -h;
451     bmi.bmiHeader.biPlanes      = 1;
452     bmi.bmiHeader.biBitCount    = 32;
453     bmi.bmiHeader.biCompression = BI_RGB;
454 
455     // Create the pixmap
456     bufferPixels = nullptr;
457     bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&bufferPixels), nullptr, 0);
458     GdiFlush();
459     nullBitmap = static_cast<HBITMAP>(SelectObject(bufferDC, bufferBitmap));
460 
461     if (Q_UNLIKELY(!bufferBitmap)) {
462         qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() failed.", w, h);
463         bufferW = 0;
464         bufferH = 0;
465         return nullptr;
466     }
467     if (Q_UNLIKELY(!bufferPixels)) {
468         qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() did not allocate pixel data.", w, h);
469         bufferW = 0;
470         bufferH = 0;
471         return nullptr;
472     }
473     bufferW = w;
474     bufferH = h;
475 #ifdef DEBUG_XP_STYLE
476     qDebug("Creating new dib section (%d, %d)", w, h);
477 #endif
478     return bufferBitmap;
479 }
480 
481 /*! \internal
482     Returns \c true if the part contains any transparency at all. This does
483     not indicate what kind of transparency we're dealing with. It can be
484         - Alpha transparency
485         - Masked transparency
486 */
isTransparent(XPThemeData & themeData)487 bool QWindowsXPStylePrivate::isTransparent(XPThemeData &themeData)
488 {
489     return IsThemeBackgroundPartiallyTransparent(themeData.handle(), themeData.partId,
490                                                  themeData.stateId);
491 }
492 
493 
494 /*! \internal
495     Returns a QRegion of the region of the part
496 */
region(XPThemeData & themeData)497 QRegion QWindowsXPStylePrivate::region(XPThemeData &themeData)
498 {
499     HRGN hRgn = nullptr;
500     const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(themeData.widget);
501     RECT rect = themeData.toRECT(QRect(themeData.rect.topLeft() / factor, themeData.rect.size() / factor));
502     if (!SUCCEEDED(GetThemeBackgroundRegion(themeData.handle(), bufferHDC(), themeData.partId,
503                                             themeData.stateId, &rect, &hRgn))) {
504         return QRegion();
505     }
506 
507     HRGN dest = CreateRectRgn(0, 0, 0, 0);
508     const bool success = CombineRgn(dest, hRgn, nullptr, RGN_COPY) != ERROR;
509 
510     QRegion region;
511 
512     if (success) {
513         const auto numBytes = GetRegionData(dest, 0, nullptr);
514         if (numBytes == 0)
515             return QRegion();
516 
517         char *buf = new (std::nothrow) char[numBytes];
518         if (!buf)
519             return QRegion();
520 
521         RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf);
522         if (GetRegionData(dest, numBytes, rd) == 0) {
523             delete [] buf;
524             return QRegion();
525         }
526 
527         RECT *r = reinterpret_cast<RECT*>(rd->Buffer);
528         for (uint i = 0; i < rd->rdh.nCount; ++i) {
529             QRect rect;
530             rect.setCoords(int(r->left * factor), int(r->top * factor), int((r->right - 1) * factor), int((r->bottom - 1) * factor));
531             ++r;
532             region |= rect;
533         }
534 
535         delete [] buf;
536     }
537 
538     DeleteObject(hRgn);
539     DeleteObject(dest);
540 
541     return region;
542 }
543 
544 /*! \internal
545     Returns \c true if the native doublebuffer contains pixels with
546     varying alpha value.
547 */
hasAlphaChannel(const QRect & rect)548 bool QWindowsXPStylePrivate::hasAlphaChannel(const QRect &rect)
549 {
550     const int startX = rect.left();
551     const int startY = rect.top();
552     const int w = rect.width();
553     const int h = rect.height();
554 
555     int firstAlpha = -1;
556     for (int y = startY; y < h/2; ++y) {
557         auto buffer = reinterpret_cast<const DWORD *>(bufferPixels) + (y * bufferW);
558         for (int x = startX; x < w; ++x, ++buffer) {
559             int alpha = (*buffer) >> 24;
560             if (firstAlpha == -1)
561                 firstAlpha = alpha;
562             else if (alpha != firstAlpha)
563                 return true;
564         }
565     }
566     return false;
567 }
568 
569 /*! \internal
570     When the theme engine paints both a true alpha pixmap and a glyph
571     into our buffer, the glyph might not contain a proper alpha value.
572     The rule of thumb for premultiplied pixmaps is that the color
573     values of a pixel can never be higher than the alpha values, so
574     we use this to our advantage here, and fix all instances where
575     this occures.
576 */
fixAlphaChannel(const QRect & rect)577 bool QWindowsXPStylePrivate::fixAlphaChannel(const QRect &rect)
578 {
579     const int startX = rect.left();
580     const int startY = rect.top();
581     const int w = rect.width();
582     const int h = rect.height();
583     bool hasFixedAlphaValue = false;
584 
585     for (int y = startY; y < h; ++y) {
586         auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW);
587         for (int x = startX; x < w; ++x, ++buffer) {
588             uint pixel = *buffer;
589             int alpha = qAlpha(pixel);
590             if (qRed(pixel) > alpha || qGreen(pixel) > alpha || qBlue(pixel) > alpha) {
591                 *buffer |= 0xff000000;
592                 hasFixedAlphaValue = true;
593             }
594         }
595     }
596     return hasFixedAlphaValue;
597 }
598 
599 /*! \internal
600     Swaps the alpha values on certain pixels:
601         0xFF?????? -> 0x00??????
602         0x00?????? -> 0xFF??????
603     Used to determin the mask of a non-alpha transparent pixmap in
604     the native doublebuffer, and swap the alphas so we may paint
605     the image as a Premultiplied QImage with drawImage(), and obtain
606     the mask transparency.
607 */
swapAlphaChannel(const QRect & rect,bool allPixels)608 bool QWindowsXPStylePrivate::swapAlphaChannel(const QRect &rect, bool allPixels)
609 {
610     const int startX = rect.left();
611     const int startY = rect.top();
612     const int w = rect.width();
613     const int h = rect.height();
614     bool valueChange = false;
615 
616     // Flip the alphas, so that 255-alpha pixels are 0, and 0-alpha are 255.
617     for (int y = startY; y < h; ++y) {
618         auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW);
619         for (int x = startX; x < w; ++x, ++buffer) {
620             if (allPixels) {
621                 *buffer |= 0xFF000000;
622                 continue;
623             }
624             unsigned int alphaValue = (*buffer) & 0xFF000000;
625             if (alphaValue == 0xFF000000) {
626                 *buffer = 0;
627                 valueChange = true;
628             } else if (alphaValue == 0) {
629                 *buffer |= 0xFF000000;
630                 valueChange = true;
631             }
632         }
633     }
634     return valueChange;
635 }
636 
637 enum TransformType { SimpleTransform, HighDpiScalingTransform, ComplexTransform };
638 
transformType(const QTransform & transform,qreal devicePixelRatio)639 static inline TransformType transformType(const QTransform &transform, qreal devicePixelRatio)
640 {
641     if (transform.type() <= QTransform::TxTranslate)
642         return SimpleTransform;
643     if (transform.type() > QTransform::TxScale)
644         return ComplexTransform;
645     return qFuzzyCompare(transform.m11(), devicePixelRatio)
646         && qFuzzyCompare(transform.m22(), devicePixelRatio)
647         ? HighDpiScalingTransform : ComplexTransform;
648 }
649 
650 // QTBUG-60571: Exclude known fully opaque theme parts which produce values
651 // invalid in ARGB32_Premultiplied (for example, 0x00ffffff).
isFullyOpaque(const XPThemeData & themeData)652 static inline bool isFullyOpaque(const XPThemeData &themeData)
653 {
654     return themeData.theme == QWindowsXPStylePrivate::TaskDialogTheme && themeData.partId == TDLG_PRIMARYPANEL;
655 }
656 
657 /*! \internal
658     Main theme drawing function.
659     Determines the correct lowlevel drawing method depending on several
660     factors.
661         Use drawBackgroundThruNativeBuffer() if:
662             - Painter does not have an HDC
663             - Theme part is flipped (mirrored horizontally)
664         else use drawBackgroundDirectly().
665     \note drawBackgroundThruNativeBuffer() can return false for large
666     sizes due to buffer()/CreateDIBSection() failing.
667 */
drawBackground(XPThemeData & themeData,qreal correctionFactor)668 bool QWindowsXPStylePrivate::drawBackground(XPThemeData &themeData, qreal correctionFactor)
669 {
670     if (themeData.rect.isEmpty())
671         return true;
672 
673     QPainter *painter = themeData.painter;
674     Q_ASSERT_X(painter != nullptr, "QWindowsXPStylePrivate::drawBackground()", "Trying to draw a theme part without a painter");
675     if (!painter || !painter->isActive())
676         return false;
677 
678     painter->save();
679 
680     // Access paintDevice via engine since the painter may
681     // return the clip device which can still be a widget device in case of grabWidget().
682 
683     bool translucentToplevel = false;
684     const QPaintDevice *paintDevice = painter->device();
685     const qreal aditionalDevicePixelRatio = themeData.widget ? themeData.widget->devicePixelRatioF() : qreal(1);
686     if (paintDevice->devType() == QInternal::Widget) {
687         const QWidget *window = static_cast<const QWidget *>(paintDevice)->window();
688         translucentToplevel = window->testAttribute(Qt::WA_TranslucentBackground);
689     }
690 
691     const TransformType tt = transformType(painter->deviceTransform(), aditionalDevicePixelRatio);
692 
693     bool canDrawDirectly = false;
694     if (themeData.widget && painter->opacity() == 1.0 && !themeData.rotate
695         && !isFullyOpaque(themeData)
696         && tt != ComplexTransform && !themeData.mirrorVertically
697         && !translucentToplevel) {
698         // Draw on backing store DC only for real widgets or backing store images.
699         const QPaintDevice *enginePaintDevice = painter->paintEngine()->paintDevice();
700         switch (enginePaintDevice->devType()) {
701         case QInternal::Widget:
702             canDrawDirectly = true;
703             break;
704         case QInternal::Image:
705             // Ensure the backing store has received as resize and is initialized.
706             if (QBackingStore *bs = backingStoreForWidget(themeData.widget))
707                 if (bs->size().isValid() && bs->paintDevice() == enginePaintDevice)
708                     canDrawDirectly = true;
709         }
710     }
711 
712     const HDC dc = canDrawDirectly ? hdcForWidgetBackingStore(themeData.widget) : nullptr;
713     const bool result = dc && qFuzzyCompare(correctionFactor, qreal(1))
714         ? drawBackgroundDirectly(dc, themeData, aditionalDevicePixelRatio)
715         : drawBackgroundThruNativeBuffer(themeData, aditionalDevicePixelRatio, correctionFactor);
716     painter->restore();
717     return result;
718 }
719 
scaleRect(const QRectF & r,qreal factor)720 static inline QRectF scaleRect(const QRectF &r, qreal factor)
721 {
722     return r.isValid() && factor > 1
723         ? QRectF(r.topLeft() * factor, r.size() * factor)
724         : r;
725 }
726 
scaleRegion(const QRegion & region,qreal factor)727 static QRegion scaleRegion(const QRegion &region, qreal factor)
728 {
729     if (region.isEmpty() || qFuzzyCompare(factor, qreal(1)))
730         return region;
731     QRegion result;
732     for (const QRect &rect : region)
733         result += QRectF(QPointF(rect.topLeft()) * factor, QSizeF(rect.size() * factor)).toRect();
734     return result;
735 }
736 
737 /*! \internal
738     This function draws the theme parts directly to the paintengines HDC.
739     Do not use this if you need to perform other transformations on the
740     resulting data.
741 */
drawBackgroundDirectly(HDC dc,XPThemeData & themeData,qreal additionalDevicePixelRatio)742 bool QWindowsXPStylePrivate::drawBackgroundDirectly(HDC dc, XPThemeData &themeData, qreal additionalDevicePixelRatio)
743 {
744     QPainter *painter = themeData.painter;
745 
746     const auto &deviceTransform = painter->deviceTransform();
747     const QPointF redirectionDelta(deviceTransform.dx(), deviceTransform.dy());
748     const QRect area = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio).translated(redirectionDelta).toRect();
749 
750     QRegion sysRgn = painter->paintEngine()->systemClip();
751     if (sysRgn.isEmpty())
752         sysRgn = area;
753     else
754         sysRgn &= area;
755     if (painter->hasClipping())
756         sysRgn &= scaleRegion(painter->clipRegion(), additionalDevicePixelRatio).translated(redirectionDelta.toPoint());
757     HRGN hrgn = qt_hrgn_from_qregion(sysRgn);
758     SelectClipRgn(dc, hrgn);
759 
760 #ifdef DEBUG_XP_STYLE
761         printf("---[ DIRECT PAINTING ]------------------> Name(%-10s) Part(%d) State(%d)\n",
762                qPrintable(themeData.name), themeData.partId, themeData.stateId);
763         showProperties(themeData);
764 #endif
765 
766     RECT drawRECT = themeData.toRECT(area);
767     DTBGOPTS drawOptions;
768     memset(&drawOptions, 0, sizeof(drawOptions));
769     drawOptions.dwSize = sizeof(drawOptions);
770     drawOptions.rcClip = themeData.toRECT(sysRgn.boundingRect());
771     drawOptions.dwFlags = DTBG_CLIPRECT
772                           | (themeData.noBorder ? DTBG_OMITBORDER : 0)
773                           | (themeData.noContent ? DTBG_OMITCONTENT : 0)
774                           | (themeData.mirrorHorizontally ? DTBG_MIRRORDC : 0);
775 
776     const HRESULT result = DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &drawOptions);
777     SelectClipRgn(dc, nullptr);
778     DeleteObject(hrgn);
779     return SUCCEEDED(result);
780 }
781 
782 /*! \internal
783     This function uses a secondary Native doublebuffer for painting parts.
784     It should only be used when the painteengine doesn't provide a proper
785     HDC for direct painting (e.g. when doing a grabWidget(), painting to
786     other pixmaps etc), or when special transformations are needed (e.g.
787     flips (horizonal mirroring only, vertical are handled by the theme
788     engine).
789 
790     \a correctionFactor is an additional factor used to scale up controls
791     that are too small on High DPI screens, as has been observed for
792     WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON (QTBUG-75927).
793 */
drawBackgroundThruNativeBuffer(XPThemeData & themeData,qreal additionalDevicePixelRatio,qreal correctionFactor)794 bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeData,
795                                                             qreal additionalDevicePixelRatio,
796                                                             qreal correctionFactor)
797 {
798     QPainter *painter = themeData.painter;
799     QRectF rectF = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio);
800 
801     if ((themeData.rotate + 90) % 180 == 0) { // Catch 90,270,etc.. degree flips.
802         rectF = QRectF(0, 0, rectF.height(), rectF.width());
803     }
804     rectF.moveTo(0, 0);
805 
806     const bool hasCorrectionFactor = !qFuzzyCompare(correctionFactor, qreal(1));
807     QRect rect = rectF.toRect();
808     QRect drawRect = hasCorrectionFactor
809         ? QRectF(rectF.topLeft() / correctionFactor, rectF.size() / correctionFactor).toRect() : rect;
810     int partId = themeData.partId;
811     int stateId = themeData.stateId;
812     int w = rect.width();
813     int h = rect.height();
814 
815     // Values initialized later, either from cached values, or from function calls
816     AlphaChannelType alphaType = UnknownAlpha;
817     bool stateHasData = true; // We assume so;
818     bool hasAlpha = false;
819     bool partIsTransparent;
820     bool potentialInvalidAlpha;
821 
822     QString pixmapCacheKey = QStringLiteral("$qt_xp_");
823     pixmapCacheKey.append(themeName(themeData.theme));
824     pixmapCacheKey.append(QLatin1Char('p'));
825     pixmapCacheKey.append(QString::number(partId));
826     pixmapCacheKey.append(QLatin1Char('s'));
827     pixmapCacheKey.append(QString::number(stateId));
828     pixmapCacheKey.append(QLatin1Char('s'));
829     pixmapCacheKey.append(themeData.noBorder ? QLatin1Char('0') : QLatin1Char('1'));
830     pixmapCacheKey.append(QLatin1Char('b'));
831     pixmapCacheKey.append(themeData.noContent ? QLatin1Char('0') : QLatin1Char('1'));
832     pixmapCacheKey.append(QString::number(w));
833     pixmapCacheKey.append(QLatin1Char('w'));
834     pixmapCacheKey.append(QString::number(h));
835     pixmapCacheKey.append(QLatin1Char('h'));
836     pixmapCacheKey.append(QString::number(additionalDevicePixelRatio));
837     pixmapCacheKey.append(QLatin1Char('d'));
838     if (hasCorrectionFactor) {
839         pixmapCacheKey.append(QLatin1Char('c'));
840         pixmapCacheKey.append(QString::number(correctionFactor));
841     }
842 
843     QPixmap cachedPixmap;
844     ThemeMapKey key(themeData);
845     ThemeMapData data = alphaCache.value(key);
846 
847     bool haveCachedPixmap = false;
848     bool isCached = data.dataValid;
849     if (isCached) {
850         partIsTransparent = data.partIsTransparent;
851         hasAlpha = data.hasAlphaChannel;
852         alphaType = data.alphaType;
853         potentialInvalidAlpha = data.hadInvalidAlpha;
854 
855         haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, &cachedPixmap);
856 
857 #ifdef DEBUG_XP_STYLE
858         char buf[25];
859         ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h);
860         printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n",
861                haveCachedPixmap ? buf : "]-------------------",
862                qPrintable(themeData.name), themeData.partId, themeData.stateId);
863 #endif
864     } else {
865         // Not cached, so get values from Theme Engine
866         BOOL tmt_borderonly = false;
867         COLORREF tmt_transparentcolor = 0x0;
868         PROPERTYORIGIN proporigin = PO_NOTFOUND;
869         GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERONLY, &tmt_borderonly);
870         GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, TMT_TRANSPARENTCOLOR, &tmt_transparentcolor);
871         GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_CAPTIONMARGINS, &proporigin);
872 
873         partIsTransparent = isTransparent(themeData);
874 
875         potentialInvalidAlpha = false;
876         GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &proporigin);
877         if (proporigin == PO_PART || proporigin == PO_STATE) {
878             int tmt_glyphtype = GT_NONE;
879             GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &tmt_glyphtype);
880             potentialInvalidAlpha = partIsTransparent && tmt_glyphtype == GT_IMAGEGLYPH;
881         }
882 
883 #ifdef DEBUG_XP_STYLE
884         printf("---[ NOT CACHED ]-----------------------> Name(%-10s) Part(%d) State(%d)\n",
885                qPrintable(themeData.name), themeData.partId, themeData.stateId);
886         printf("-->partIsTransparen      = %d\n", partIsTransparent);
887         printf("-->potentialInvalidAlpha = %d\n", potentialInvalidAlpha);
888         showProperties(themeData);
889 #endif
890     }
891     bool wasAlphaSwapped = false;
892     bool wasAlphaFixed = false;
893 
894     // OLD PSDK Workaround ------------------------------------------------------------------------
895     // See if we need extra clipping for the older PSDK, which does
896     // not have a DrawThemeBackgroundEx function for DTGB_OMITBORDER
897     // and DTGB_OMITCONTENT
898     bool addBorderContentClipping = false;
899     QRegion extraClip;
900     QRect area = drawRect;
901     if (themeData.noBorder || themeData.noContent) {
902         extraClip = area;
903         // We are running on a system where the uxtheme.dll does not have
904         // the DrawThemeBackgroundEx function, so we need to clip away
905         // borders or contents manually.
906 
907         int borderSize = 0;
908         PROPERTYORIGIN origin = PO_NOTFOUND;
909         GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin);
910         GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize);
911         borderSize *= additionalDevicePixelRatio;
912 
913         // Clip away border region
914         if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) {
915             if (themeData.noBorder) {
916                 extraClip &= area;
917                 area = area.adjusted(-borderSize, -borderSize, borderSize, borderSize);
918             }
919 
920             // Clip away content region
921             if (themeData.noContent) {
922                 QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize);
923                 extraClip ^= content;
924             }
925         }
926         addBorderContentClipping = (themeData.noBorder | themeData.noContent);
927     }
928 
929     QImage img;
930     if (!haveCachedPixmap) { // If the pixmap is not cached, generate it! -------------------------
931         if (!buffer(drawRect.width(), drawRect.height())) // Ensure a buffer of at least (w, h) in size
932             return false;
933         HDC dc = bufferHDC();
934 
935         // Clear the buffer
936         if (alphaType != NoAlpha) {
937             // Consider have separate "memset" function for small chunks for more speedup
938             memset(bufferPixels, 0x00, bufferW * drawRect.height() * 4);
939         }
940 
941         // Difference between area and rect
942         int dx = area.x() - drawRect.x();
943         int dy = area.y() - drawRect.y();
944 
945         // Adjust so painting rect starts from Origo
946         rect.moveTo(0,0);
947         area.moveTo(dx,dy);
948         DTBGOPTS drawOptions;
949         drawOptions.dwSize = sizeof(drawOptions);
950         drawOptions.rcClip = themeData.toRECT(rect);
951         drawOptions.dwFlags = DTBG_CLIPRECT
952                             | (themeData.noBorder ? DTBG_OMITBORDER : 0)
953                             | (themeData.noContent ? DTBG_OMITCONTENT : 0);
954 
955         // Drawing the part into the backing store
956         RECT wRect(themeData.toRECT(area));
957         DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &wRect, &drawOptions);
958 
959         // If not cached, analyze the buffer data to figure
960         // out alpha type, and if it contains data
961         if (!isCached) {
962             // SHORTCUT: If the part's state has no data, cache it for NOOP later
963             if (!stateHasData) {
964                 memset(static_cast<void *>(&data), 0, sizeof(data));
965                 data.dataValid = true;
966                 alphaCache.insert(key, data);
967                 return true;
968             }
969             hasAlpha = hasAlphaChannel(rect);
970             if (!hasAlpha && partIsTransparent)
971                 potentialInvalidAlpha = true;
972 #if defined(DEBUG_XP_STYLE) && 1
973             dumpNativeDIB(drawRect.width(), drawRect.height());
974 #endif
975         }
976 
977         // Fix alpha values, if needed
978         if (potentialInvalidAlpha)
979             wasAlphaFixed = fixAlphaChannel(drawRect);
980 
981         QImage::Format format;
982         if ((partIsTransparent && !wasAlphaSwapped) || (!partIsTransparent && hasAlpha)) {
983             format = QImage::Format_ARGB32_Premultiplied;
984             alphaType = RealAlpha;
985         } else if (wasAlphaSwapped) {
986             format = QImage::Format_ARGB32_Premultiplied;
987             alphaType = MaskAlpha;
988         } else {
989             format = QImage::Format_RGB32;
990             // The image data we got from the theme engine does not have any transparency,
991             // thus the alpha channel is set to 0.
992             // However, Format_RGB32 requires the alpha part to be set to 0xff, thus
993             // we must flip it from 0x00 to 0xff
994             swapAlphaChannel(rect, true);
995             alphaType = NoAlpha;
996         }
997 #if defined(DEBUG_XP_STYLE) && 1
998         printf("Image format is: %s\n", alphaType == RealAlpha ? "Real Alpha" : alphaType == MaskAlpha ? "Masked Alpha" : "No Alpha");
999 #endif
1000         img = QImage(bufferPixels, bufferW, bufferH, format);
1001         if (hasCorrectionFactor)
1002             img = img.scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation);
1003         img.setDevicePixelRatio(additionalDevicePixelRatio);
1004     }
1005 
1006     // Blitting backing store
1007     bool useRegion = partIsTransparent && !hasAlpha && !wasAlphaSwapped;
1008 
1009     QRegion newRegion;
1010     QRegion oldRegion;
1011     if (useRegion) {
1012         newRegion = region(themeData);
1013         oldRegion = painter->clipRegion();
1014         painter->setClipRegion(newRegion);
1015 #if defined(DEBUG_XP_STYLE) && 0
1016         printf("Using region:\n");
1017         for (const QRect &r : newRegion)
1018             printf("    (%d, %d, %d, %d)\n", r.x(), r.y(), r.right(), r.bottom());
1019 #endif
1020     }
1021 
1022     if (addBorderContentClipping)
1023         painter->setClipRegion(scaleRegion(extraClip, 1.0 / additionalDevicePixelRatio), Qt::IntersectClip);
1024 
1025     if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) {
1026         if (!haveCachedPixmap)
1027             painter->drawImage(themeData.rect, img, rect);
1028         else
1029             painter->drawPixmap(themeData.rect, cachedPixmap);
1030     } else {
1031         // This is _slow_!
1032         // Make a copy containing only the necessary data, and mirror
1033         // on all wanted axes. Then draw the copy.
1034         // If cached, the normal pixmap is cached, instead of caching
1035         // all possible orientations for each part and state.
1036         QImage imgCopy;
1037         if (!haveCachedPixmap)
1038             imgCopy = img.copy(rect);
1039         else
1040             imgCopy = cachedPixmap.toImage();
1041 
1042         if (themeData.rotate) {
1043             QTransform rotMatrix;
1044             rotMatrix.rotate(themeData.rotate);
1045             imgCopy = imgCopy.transformed(rotMatrix);
1046         }
1047         if (themeData.mirrorHorizontally || themeData.mirrorVertically) {
1048             imgCopy = imgCopy.mirrored(themeData.mirrorHorizontally, themeData.mirrorVertically);
1049         }
1050         painter->drawImage(themeData.rect,
1051                            imgCopy);
1052     }
1053 
1054     if (useRegion || addBorderContentClipping) {
1055         if (oldRegion.isEmpty())
1056             painter->setClipping(false);
1057         else
1058             painter->setClipRegion(oldRegion);
1059     }
1060 
1061     // Cache the pixmap to avoid expensive swapAlphaChannel() calls
1062     if (!haveCachedPixmap && w && h) {
1063         QPixmap pix = QPixmap::fromImage(img).copy(rect);
1064         QPixmapCache::insert(pixmapCacheKey, pix);
1065 #ifdef DEBUG_XP_STYLE
1066         printf("+++Adding pixmap to cache, size(%d, %d), wasAlphaSwapped(%d), wasAlphaFixed(%d), name(%s)\n",
1067                w, h, wasAlphaSwapped, wasAlphaFixed, qPrintable(pixmapCacheKey));
1068 #endif
1069     }
1070 
1071     // Add to theme part cache
1072     if (!isCached) {
1073         memset(static_cast<void *>(&data), 0, sizeof(data));
1074         data.dataValid = true;
1075         data.partIsTransparent = partIsTransparent;
1076         data.alphaType = alphaType;
1077         data.hasAlphaChannel = hasAlpha;
1078         data.wasAlphaSwapped = wasAlphaSwapped;
1079         data.hadInvalidAlpha = wasAlphaFixed;
1080         alphaCache.insert(key, data);
1081     }
1082     return true;
1083 }
1084 
1085 
1086 // ------------------------------------------------------------------------------------------------
1087 
1088 /*!
1089     \class QWindowsXPStyle
1090     \brief The QWindowsXPStyle class provides a Microsoft Windows XP-like look and feel.
1091 
1092     \ingroup appearance
1093     \inmodule QtWidgets
1094     \internal
1095 
1096     \warning This style is only available on the Windows XP platform
1097     because it makes use of Windows XP's style engine.
1098 
1099     Most of the functions are documented in the base classes
1100     QWindowsStyle, QCommonStyle, and QStyle, but the
1101     QWindowsXPStyle overloads of drawComplexControl(), drawControl(),
1102     drawControlMask(), drawPrimitive(), proxy()->subControlRect(), and
1103     sizeFromContents(), are documented here.
1104 
1105     \image qwindowsxpstyle.png
1106     \sa QMacStyle, QWindowsStyle, QFusionStyle
1107 */
1108 
1109 /*!
1110     Constructs a QWindowsStyle
1111 */
QWindowsXPStyle()1112 QWindowsXPStyle::QWindowsXPStyle()
1113     : QWindowsStyle(*new QWindowsXPStylePrivate)
1114 {
1115 }
1116 
1117 /*!
1118     Destroys the style.
1119 */
1120 QWindowsXPStyle::~QWindowsXPStyle() = default;
1121 
1122 /*! \reimp */
unpolish(QApplication * app)1123 void QWindowsXPStyle::unpolish(QApplication *app)
1124 {
1125     QWindowsStyle::unpolish(app);
1126 }
1127 
1128 /*! \reimp */
polish(QApplication * app)1129 void QWindowsXPStyle::polish(QApplication *app)
1130 {
1131     QWindowsStyle::polish(app);
1132     if (!QWindowsXPStylePrivate::useXP())
1133         return;
1134 }
1135 
1136 /*! \reimp */
polish(QWidget * widget)1137 void QWindowsXPStyle::polish(QWidget *widget)
1138 {
1139     QWindowsStyle::polish(widget);
1140     if (!QWindowsXPStylePrivate::useXP())
1141         return;
1142 
1143     if (false
1144 #if QT_CONFIG(abstractbutton)
1145         || qobject_cast<QAbstractButton*>(widget)
1146 #endif
1147         || qobject_cast<QToolButton*>(widget)
1148         || qobject_cast<QTabBar*>(widget)
1149 #if QT_CONFIG(combobox)
1150         || qobject_cast<QComboBox*>(widget)
1151 #endif // QT_CONFIG(combobox)
1152         || qobject_cast<QScrollBar*>(widget)
1153         || qobject_cast<QSlider*>(widget)
1154         || qobject_cast<QHeaderView*>(widget)
1155 #if QT_CONFIG(spinbox)
1156         || qobject_cast<QAbstractSpinBox*>(widget)
1157         || qobject_cast<QSpinBox*>(widget)
1158 #endif // QT_CONFIG(spinbox)
1159         ) {
1160         widget->setAttribute(Qt::WA_Hover);
1161     }
1162 
1163 #if QT_CONFIG(rubberband)
1164     if (qobject_cast<QRubberBand*>(widget)) {
1165         widget->setWindowOpacity(0.6);
1166     }
1167 #endif
1168 
1169     Q_D(QWindowsXPStyle);
1170     if (!d->hasInitColors) {
1171         // Get text color for group box labels
1172         COLORREF cref;
1173         XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, 0, 0);
1174         GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref);
1175         d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
1176         GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref);
1177         d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref));
1178         // Where does this color come from?
1179         //GetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref);
1180         d->sliderTickColor = qRgb(165, 162, 148);
1181         d->hasInitColors = true;
1182     }
1183 }
1184 
1185 /*! \reimp */
polish(QPalette & pal)1186 void QWindowsXPStyle::polish(QPalette &pal)
1187 {
1188     QWindowsStyle::polish(pal);
1189     pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(110));
1190 }
1191 
1192 /*! \reimp */
unpolish(QWidget * widget)1193 void QWindowsXPStyle::unpolish(QWidget *widget)
1194 {
1195 #if QT_CONFIG(rubberband)
1196     if (qobject_cast<QRubberBand*>(widget)) {
1197         widget->setWindowOpacity(1.0);
1198     }
1199 #endif
1200     Q_D(QWindowsXPStyle);
1201     // Unpolish of widgets is the first thing that
1202     // happens when a theme changes, or the theme
1203     // engine is turned off. So we detect it here.
1204     bool oldState = QWindowsXPStylePrivate::useXP();
1205     bool newState = QWindowsXPStylePrivate::useXP(true);
1206     if ((oldState != newState) && newState) {
1207         d->cleanup(true);
1208         d->init(true);
1209     } else {
1210         // Cleanup handle map, if just changing style,
1211         // or turning it on. In both cases the values
1212         // already in the map might be old (other style).
1213         d->cleanupHandleMap();
1214     }
1215     if (false
1216 #if QT_CONFIG(abstractbutton)
1217         || qobject_cast<QAbstractButton*>(widget)
1218 #endif
1219         || qobject_cast<QToolButton*>(widget)
1220         || qobject_cast<QTabBar*>(widget)
1221 #if QT_CONFIG(combobox)
1222         || qobject_cast<QComboBox*>(widget)
1223 #endif // QT_CONFIG(combobox)
1224         || qobject_cast<QScrollBar*>(widget)
1225         || qobject_cast<QSlider*>(widget)
1226         || qobject_cast<QHeaderView*>(widget)
1227 #if QT_CONFIG(spinbox)
1228         || qobject_cast<QAbstractSpinBox*>(widget)
1229         || qobject_cast<QSpinBox*>(widget)
1230 #endif // QT_CONFIG(spinbox)
1231         ) {
1232         widget->setAttribute(Qt::WA_Hover, false);
1233     }
1234     QWindowsStyle::unpolish(widget);
1235 }
1236 
1237 /*! \reimp */
subElementRect(SubElement sr,const QStyleOption * option,const QWidget * widget) const1238 QRect QWindowsXPStyle::subElementRect(SubElement sr, const QStyleOption *option, const QWidget *widget) const
1239 {
1240     if (!QWindowsXPStylePrivate::useXP()) {
1241         return QWindowsStyle::subElementRect(sr, option, widget);
1242     }
1243 
1244     QRect rect(option->rect);
1245     switch(sr) {
1246     case SE_DockWidgetCloseButton:
1247     case SE_DockWidgetFloatButton:
1248         rect = QWindowsStyle::subElementRect(sr, option, widget);
1249         return rect.translated(0, 1);
1250     break;
1251     case SE_TabWidgetTabContents:
1252         if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))
1253         {
1254             rect = QWindowsStyle::subElementRect(sr, option, widget);
1255             if (sr == SE_TabWidgetTabContents) {
1256                 if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) {
1257                     if (tabWidget->documentMode())
1258                         break;
1259                 }
1260 
1261                 rect.adjust(0, 0, -2, -2);
1262             }
1263         }
1264         break;
1265     case SE_TabWidgetTabBar: {
1266         rect = QWindowsStyle::subElementRect(sr, option, widget);
1267         const QStyleOptionTabWidgetFrame *twfOption =
1268             qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option);
1269         if (twfOption && twfOption->direction == Qt::RightToLeft
1270             && (twfOption->shape == QTabBar::RoundedNorth
1271                 || twfOption->shape == QTabBar::RoundedSouth))
1272         {
1273             QStyleOptionTab otherOption;
1274             otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth
1275                                 ? QTabBar::RoundedEast : QTabBar::RoundedSouth);
1276             int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget);
1277             int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
1278             rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0);
1279         }
1280         break;}
1281 
1282     case SE_PushButtonContents:
1283         if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1284             MARGINS borderSize;
1285             if (widget) {
1286                 XPThemeData buttontheme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme);
1287                 HTHEME theme = buttontheme.handle();
1288                 if (theme) {
1289                     int stateId;
1290                     if (!(option->state & State_Enabled))
1291                         stateId = PBS_DISABLED;
1292                     else if (option->state & State_Sunken)
1293                         stateId = PBS_PRESSED;
1294                     else if (option->state & State_MouseOver)
1295                         stateId = PBS_HOT;
1296                     else if (btn->features & QStyleOptionButton::DefaultButton)
1297                         stateId = PBS_DEFAULTED;
1298                     else
1299                         stateId = PBS_NORMAL;
1300 
1301                     int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget);
1302                     rect = option->rect.adjusted(border, border, -border, -border);
1303 
1304                     if (SUCCEEDED(GetThemeMargins(theme, nullptr, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, nullptr, &borderSize))) {
1305                         rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight,
1306                                     -borderSize.cxRightWidth, -borderSize.cyBottomHeight);
1307                         rect = visualRect(option->direction, option->rect, rect);
1308                     }
1309                 }
1310             }
1311         }
1312         break;
1313     case SE_ProgressBarContents:
1314         rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget);
1315         if (option->state & QStyle::State_Horizontal)
1316             rect.adjust(4, 3, -4, -3);
1317         else
1318             rect.adjust(3, 2, -3, -2);
1319         break;
1320     default:
1321         rect = QWindowsStyle::subElementRect(sr, option, widget);
1322     }
1323     return rect;
1324 }
1325 
1326 /*!
1327     \reimp
1328 */
drawPrimitive(PrimitiveElement pe,const QStyleOption * option,QPainter * p,const QWidget * widget) const1329 void QWindowsXPStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p,
1330                                     const QWidget *widget) const
1331 {
1332     QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
1333 
1334     if (!QWindowsXPStylePrivate::useXP()) {
1335         QWindowsStyle::drawPrimitive(pe, option, p, widget);
1336         return;
1337     }
1338 
1339     int themeNumber = -1;
1340     int partId = 0;
1341     int stateId = 0;
1342     QRect rect = option->rect;
1343     State flags = option->state;
1344     bool hMirrored = false;
1345     bool vMirrored = false;
1346     bool noBorder = false;
1347     bool noContent = false;
1348     int  rotate = 0;
1349 
1350     switch (pe) {
1351     case PE_FrameTabBarBase:
1352         if (const QStyleOptionTabBarBase *tbb
1353                 = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
1354             p->save();
1355             switch (tbb->shape) {
1356             case QTabBar::RoundedNorth:
1357                 p->setPen(QPen(tbb->palette.dark(), 0));
1358                 p->drawLine(tbb->rect.topLeft(), tbb->rect.topRight());
1359                 break;
1360             case QTabBar::RoundedWest:
1361                 p->setPen(QPen(tbb->palette.dark(), 0));
1362                 p->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom());
1363                 break;
1364             case QTabBar::RoundedSouth:
1365                 p->setPen(QPen(tbb->palette.dark(), 0));
1366                 p->drawLine(tbb->rect.left(), tbb->rect.top(),
1367                             tbb->rect.right(), tbb->rect.top());
1368                 break;
1369             case QTabBar::RoundedEast:
1370                 p->setPen(QPen(tbb->palette.dark(), 0));
1371                 p->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft());
1372                 break;
1373             case QTabBar::TriangularNorth:
1374             case QTabBar::TriangularEast:
1375             case QTabBar::TriangularWest:
1376             case QTabBar::TriangularSouth:
1377                 p->restore();
1378                 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1379                 return;
1380             }
1381             p->restore();
1382         }
1383         return;
1384     case PE_PanelButtonBevel:
1385         themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1386         partId = BP_PUSHBUTTON;
1387         if (!(flags & State_Enabled))
1388             stateId = PBS_DISABLED;
1389         else if ((flags & State_Sunken) || (flags & State_On))
1390             stateId = PBS_PRESSED;
1391         else if (flags & State_MouseOver)
1392             stateId = PBS_HOT;
1393         //else if (flags & State_ButtonDefault)
1394         //    stateId = PBS_DEFAULTED;
1395         else
1396             stateId = PBS_NORMAL;
1397         break;
1398 
1399     case PE_PanelButtonTool:
1400         if (widget && widget->inherits("QDockWidgetTitleButton")) {
1401             if (const QWidget *dw = widget->parentWidget())
1402                 if (dw->isWindow())
1403                     return;
1404         }
1405         themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
1406         partId = TP_BUTTON;
1407         if (!(flags & State_Enabled))
1408             stateId = TS_DISABLED;
1409         else if (flags & State_Sunken)
1410             stateId = TS_PRESSED;
1411         else if (flags & State_MouseOver)
1412             stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
1413         else if (flags & State_On)
1414             stateId = TS_CHECKED;
1415         else if (!(flags & State_AutoRaise))
1416             stateId = TS_HOT;
1417         else
1418             stateId = TS_NORMAL;
1419         break;
1420 
1421     case PE_IndicatorButtonDropDown:
1422         themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
1423         partId = TP_SPLITBUTTONDROPDOWN;
1424         if (!(flags & State_Enabled))
1425             stateId = TS_DISABLED;
1426         else if (flags & State_Sunken)
1427             stateId = TS_PRESSED;
1428         else if (flags & State_MouseOver)
1429             stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
1430         else if (flags & State_On)
1431             stateId = TS_CHECKED;
1432         else if (!(flags & State_AutoRaise))
1433             stateId = TS_HOT;
1434         else
1435             stateId = TS_NORMAL;
1436         if (option->direction == Qt::RightToLeft)
1437             hMirrored = true;
1438         break;
1439 
1440     case PE_IndicatorCheckBox:
1441         themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1442         partId = BP_CHECKBOX;
1443         if (!(flags & State_Enabled))
1444             stateId = CBS_UNCHECKEDDISABLED;
1445         else if (flags & State_Sunken)
1446             stateId = CBS_UNCHECKEDPRESSED;
1447         else if (flags & State_MouseOver)
1448             stateId = CBS_UNCHECKEDHOT;
1449         else
1450             stateId = CBS_UNCHECKEDNORMAL;
1451 
1452         if (flags & State_On)
1453             stateId += CBS_CHECKEDNORMAL-1;
1454         else if (flags & State_NoChange)
1455             stateId += CBS_MIXEDNORMAL-1;
1456 
1457         break;
1458 
1459     case PE_IndicatorRadioButton:
1460         themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1461         partId = BP_RADIOBUTTON;
1462         if (!(flags & State_Enabled))
1463             stateId = RBS_UNCHECKEDDISABLED;
1464         else if (flags & State_Sunken)
1465             stateId = RBS_UNCHECKEDPRESSED;
1466         else if (flags & State_MouseOver)
1467             stateId = RBS_UNCHECKEDHOT;
1468         else
1469             stateId = RBS_UNCHECKEDNORMAL;
1470 
1471         if (flags & State_On)
1472             stateId += RBS_CHECKEDNORMAL-1;
1473         break;
1474 
1475     case PE_IndicatorDockWidgetResizeHandle:
1476         return;
1477 
1478 case PE_Frame:
1479     {
1480         if (flags & State_Raised)
1481             return;
1482         themeNumber = QWindowsXPStylePrivate::ListViewTheme;
1483         partId = LVP_LISTGROUP;
1484         XPThemeData theme(widget, nullptr, themeNumber, partId);
1485 
1486         if (!(flags & State_Enabled))
1487             stateId = ETS_DISABLED;
1488         else
1489             stateId = ETS_NORMAL;
1490         int fillType;
1491         if (GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) {
1492             if (fillType == BT_BORDERFILL) {
1493                 COLORREF bcRef;
1494                 GetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef);
1495                 QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef)));
1496                 QPen oldPen = p->pen();
1497                 // int borderSize = 1;
1498                 // GetThemeInt(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &borderSize);
1499 
1500                 // Inner white border
1501                 p->setPen(QPen(option->palette.base().color(), 0));
1502                 const qreal dpi = QStyleHelper::dpi(option);
1503                 const auto topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi);
1504                 const auto bottomRightAdjustment = QStyleHelper::dpiScaled(-1, dpi);
1505                 p->drawRect(QRectF(option->rect).adjusted(topLevelAdjustment, topLevelAdjustment,
1506                                                           bottomRightAdjustment, bottomRightAdjustment));
1507                 // Outer dark border
1508                 p->setPen(QPen(bordercolor, 0));
1509                 p->drawRect(QRectF(option->rect).adjusted(0, 0, -topLevelAdjustment, -topLevelAdjustment));
1510                 p->setPen(oldPen);
1511                 return;
1512             }
1513             if (fillType == BT_NONE)
1514                 return;
1515         }
1516         break;
1517     }
1518     case PE_FrameLineEdit: {
1519         // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class.
1520         if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) {
1521             QPen oldPen = p->pen();
1522             // Inner white border
1523             p->setPen(QPen(option->palette.base().color(), 1));
1524             p->drawRect(option->rect.adjusted(1, 1, -2, -2));
1525             // Outer dark border
1526             p->setPen(QPen(option->palette.shadow().color(), 1));
1527             p->drawRect(option->rect.adjusted(0, 0, -1, -1));
1528             p->setPen(oldPen);
1529             return;
1530         }
1531         if (qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1532             themeNumber = QWindowsXPStylePrivate::EditTheme;
1533             partId = EP_EDITTEXT;
1534             noContent = true;
1535             if (!(flags & State_Enabled))
1536                 stateId = ETS_DISABLED;
1537             else
1538                 stateId = ETS_NORMAL;
1539         }
1540         break;
1541     }
1542 
1543     case PE_PanelLineEdit:
1544         if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1545             themeNumber = QWindowsXPStylePrivate::EditTheme;
1546             partId = EP_EDITTEXT;
1547             noBorder = true;
1548             bool isEnabled = flags & State_Enabled;
1549 
1550             stateId = isEnabled ? ETS_NORMAL : ETS_DISABLED;
1551 
1552             if (QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget)) {
1553                 p->fillRect(panel->rect, panel->palette.brush(QPalette::Base));
1554             } else {
1555                 XPThemeData theme(nullptr, p, themeNumber, partId, stateId, rect);
1556                 if (!theme.isValid()) {
1557                     QWindowsStyle::drawPrimitive(pe, option, p, widget);
1558                     return;
1559                 }
1560                 int bgType;
1561                 GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType);
1562                 if( bgType == BT_IMAGEFILE ) {
1563                     theme.mirrorHorizontally = hMirrored;
1564                     theme.mirrorVertically = vMirrored;
1565                     theme.noBorder = noBorder;
1566                     theme.noContent = noContent;
1567                     theme.rotate = rotate;
1568                     d->drawBackground(theme);
1569                 } else {
1570                     QBrush fillColor = option->palette.brush(QPalette::Base);
1571 
1572                     if (!isEnabled) {
1573                         PROPERTYORIGIN origin = PO_NOTFOUND;
1574                         GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin);
1575                         // Use only if the fill property comes from our part
1576                         if ((origin == PO_PART || origin == PO_STATE)) {
1577                             COLORREF bgRef;
1578                             GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef);
1579                             fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef)));
1580                         }
1581                     }
1582                     p->fillRect(option->rect, fillColor);
1583                 }
1584             }
1585 
1586             if (panel->lineWidth > 0)
1587                 proxy()->drawPrimitive(PE_FrameLineEdit, panel, p, widget);
1588             return;
1589         }
1590         break;
1591 
1592     case PE_FrameTabWidget:
1593         if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))
1594         {
1595             themeNumber = QWindowsXPStylePrivate::TabTheme;
1596             partId = TABP_PANE;
1597 
1598             if (widget) {
1599                 bool useGradient = true;
1600                 const int maxlength = 256;
1601                 wchar_t themeFileName[maxlength];
1602                 wchar_t themeColor[maxlength];
1603                 // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it
1604                 if (GetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, nullptr, 0) == S_OK) {
1605                     wchar_t *offset = nullptr;
1606                     if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != nullptr) {
1607                         offset++;
1608                         if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic")) {
1609                             useGradient = false;
1610                         }
1611                     }
1612                 }
1613                 // This should work, but currently there's an error in the ::drawBackgroundDirectly()
1614                 // code, when using the HDC directly..
1615                 if (useGradient) {
1616                     QStyleOptionTabWidgetFrame frameOpt = *tab;
1617                     frameOpt.rect = widget->rect();
1618                     QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt, widget);
1619                     QRegion reg = option->rect;
1620                     reg -= contentsRect;
1621                     p->setClipRegion(reg);
1622                     XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
1623                     theme.mirrorHorizontally = hMirrored;
1624                     theme.mirrorVertically = vMirrored;
1625                     d->drawBackground(theme);
1626                     p->setClipRect(contentsRect);
1627                     partId = TABP_BODY;
1628                 }
1629             }
1630             switch (tab->shape) {
1631             case QTabBar::RoundedNorth:
1632             case QTabBar::TriangularNorth:
1633                 break;
1634             case QTabBar::RoundedSouth:
1635             case QTabBar::TriangularSouth:
1636                 vMirrored = true;
1637                 break;
1638             case QTabBar::RoundedEast:
1639             case QTabBar::TriangularEast:
1640                 rotate = 90;
1641                 break;
1642             case QTabBar::RoundedWest:
1643             case QTabBar::TriangularWest:
1644                 rotate = 90;
1645                 hMirrored = true;
1646                 break;
1647             default:
1648                 break;
1649             }
1650         }
1651         break;
1652 
1653     case PE_FrameMenu:
1654         p->save();
1655         p->setPen(option->palette.dark().color());
1656         p->drawRect(rect.adjusted(0, 0, -1, -1));
1657         p->restore();
1658         return;
1659 
1660     case PE_PanelMenuBar:
1661         break;
1662 
1663  case PE_FrameDockWidget:
1664      if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option))
1665         {
1666             themeNumber = QWindowsXPStylePrivate::WindowTheme;
1667             if (flags & State_Active)
1668                 stateId = FS_ACTIVE;
1669             else
1670                 stateId = FS_INACTIVE;
1671 
1672             int fwidth = proxy()->pixelMetric(PM_DockWidgetFrameWidth, frm, widget);
1673 
1674             XPThemeData theme(widget, p, themeNumber, 0, stateId);
1675             if (!theme.isValid())
1676                 break;
1677             theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth);           theme.partId = WP_SMALLFRAMELEFT;
1678             d->drawBackground(theme);
1679             theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth);
1680             theme.partId = WP_SMALLFRAMERIGHT;
1681             d->drawBackground(theme);
1682             theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth);
1683             theme.partId = WP_SMALLFRAMEBOTTOM;
1684             d->drawBackground(theme);
1685             return;
1686         }
1687         break;
1688 
1689     case PE_IndicatorHeaderArrow:
1690         {
1691 #if 0 // XP theme engine doesn't know about this :(
1692             name = QWindowsXPStylePrivate::HeaderTheme;
1693             partId = HP_HEADERSORTARROW;
1694             if (flags & State_Down)
1695                 stateId = HSAS_SORTEDDOWN;
1696             else
1697                 stateId = HSAS_SORTEDUP;
1698 #else
1699             if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
1700                 p->save();
1701                 p->setPen(option->palette.dark().color());
1702                 p->translate(0, option->rect.height()/2 - 4);
1703                 if (header->sortIndicator & QStyleOptionHeader::SortUp) { // invert logic to follow Windows style guide
1704                     p->drawLine(option->rect.x(), option->rect.y(), option->rect.x()+8, option->rect.y());
1705                     p->drawLine(option->rect.x()+1, option->rect.y()+1, option->rect.x()+7, option->rect.y()+1);
1706                     p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2);
1707                     p->drawLine(option->rect.x()+3, option->rect.y()+3, option->rect.x()+5, option->rect.y()+3);
1708                     p->drawPoint(option->rect.x()+4, option->rect.y()+4);
1709                 } else if(header->sortIndicator & QStyleOptionHeader::SortDown) {
1710                     p->drawLine(option->rect.x(), option->rect.y()+4, option->rect.x()+8, option->rect.y()+4);
1711                     p->drawLine(option->rect.x()+1, option->rect.y()+3, option->rect.x()+7, option->rect.y()+3);
1712                     p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2);
1713                     p->drawLine(option->rect.x()+3, option->rect.y()+1, option->rect.x()+5, option->rect.y()+1);
1714                     p->drawPoint(option->rect.x()+4, option->rect.y());
1715                 }
1716                 p->restore();
1717                 return;
1718             }
1719 #endif
1720         }
1721         break;
1722 
1723     case PE_FrameStatusBarItem:
1724         themeNumber = QWindowsXPStylePrivate::StatusTheme;
1725         partId = SP_PANE;
1726         break;
1727 
1728     case PE_FrameGroupBox:
1729         themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1730         partId = BP_GROUPBOX;
1731         if (!(flags & State_Enabled))
1732             stateId = GBS_DISABLED;
1733         else
1734             stateId = GBS_NORMAL;
1735         if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1736             if (frame->features & QStyleOptionFrame::Flat) {
1737                 // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style
1738                 QRect fr = frame->rect;
1739                 QPoint p1(fr.x(), fr.y() + 1);
1740                 QPoint p2(fr.x() + fr.width(), p1.y() + 1);
1741                 rect = QRect(p1, p2);
1742                 themeNumber = -1;
1743             }
1744         }
1745         break;
1746 
1747     case PE_IndicatorProgressChunk:
1748         {
1749         Qt::Orientation orient = Qt::Horizontal;
1750         bool inverted = false;
1751         if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1752             orient = pb->orientation;
1753             inverted = pb->invertedAppearance;
1754         }
1755         if (orient == Qt::Horizontal) {
1756             partId = PP_CHUNK;
1757             rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height() );
1758             if (inverted && option->direction == Qt::LeftToRight)
1759                 hMirrored = true;
1760         } else {
1761             partId = PP_CHUNKVERT;
1762             rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height());
1763         }
1764         themeNumber = QWindowsXPStylePrivate::ProgressTheme;
1765         stateId = 1;
1766         }
1767         break;
1768 
1769     case PE_FrameWindow:
1770         if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option))
1771         {
1772             themeNumber = QWindowsXPStylePrivate::WindowTheme;
1773             if (flags & State_Active)
1774                 stateId = FS_ACTIVE;
1775             else
1776                 stateId = FS_INACTIVE;
1777 
1778             int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget));
1779 
1780             XPThemeData theme(widget, p, themeNumber, 0, stateId);
1781             if (!theme.isValid())
1782                 break;
1783 
1784             // May fail due to too-large buffers for large widgets, fall back to Windows style.
1785             theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth);
1786             theme.partId = WP_FRAMELEFT;
1787             if (!d->drawBackground(theme)) {
1788                 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1789                 return;
1790             }
1791             theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth);
1792             theme.partId = WP_FRAMERIGHT;
1793             if (!d->drawBackground(theme)) {
1794                 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1795                 return;
1796             }
1797             theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth);
1798             theme.partId = WP_FRAMEBOTTOM;
1799             if (!d->drawBackground(theme)) {
1800                 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1801                 return;
1802             }
1803             theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth);
1804             theme.partId = WP_CAPTION;
1805             if (!d->drawBackground(theme))
1806                 QWindowsStyle::drawPrimitive(pe, option, p, widget);
1807             return;
1808         }
1809         break;
1810 
1811     case PE_IndicatorBranch:
1812         {
1813             static const int decoration_size = 9;
1814             int mid_h = option->rect.x() + option->rect.width() / 2;
1815             int mid_v = option->rect.y() + option->rect.height() / 2;
1816             int bef_h = mid_h;
1817             int bef_v = mid_v;
1818             int aft_h = mid_h;
1819             int aft_v = mid_v;
1820             QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern);
1821             if (option->state & State_Item) {
1822                 if (option->direction == Qt::RightToLeft)
1823                     p->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush);
1824                 else
1825                     p->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush);
1826             }
1827             if (option->state & State_Sibling)
1828                 p->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush);
1829             if (option->state & (State_Open | State_Children | State_Item | State_Sibling))
1830                 p->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush);
1831             if (option->state & State_Children) {
1832                 int delta = decoration_size / 2;
1833                 bef_h -= delta;
1834                 bef_v -= delta;
1835                 aft_h += delta;
1836                 aft_v += delta;
1837                 XPThemeData theme(nullptr, p, QWindowsXPStylePrivate::XpTreeViewTheme);
1838                 theme.rect = QRect(bef_h, bef_v, decoration_size, decoration_size);
1839                 theme.partId = TVP_GLYPH;
1840                 theme.stateId = flags & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED;
1841                 d->drawBackground(theme);
1842             }
1843         }
1844         return;
1845 
1846     case PE_IndicatorToolBarSeparator:
1847         if (option->rect.height() < 3) {
1848             // XP style requires a few pixels for the separator
1849             // to be visible.
1850             QWindowsStyle::drawPrimitive(pe, option, p, widget);
1851             return;
1852         }
1853         themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
1854         partId = TP_SEPARATOR;
1855 
1856         if (option->state & State_Horizontal)
1857             partId = TP_SEPARATOR;
1858         else
1859             partId = TP_SEPARATORVERT;
1860 
1861         break;
1862 
1863     case PE_IndicatorToolBarHandle:
1864 
1865         themeNumber = QWindowsXPStylePrivate::RebarTheme;
1866         partId = RP_GRIPPER;
1867         if (option->state & State_Horizontal) {
1868             partId = RP_GRIPPER;
1869             rect.adjust(0, 0, -2, 0);
1870         }
1871         else {
1872             partId = RP_GRIPPERVERT;
1873             rect.adjust(0, 0, 0, -2);
1874         }
1875         break;
1876 
1877     case PE_IndicatorItemViewItemCheck: {
1878         QStyleOptionButton button;
1879         button.QStyleOption::operator=(*option);
1880         button.state &= ~State_MouseOver;
1881         proxy()->drawPrimitive(PE_IndicatorCheckBox, &button, p, widget);
1882         return;
1883     }
1884 
1885     default:
1886         break;
1887     }
1888 
1889     XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
1890     if (!theme.isValid()) {
1891         QWindowsStyle::drawPrimitive(pe, option, p, widget);
1892         return;
1893     }
1894     theme.mirrorHorizontally = hMirrored;
1895     theme.mirrorVertically = vMirrored;
1896     theme.noBorder = noBorder;
1897     theme.noContent = noContent;
1898     theme.rotate = rotate;
1899     d->drawBackground(theme);
1900 }
1901 
1902 /*!
1903     \reimp
1904 */
drawControl(ControlElement element,const QStyleOption * option,QPainter * p,const QWidget * widget) const1905 void QWindowsXPStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *p,
1906                                   const QWidget *widget) const
1907 {
1908   QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
1909     if (!QWindowsXPStylePrivate::useXP()) {
1910         QWindowsStyle::drawControl(element, option, p, widget);
1911         return;
1912     }
1913 
1914     QRect rect(option->rect);
1915     State flags = option->state;
1916 
1917     int rotate = 0;
1918     bool hMirrored = false;
1919     bool vMirrored = false;
1920 
1921     int themeNumber = -1;
1922     int partId = 0;
1923     int stateId = 0;
1924     switch (element) {
1925     case CE_SizeGrip:
1926         {
1927             themeNumber = QWindowsXPStylePrivate::StatusTheme;
1928             partId = SP_GRIPPER;
1929             XPThemeData theme(nullptr, p, themeNumber, partId);
1930             QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
1931             size.rheight()--;
1932             if (const QStyleOptionSizeGrip *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(option)) {
1933                 switch (sg->corner) {
1934                     case Qt::BottomRightCorner:
1935                         rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size);
1936                         break;
1937                     case Qt::BottomLeftCorner:
1938                         rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size);
1939                         hMirrored = true;
1940                         break;
1941                     case Qt::TopRightCorner:
1942                         rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size);
1943                         vMirrored = true;
1944                         break;
1945                     case Qt::TopLeftCorner:
1946                         rect = QRect(rect.topLeft() + QPoint(1, 1), size);
1947                         hMirrored = vMirrored = true;
1948                 }
1949             }
1950         }
1951         break;
1952 
1953     case CE_HeaderSection:
1954         themeNumber = QWindowsXPStylePrivate::HeaderTheme;
1955         partId = HP_HEADERITEM;
1956         if (flags & State_Sunken)
1957             stateId = HIS_PRESSED;
1958         else if (flags & State_MouseOver)
1959             stateId = HIS_HOT;
1960         else
1961             stateId = HIS_NORMAL;
1962         break;
1963 
1964     case CE_Splitter:
1965         p->eraseRect(option->rect);
1966         return;
1967 
1968     case CE_PushButtonBevel:
1969         if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option))
1970         {
1971             themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1972             partId = BP_PUSHBUTTON;
1973             bool justFlat = ((btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken)))
1974                 || ((btn->features & QStyleOptionButton::CommandLinkButton)
1975                     && !(flags & State_MouseOver)
1976                     && !(btn->features & QStyleOptionButton::DefaultButton));
1977             if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
1978                 stateId = PBS_DISABLED;
1979             else if (justFlat)
1980                 ;
1981             else if (flags & (State_Sunken | State_On))
1982                 stateId = PBS_PRESSED;
1983             else if (flags & State_MouseOver)
1984                 stateId = PBS_HOT;
1985             else if (btn->features & QStyleOptionButton::DefaultButton)
1986                 stateId = PBS_DEFAULTED;
1987             else
1988                 stateId = PBS_NORMAL;
1989 
1990             if (!justFlat) {
1991                 XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
1992                 d->drawBackground(theme);
1993             }
1994 
1995             if (btn->features & QStyleOptionButton::HasMenu) {
1996                 int mbiw = 0, mbih = 0;
1997                 XPThemeData theme(widget, nullptr,
1998                                   QWindowsXPStylePrivate::ToolBarTheme,
1999                                   TP_SPLITBUTTONDROPDOWN);
2000                 if (theme.isValid()) {
2001                     const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
2002                     mbiw = size.width();
2003                     mbih = size.height();
2004                 }
2005 
2006                 QRect ir = btn->rect;
2007                 QStyleOptionButton newBtn = *btn;
2008                 newBtn.rect = QRect(ir.right() - mbiw - 1, 1 + (ir.height()/2) - (mbih/2), mbiw, mbih);
2009                 proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
2010             }
2011             return;
2012         }
2013         break;
2014     case CE_TabBarTab:
2015         if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
2016         {
2017             stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED;
2018         }
2019     break;
2020 
2021     case CE_TabBarTabShape:
2022         if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
2023         {
2024             themeNumber = QWindowsXPStylePrivate::TabTheme;
2025             bool isDisabled = !(tab->state & State_Enabled);
2026             bool hasFocus = tab->state & State_HasFocus;
2027             bool isHot = tab->state & State_MouseOver;
2028             bool selected = tab->state & State_Selected;
2029             bool lastTab = tab->position == QStyleOptionTab::End;
2030             bool firstTab = tab->position == QStyleOptionTab::Beginning;
2031             bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
2032             bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft;
2033             bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter;
2034             int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget);
2035             int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option, widget);
2036 
2037             if (isDisabled)
2038                 stateId = TIS_DISABLED;
2039             else if (selected)
2040                 stateId = TIS_SELECTED;
2041             else if (hasFocus)
2042                 stateId = TIS_FOCUSED;
2043             else if (isHot)
2044                 stateId = TIS_HOT;
2045             else
2046                 stateId = TIS_NORMAL;
2047 
2048             // Selecting proper part depending on position
2049             if (firstTab || onlyOne) {
2050                 if (leftAligned) {
2051                     partId = TABP_TABITEMLEFTEDGE;
2052                 } else if (centerAligned) {
2053                     partId = TABP_TABITEM;
2054                 } else { // rightAligned
2055                     partId = TABP_TABITEMRIGHTEDGE;
2056                 }
2057             } else {
2058                 partId = TABP_TABITEM;
2059             }
2060 
2061             if (tab->direction == Qt::RightToLeft
2062                 && (tab->shape == QTabBar::RoundedNorth
2063                     || tab->shape == QTabBar::RoundedSouth)) {
2064                 bool temp = firstTab;
2065                 firstTab = lastTab;
2066                 lastTab = temp;
2067             }
2068             bool begin = firstTab || onlyOne;
2069             bool end = lastTab || onlyOne;
2070             switch (tab->shape) {
2071             case QTabBar::RoundedNorth:
2072                 if (selected)
2073                     rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness);
2074                 else
2075                     rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0);
2076                 break;
2077             case QTabBar::RoundedSouth:
2078                 //vMirrored = true;
2079                 rotate = 180; // Not 100% correct, but works
2080                 if (selected)
2081                     rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0);
2082                 else
2083                     rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap);
2084                 break;
2085             case QTabBar::RoundedEast:
2086                 rotate = 90;
2087                 if (selected) {
2088                     rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap);
2089                 }else{
2090                     rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0);
2091                 }
2092                 break;
2093             case QTabBar::RoundedWest:
2094                 hMirrored = true;
2095                 rotate = 90;
2096                 if (selected) {
2097                     rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap);
2098                 }else{
2099                     rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0);
2100                 }
2101                 break;
2102             default:
2103                 themeNumber = -1; // Do our own painting for triangular
2104                 break;
2105             }
2106 
2107             if (!selected) {
2108                 switch (tab->shape) {
2109                 case QTabBar::RoundedNorth:
2110                     rect.adjust(0,0, 0,-1);
2111                     break;
2112                 case QTabBar::RoundedSouth:
2113                     rect.adjust(0,1, 0,0);
2114                     break;
2115                 case QTabBar::RoundedEast:
2116                     rect.adjust( 1,0, 0,0);
2117                     break;
2118                 case QTabBar::RoundedWest:
2119                     rect.adjust(0,0, -1,0);
2120                     break;
2121                 default:
2122                     break;
2123                 }
2124             }
2125         }
2126         break;
2127 
2128     case CE_ProgressBarGroove:
2129         {
2130         Qt::Orientation orient = Qt::Horizontal;
2131         if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
2132             orient = pb->orientation;
2133         partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT;
2134         themeNumber = QWindowsXPStylePrivate::ProgressTheme;
2135         stateId = 1;
2136         }
2137         break;
2138 
2139     case CE_MenuEmptyArea:
2140     case CE_MenuItem:
2141         if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
2142         {
2143             int tab = menuitem->tabWidth;
2144             bool dis = !(menuitem->state & State_Enabled);
2145             bool act = menuitem->state & State_Selected;
2146             bool checkable = menuitem->menuHasCheckableItems;
2147             bool checked = checkable ? menuitem->checked : false;
2148 
2149             // windows always has a check column, regardless whether we have an icon or not
2150             int checkcol = qMax(menuitem->maxIconWidth, 12);
2151 
2152             int x, y, w, h;
2153             rect.getRect(&x, &y, &w, &h);
2154 
2155             QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button);
2156             p->fillRect(rect, fill);
2157 
2158             if (element == CE_MenuEmptyArea)
2159                 break;
2160 
2161             // draw separator -------------------------------------------------
2162             if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
2163                 int yoff = y-1 + h / 2;
2164                 p->setPen(menuitem->palette.dark().color());
2165                 p->drawLine(x, yoff, x+w, yoff);
2166                 ++yoff;
2167                 p->setPen(menuitem->palette.light().color());
2168                 p->drawLine(x, yoff, x+w, yoff);
2169                 return;
2170             }
2171 
2172             int xpos = x;
2173 
2174             // draw icon ------------------------------------------------------
2175             if (!menuitem->icon.isNull()) {
2176                 QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
2177                 if (act && !dis)
2178                     mode = QIcon::Active;
2179                 QPixmap pixmap = checked ?
2180                                  menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On) :
2181                                  menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode);
2182                 const int pixw = pixmap.width() / pixmap.devicePixelRatio();
2183                 const int pixh = pixmap.height() / pixmap.devicePixelRatio();
2184                 QRect iconRect(0, 0, pixw, pixh);
2185                 iconRect.moveCenter(QRect(xpos, y, checkcol, h).center());
2186                 QRect vIconRect = visualRect(option->direction, option->rect, iconRect);
2187                 p->setPen(menuitem->palette.text().color());
2188                 p->setBrush(Qt::NoBrush);
2189                 if (checked)
2190                     p->drawRect(vIconRect.adjusted(-1, -1, 0, 0));
2191                 p->drawPixmap(vIconRect.topLeft(), pixmap);
2192 
2193             // draw checkmark -------------------------------------------------
2194             } else if (checked) {
2195                 QStyleOptionMenuItem newMi = *menuitem;
2196                 newMi.state = State_None;
2197                 if (!dis)
2198                     newMi.state |= State_Enabled;
2199                 if (act)
2200                     newMi.state |= State_On;
2201 
2202                 QRect checkMarkRect = QRect(menuitem->rect.x() + windowsItemFrame,
2203                                             menuitem->rect.y() + windowsItemFrame,
2204                                             checkcol - 2 * windowsItemFrame,
2205                                             menuitem->rect.height() - 2*windowsItemFrame);
2206                 newMi.rect = visualRect(option->direction, option->rect, checkMarkRect);
2207                 proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, widget);
2208             }
2209 
2210             QColor textColor = dis ? menuitem->palette.text().color() :
2211                                act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color();
2212             p->setPen(textColor);
2213 
2214             // draw text ------------------------------------------------------
2215             int xm = windowsItemFrame + checkcol + windowsItemHMargin;
2216             xpos = menuitem->rect.x() + xm;
2217             QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
2218             QRect vTextRect = visualRect(option->direction, option->rect, textRect);
2219             QString s = menuitem->text;
2220             if (!s.isEmpty()) {
2221                 p->save();
2222                 int t = s.indexOf(QLatin1Char('\t'));
2223                 int text_flags = Qt::AlignVCenter|Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine | Qt::AlignLeft;
2224                 if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget))
2225                     text_flags |= Qt::TextHideMnemonic;
2226                 // draw tab text ----------------
2227                 if (t >= 0) {
2228                     QRect vShortcutRect = visualRect(option->direction, option->rect, QRect(textRect.topRight(), menuitem->rect.bottomRight()));
2229                     if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) {
2230                         p->setPen(menuitem->palette.light().color());
2231                         p->drawText(vShortcutRect.adjusted(1,1,1,1), text_flags, s.mid(t + 1));
2232                         p->setPen(textColor);
2233                     }
2234                     p->drawText(vShortcutRect, text_flags, s.mid(t + 1));
2235                     s = s.left(t);
2236                 }
2237                 QFont font = menuitem->font;
2238                 if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
2239                     font.setBold(true);
2240                 p->setFont(font);
2241                 if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) {
2242                     p->setPen(menuitem->palette.light().color());
2243                     p->drawText(vTextRect.adjusted(1,1,1,1), text_flags, s.left(t));
2244                     p->setPen(textColor);
2245                 }
2246                 p->drawText(vTextRect, text_flags, s);
2247                 p->restore();
2248             }
2249 
2250             // draw sub menu arrow --------------------------------------------
2251             if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {
2252                 int dim = (h - 2) / 2;
2253                 PrimitiveElement arrow;
2254                 arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
2255                 xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
2256                 QRect vSubMenuRect = visualRect(option->direction, option->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
2257                 QStyleOptionMenuItem newMI = *menuitem;
2258                 newMI.rect = vSubMenuRect;
2259                 newMI.state = dis ? State_None : State_Enabled;
2260                 if (act)
2261                     newMI.palette.setColor(QPalette::ButtonText, newMI.palette.highlightedText().color());
2262                 proxy()->drawPrimitive(arrow, &newMI, p, widget);
2263             }
2264         }
2265         return;
2266 
2267     case CE_MenuBarItem:
2268         if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
2269         {
2270             if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem)
2271                 break;
2272 
2273             bool act = mbi->state & State_Selected;
2274             bool dis = !(mbi->state & State_Enabled);
2275 
2276             QBrush fill = mbi->palette.brush(act ? QPalette::Highlight : QPalette::Button);
2277             QPalette::ColorRole textRole = dis ? QPalette::Text:
2278                                            act ? QPalette::HighlightedText : QPalette::ButtonText;
2279             QPixmap pix = mbi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal);
2280 
2281             uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
2282             if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget))
2283                 alignment |= Qt::TextHideMnemonic;
2284 
2285             p->fillRect(rect, fill);
2286             if (!pix.isNull())
2287                 drawItemPixmap(p, mbi->rect, alignment, pix);
2288             else
2289                 drawItemText(p, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
2290         }
2291         return;
2292 #if QT_CONFIG(dockwidget)
2293     case CE_DockWidgetTitle:
2294         if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option))
2295         {
2296             int buttonMargin = 4;
2297             int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget);
2298             int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget);
2299             bool isFloating = widget && widget->isWindow();
2300             bool isActive = dwOpt->state & State_Active;
2301 
2302             const bool verticalTitleBar = dwOpt->verticalTitleBar;
2303 
2304             if (verticalTitleBar) {
2305                 rect = rect.transposed();
2306 
2307                 p->translate(rect.left() - 1, rect.top() + rect.width());
2308                 p->rotate(-90);
2309                 p->translate(-rect.left() + 1, -rect.top());
2310             }
2311             QRect r = rect.adjusted(0, 2, -1, -3);
2312             QRect titleRect = r;
2313 
2314             if (dwOpt->closable) {
2315                 QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10));
2316                 titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
2317             }
2318 
2319             if (dwOpt->floatable) {
2320                 QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10));
2321                 titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
2322             }
2323 
2324             if (isFloating) {
2325                 titleRect.adjust(0, -fw, 0, 0);
2326                 if (widget != nullptr && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey())
2327                     titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
2328             } else {
2329                 titleRect.adjust(mw, 0, 0, 0);
2330                 if (!dwOpt->floatable && !dwOpt->closable)
2331                     titleRect.adjust(0, 0, -mw, 0);
2332             }
2333 
2334             if (!verticalTitleBar)
2335                 titleRect = visualRect(dwOpt->direction, r, titleRect);
2336 
2337             if (!isFloating) {
2338                 QPen oldPen = p->pen();
2339                 QString titleText = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
2340                 p->setPen(dwOpt->palette.color(QPalette::Dark));
2341                 p->drawRect(r);
2342 
2343                 if (!titleText.isEmpty()) {
2344                     drawItemText(p, titleRect,
2345                                 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette,
2346                                 dwOpt->state & State_Enabled, titleText,
2347                                 QPalette::WindowText);
2348                 }
2349 
2350                 p->setPen(oldPen);
2351             } else {
2352                 themeNumber = QWindowsXPStylePrivate::WindowTheme;
2353                 if (isActive)
2354                     stateId = CS_ACTIVE;
2355                 else
2356                     stateId = CS_INACTIVE;
2357 
2358                 int titleHeight = rect.height() - 2;
2359                 rect = rect.adjusted(-fw, -fw, fw, 0);
2360 
2361                 XPThemeData theme(widget, p, themeNumber, 0, stateId);
2362                 if (!theme.isValid())
2363                     break;
2364 
2365                 // Draw small type title bar
2366                 theme.rect = rect;
2367                 theme.partId = WP_SMALLCAPTION;
2368                 d->drawBackground(theme);
2369 
2370                 // Figure out maximal button space on title bar
2371 
2372                 QIcon ico = widget->windowIcon();
2373                 bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey());
2374                 if (hasIcon) {
2375                     QPixmap pxIco = ico.pixmap(titleHeight);
2376                     if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft)
2377                         p->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco);
2378                     else
2379                         p->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco);
2380                 }
2381                 if (!dwOpt->title.isEmpty()) {
2382                     QPen oldPen = p->pen();
2383                     QFont oldFont = p->font();
2384                     QFont titleFont = oldFont;
2385                     titleFont.setBold(true);
2386                     p->setFont(titleFont);
2387                     QString titleText
2388                         = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
2389 
2390                     int result = TST_NONE;
2391                     GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
2392                     if (result != TST_NONE) {
2393                         COLORREF textShadowRef;
2394                         GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
2395                         QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
2396                         p->setPen(textShadow);
2397                         drawItemText(p, titleRect.adjusted(1, 1, 1, 1),
2398                                     Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
2399                                     dwOpt->state & State_Enabled, titleText);
2400                     }
2401 
2402                     COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
2403                     QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
2404                     p->setPen(textColor);
2405                     drawItemText(p, titleRect,
2406                                 Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
2407                                 dwOpt->state & State_Enabled, titleText);
2408                     p->setFont(oldFont);
2409                     p->setPen(oldPen);
2410                 }
2411 
2412             }
2413 
2414             return;
2415         }
2416         break;
2417 #endif // QT_CONFIG(dockwidget)
2418 #if QT_CONFIG(rubberband)
2419     case CE_RubberBand:
2420         if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) {
2421             QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight);
2422             p->save();
2423             p->setPen(highlight.darker(120));
2424             QColor dimHighlight(qMin(highlight.red()/2 + 110, 255),
2425                                 qMin(highlight.green()/2 + 110, 255),
2426                                 qMin(highlight.blue()/2 + 110, 255),
2427                                 (widget && widget->isTopLevel())? 255 : 127);
2428             p->setBrush(dimHighlight);
2429             p->drawRect(option->rect.adjusted(0, 0, -1, -1));
2430             p->restore();
2431             return;
2432         }
2433         break;
2434 #endif // QT_CONFIG(rubberband)
2435     case CE_HeaderEmptyArea:
2436         if (option->state & State_Horizontal)
2437         {
2438             themeNumber = QWindowsXPStylePrivate::HeaderTheme;
2439             stateId = HIS_NORMAL;
2440         }
2441         else {
2442             QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, p, widget);
2443             return;
2444         }
2445         break;
2446     default:
2447         break;
2448     }
2449 
2450     XPThemeData theme(widget, p, themeNumber, partId, stateId, rect);
2451     if (!theme.isValid()) {
2452         QWindowsStyle::drawControl(element, option, p, widget);
2453         return;
2454     }
2455 
2456     theme.rotate = rotate;
2457     theme.mirrorHorizontally = hMirrored;
2458     theme.mirrorVertically = vMirrored;
2459     d->drawBackground(theme);
2460 }
2461 
scrollBarGripperBounds(QStyle::State flags,const QWidget * widget,XPThemeData * theme)2462 QRect QWindowsXPStylePrivate::scrollBarGripperBounds(QStyle::State flags, const QWidget *widget, XPThemeData *theme)
2463 {
2464     const bool horizontal = flags & QStyle::State_Horizontal;
2465     const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
2466     const QMargins contentsMargin =
2467         (theme->margins(theme->rect, TMT_SIZINGMARGINS) * factor).toMargins();
2468     theme->partId = horizontal ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT;
2469     const QSize size = (theme->size() * factor).toSize();
2470 
2471     const int hSpace = theme->rect.width() - size.width();
2472     const int vSpace = theme->rect.height() - size.height();
2473     const bool sufficientSpace = (horizontal && hSpace > (contentsMargin.left() + contentsMargin.right()))
2474         || vSpace > contentsMargin.top() + contentsMargin.bottom();
2475     return sufficientSpace ? QRect(theme->rect.topLeft() + QPoint(hSpace, vSpace) / 2, size) : QRect();
2476 }
2477 
2478 #if QT_CONFIG(mdiarea)
2479 // Helper for drawing MDI buttons into the corner widget of QMenuBar in case a
2480 // QMdiSubWindow is maximized.
populateMdiButtonTheme(const QStyle * proxy,const QWidget * widget,const QStyleOptionComplex * option,QStyle::SubControl subControl,int part,XPThemeData * theme)2481 static void populateMdiButtonTheme(const QStyle *proxy, const QWidget *widget,
2482                                    const QStyleOptionComplex *option,
2483                                    QStyle::SubControl subControl, int part,
2484                                    XPThemeData *theme)
2485 {
2486         theme->partId = part;
2487         theme->rect = proxy->subControlRect(QStyle::CC_MdiControls, option, subControl, widget);
2488         if (!option->state.testFlag(QStyle::State_Enabled))
2489             theme->stateId = CBS_INACTIVE;
2490         else if (option->state.testFlag(QStyle::State_Sunken) && option->activeSubControls.testFlag(subControl))
2491             theme->stateId = CBS_PUSHED;
2492         else if (option->state.testFlag(QStyle::State_MouseOver) && option->activeSubControls.testFlag(subControl))
2493             theme->stateId = CBS_HOT;
2494         else
2495             theme->stateId = CBS_NORMAL;
2496 }
2497 
2498 // Calculate an small (max 2), empirical correction factor for scaling up
2499 // WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON, which are too
2500 // small on High DPI screens (QTBUG-75927).
mdiButtonCorrectionFactor(XPThemeData & theme,const QPaintDevice * pd=nullptr)2501 qreal mdiButtonCorrectionFactor(XPThemeData &theme, const QPaintDevice *pd = nullptr)
2502 {
2503     const auto dpr = pd ? pd->devicePixelRatioF() : qApp->devicePixelRatio();
2504     const QSizeF nativeSize = QSizeF(theme.size()) / dpr;
2505     const QSizeF requestedSize(theme.rect.size());
2506     const auto rawFactor = qMin(requestedSize.width() / nativeSize.width(),
2507                                 requestedSize.height() / nativeSize.height());
2508     const auto factor = rawFactor >= qreal(2) ? qreal(2) : qreal(1);
2509     return factor;
2510 }
2511 #endif // QT_CONFIG(mdiarea)
2512 
populateTitleBarButtonTheme(const QStyle * proxy,const QWidget * widget,const QStyleOptionComplex * option,QStyle::SubControl subControl,bool isTitleBarActive,int part,XPThemeData * theme)2513 static void populateTitleBarButtonTheme(const QStyle *proxy, const QWidget *widget,
2514                                         const QStyleOptionComplex *option,
2515                                         QStyle::SubControl subControl,
2516                                         bool isTitleBarActive, int part,
2517                                         XPThemeData *theme)
2518 {
2519     theme->rect = proxy->subControlRect(QStyle::CC_TitleBar, option, subControl, widget);
2520     theme->partId = part;
2521     if (widget && !widget->isEnabled())
2522         theme->stateId = RBS_DISABLED;
2523     else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_Sunken))
2524         theme->stateId = RBS_PUSHED;
2525     else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_MouseOver))
2526         theme->stateId = RBS_HOT;
2527     else if (!isTitleBarActive)
2528         theme->stateId = RBS_INACTIVE;
2529     else
2530         theme->stateId = RBS_NORMAL;
2531 }
2532 
2533 /*!
2534     \reimp
2535 */
drawComplexControl(ComplexControl cc,const QStyleOptionComplex * option,QPainter * p,const QWidget * widget) const2536 void QWindowsXPStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option,
2537                                          QPainter *p, const QWidget *widget) const
2538 {
2539     QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
2540 
2541     if (!QWindowsXPStylePrivate::useXP()) {
2542         QWindowsStyle::drawComplexControl(cc, option, p, widget);
2543         return;
2544     }
2545 
2546     State flags = option->state;
2547     SubControls sub = option->subControls;
2548     QRect r = option->rect;
2549 
2550     int partId = 0;
2551     int stateId = 0;
2552     if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow())
2553         flags |= State_MouseOver;
2554 
2555     switch (cc) {
2556 #if QT_CONFIG(spinbox)
2557     case CC_SpinBox:
2558         if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option))
2559         {
2560             XPThemeData theme(widget, p, QWindowsXPStylePrivate::SpinTheme);
2561 
2562             if (sb->frame && (sub & SC_SpinBoxFrame)) {
2563                 partId = EP_EDITTEXT;
2564                 if (!(flags & State_Enabled))
2565                     stateId = ETS_DISABLED;
2566                 else if (flags & State_HasFocus)
2567                     stateId = ETS_FOCUSED;
2568                 else
2569                     stateId = ETS_NORMAL;
2570 
2571                 XPThemeData ftheme(widget, p, QWindowsXPStylePrivate::EditTheme,
2572                                    partId, stateId, r);
2573                 ftheme.noContent = true;
2574                 d->drawBackground(ftheme);
2575             }
2576             if (sub & SC_SpinBoxUp) {
2577                 theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget);
2578                 partId = SPNP_UP;
2579                 if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled))
2580                     stateId = UPS_DISABLED;
2581                 else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken))
2582                     stateId = UPS_PRESSED;
2583                 else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver))
2584                     stateId = UPS_HOT;
2585                 else
2586                     stateId = UPS_NORMAL;
2587                 theme.partId = partId;
2588                 theme.stateId = stateId;
2589                 d->drawBackground(theme);
2590             }
2591             if (sub & SC_SpinBoxDown) {
2592                 theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget);
2593                 partId = SPNP_DOWN;
2594                 if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled))
2595                     stateId = DNS_DISABLED;
2596                 else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken))
2597                     stateId = DNS_PRESSED;
2598                 else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver))
2599                     stateId = DNS_HOT;
2600                 else
2601                     stateId = DNS_NORMAL;
2602                 theme.partId = partId;
2603                 theme.stateId = stateId;
2604                 d->drawBackground(theme);
2605             }
2606         }
2607         break;
2608 #endif // QT_CONFIG(spinbox)
2609 #if QT_CONFIG(combobox)
2610     case CC_ComboBox:
2611         if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option))
2612         {
2613             if (sub & SC_ComboBoxEditField) {
2614                 if (cmb->frame) {
2615                     partId = EP_EDITTEXT;
2616                     if (!(flags & State_Enabled))
2617                         stateId = ETS_DISABLED;
2618                     else if (flags & State_HasFocus)
2619                         stateId = ETS_FOCUSED;
2620                     else
2621                         stateId = ETS_NORMAL;
2622                     XPThemeData theme(widget, p, QWindowsXPStylePrivate::EditTheme, partId, stateId, r);
2623                     d->drawBackground(theme);
2624                 } else {
2625                     QBrush editBrush = cmb->palette.brush(QPalette::Base);
2626                     p->fillRect(option->rect, editBrush);
2627                 }
2628                 if (!cmb->editable) {
2629                     QRect re = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget);
2630                     if (option->state & State_HasFocus) {
2631                         p->fillRect(re, option->palette.highlight());
2632                         p->setPen(option->palette.highlightedText().color());
2633                         p->setBackground(option->palette.highlight());
2634                     } else {
2635                         p->fillRect(re, option->palette.base());
2636                         p->setPen(option->palette.text().color());
2637                         p->setBackground(option->palette.base());
2638                     }
2639                 }
2640             }
2641 
2642             if (sub & SC_ComboBoxArrow) {
2643                 XPThemeData theme(widget, p, QWindowsXPStylePrivate::ComboboxTheme);
2644                 theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
2645                 partId = CP_DROPDOWNBUTTON;
2646                 if (!(flags & State_Enabled))
2647                     stateId = CBXS_DISABLED;
2648                 else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_Sunken))
2649                     stateId = CBXS_PRESSED;
2650                 else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_MouseOver))
2651                     stateId = CBXS_HOT;
2652                 else
2653                     stateId = CBXS_NORMAL;
2654                 theme.partId = partId;
2655                 theme.stateId = stateId;
2656                 d->drawBackground(theme);
2657             }
2658         }
2659         break;
2660 #endif // QT_CONFIG(combobox)
2661     case CC_ScrollBar:
2662         if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option))
2663         {
2664             XPThemeData theme(widget, p, QWindowsXPStylePrivate::ScrollBarTheme);
2665             bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
2666             if (maxedOut)
2667                 flags &= ~State_Enabled;
2668 
2669             bool isHorz = flags & State_Horizontal;
2670             bool isRTL  = option->direction == Qt::RightToLeft;
2671             if (sub & SC_ScrollBarAddLine) {
2672                 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
2673                 partId = SBP_ARROWBTN;
2674                 if (!(flags & State_Enabled))
2675                     stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED);
2676                 else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken))
2677                     stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED);
2678                 else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver))
2679                     stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT);
2680                 else
2681                     stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL);
2682                 theme.partId = partId;
2683                 theme.stateId = stateId;
2684                 d->drawBackground(theme);
2685             }
2686             if (sub & SC_ScrollBarSubLine) {
2687                 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
2688                 partId = SBP_ARROWBTN;
2689                 if (!(flags & State_Enabled))
2690                     stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED);
2691                 else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken))
2692                     stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED);
2693                 else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver))
2694                     stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT);
2695                 else
2696                     stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL);
2697                 theme.partId = partId;
2698                 theme.stateId = stateId;
2699                 d->drawBackground(theme);
2700             }
2701             if (maxedOut) {
2702                 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
2703                 theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget));
2704                 theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget));
2705                 partId = scrollbar->orientation == Qt::Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
2706                 stateId = SCRBS_DISABLED;
2707                 theme.partId = partId;
2708                 theme.stateId = stateId;
2709                 d->drawBackground(theme);
2710             } else {
2711                 if (sub & SC_ScrollBarSubPage) {
2712                     theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget);
2713                     partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
2714                     if (!(flags & State_Enabled))
2715                         stateId = SCRBS_DISABLED;
2716                     else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken))
2717                         stateId = SCRBS_PRESSED;
2718                     else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver))
2719                         stateId = SCRBS_HOT;
2720                     else
2721                         stateId = SCRBS_NORMAL;
2722                     theme.partId = partId;
2723                     theme.stateId = stateId;
2724                     d->drawBackground(theme);
2725                 }
2726                 if (sub & SC_ScrollBarAddPage) {
2727                     theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget);
2728                     partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
2729                     if (!(flags & State_Enabled))
2730                         stateId = SCRBS_DISABLED;
2731                     else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken))
2732                         stateId = SCRBS_PRESSED;
2733                     else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver))
2734                         stateId = SCRBS_HOT;
2735                     else
2736                         stateId = SCRBS_NORMAL;
2737                     theme.partId = partId;
2738                     theme.stateId = stateId;
2739                     d->drawBackground(theme);
2740                 }
2741                 if (sub & SC_ScrollBarSlider) {
2742                     theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
2743                     if (!(flags & State_Enabled))
2744                         stateId = SCRBS_DISABLED;
2745                     else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken))
2746                         stateId = SCRBS_PRESSED;
2747                     else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver))
2748                         stateId = SCRBS_HOT;
2749                     else
2750                         stateId = SCRBS_NORMAL;
2751 
2752                     // Draw handle
2753                     theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
2754                     theme.stateId = stateId;
2755                     d->drawBackground(theme);
2756 
2757                     const QRect gripperBounds = QWindowsXPStylePrivate::scrollBarGripperBounds(flags, widget, &theme);
2758                     // Draw gripper if there is enough space
2759                     if (!gripperBounds.isEmpty()) {
2760                         p->save();
2761                         theme.rect = gripperBounds;
2762                         p->setClipRegion(d->region(theme));// Only change inside the region of the gripper
2763                         d->drawBackground(theme);          // Transparent gripper ontop of background
2764                         p->restore();
2765                     }
2766                 }
2767             }
2768         }
2769         break;
2770 
2771 #if QT_CONFIG(slider)
2772     case CC_Slider:
2773         if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option))
2774         {
2775             XPThemeData theme(widget, p, QWindowsXPStylePrivate::TrackBarTheme);
2776             QRect slrect = slider->rect;
2777             QRegion tickreg = slrect;
2778             if (sub & SC_SliderGroove) {
2779                 theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget);
2780                 if (slider->orientation == Qt::Horizontal) {
2781                     partId = TKP_TRACK;
2782                     stateId = TRS_NORMAL;
2783                     theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4);
2784                 } else {
2785                     partId = TKP_TRACKVERT;
2786                     stateId = TRVS_NORMAL;
2787                     theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height());
2788                 }
2789                 theme.partId = partId;
2790                 theme.stateId = stateId;
2791                 d->drawBackground(theme);
2792                 tickreg -= theme.rect;
2793             }
2794             if (sub & SC_SliderTickmarks) {
2795                 int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget);
2796                 int ticks = slider->tickPosition;
2797                 int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget);
2798                 int len = proxy()->pixelMetric(PM_SliderLength, slider, widget);
2799                 int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget);
2800                 int interval = slider->tickInterval;
2801                 if (interval <= 0) {
2802                     interval = slider->singleStep;
2803                     if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
2804                                                         available)
2805                         - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
2806                                                           0, available) < 3)
2807                         interval = slider->pageStep;
2808                 }
2809                 if (!interval)
2810                     interval = 1;
2811                 int fudge = len / 2;
2812                 int pos;
2813                 int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0;
2814                 p->setPen(d->sliderTickColor);
2815                 QVarLengthArray<QLine, 32> lines;
2816                 int v = slider->minimum;
2817                 while (v <= slider->maximum + 1) {
2818                     if (v == slider->maximum + 1 && interval == 1)
2819                         break;
2820                     const int v_ = qMin(v, slider->maximum);
2821                     int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3;
2822                     pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
2823                                                           v_, available) + fudge;
2824                     if (slider->orientation == Qt::Horizontal) {
2825                         if (ticks & QSlider::TicksAbove)
2826                             lines.append(QLine(pos, tickOffset - 1 - bothOffset,
2827                                                pos, tickOffset - 1 - bothOffset - tickLength));
2828 
2829                         if (ticks & QSlider::TicksBelow)
2830                             lines.append(QLine(pos, tickOffset + thickness + bothOffset,
2831                                                pos, tickOffset + thickness + bothOffset + tickLength));
2832                     } else {
2833                         if (ticks & QSlider::TicksAbove)
2834                             lines.append(QLine(tickOffset - 1 - bothOffset, pos,
2835                                                tickOffset - 1 - bothOffset - tickLength, pos));
2836 
2837                         if (ticks & QSlider::TicksBelow)
2838                             lines.append(QLine(tickOffset + thickness + bothOffset, pos,
2839                                                tickOffset + thickness + bothOffset + tickLength, pos));
2840                     }
2841                     // in the case where maximum is max int
2842                     int nextInterval = v + interval;
2843                     if (nextInterval < v)
2844                         break;
2845                     v = nextInterval;
2846                 }
2847                 if (!lines.isEmpty()) {
2848                     p->save();
2849                     p->translate(slrect.topLeft());
2850                     p->drawLines(lines.constData(), lines.size());
2851                     p->restore();
2852                 }
2853             }
2854             if (sub & SC_SliderHandle) {
2855                 theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget);
2856                 if (slider->orientation == Qt::Horizontal) {
2857                     if (slider->tickPosition == QSlider::TicksAbove)
2858                         partId = TKP_THUMBTOP;
2859                     else if (slider->tickPosition == QSlider::TicksBelow)
2860                         partId = TKP_THUMBBOTTOM;
2861                     else
2862                         partId = TKP_THUMB;
2863 
2864                     if (!(slider->state & State_Enabled))
2865                         stateId = TUS_DISABLED;
2866                     else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
2867                         stateId = TUS_PRESSED;
2868                     else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
2869                         stateId = TUS_HOT;
2870                     else if (flags & State_HasFocus)
2871                         stateId = TUS_FOCUSED;
2872                     else
2873                         stateId = TUS_NORMAL;
2874                 } else {
2875                     if (slider->tickPosition == QSlider::TicksLeft)
2876                         partId = TKP_THUMBLEFT;
2877                     else if (slider->tickPosition == QSlider::TicksRight)
2878                         partId = TKP_THUMBRIGHT;
2879                     else
2880                         partId = TKP_THUMBVERT;
2881 
2882                     if (!(slider->state & State_Enabled))
2883                         stateId = TUVS_DISABLED;
2884                     else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
2885                         stateId = TUVS_PRESSED;
2886                     else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
2887                         stateId = TUVS_HOT;
2888                     else if (flags & State_HasFocus)
2889                         stateId = TUVS_FOCUSED;
2890                     else
2891                         stateId = TUVS_NORMAL;
2892                 }
2893                 theme.partId = partId;
2894                 theme.stateId = stateId;
2895                 d->drawBackground(theme);
2896             }
2897             if (slider->state & State_HasFocus) {
2898                 QStyleOptionFocusRect fropt;
2899                 fropt.QStyleOption::operator=(*slider);
2900                 fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget);
2901                 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
2902             }
2903         }
2904         break;
2905 #endif
2906 #if QT_CONFIG(toolbutton)
2907     case CC_ToolButton:
2908         if (const QStyleOptionToolButton *toolbutton
2909             = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
2910             QRect button, menuarea;
2911             button = proxy()->subControlRect(cc, toolbutton, SC_ToolButton, widget);
2912             menuarea = proxy()->subControlRect(cc, toolbutton, SC_ToolButtonMenu, widget);
2913 
2914             State bflags = toolbutton->state & ~State_Sunken;
2915             State mflags = bflags;
2916             bool autoRaise = flags & State_AutoRaise;
2917             if (autoRaise) {
2918                 if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) {
2919                     bflags &= ~State_Raised;
2920                 }
2921             }
2922 
2923             if (toolbutton->state & State_Sunken) {
2924                 if (toolbutton->activeSubControls & SC_ToolButton) {
2925                     bflags |= State_Sunken;
2926                     mflags |= State_MouseOver | State_Sunken;
2927                 } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) {
2928                     mflags |= State_Sunken;
2929                     bflags |= State_MouseOver;
2930                 }
2931             }
2932 
2933             QStyleOption tool = *toolbutton;
2934             if (toolbutton->subControls & SC_ToolButton) {
2935                 if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) {
2936                     if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) {
2937                         XPThemeData theme(widget, p, QWindowsXPStylePrivate::ToolBarTheme);
2938                         theme.partId = TP_SPLITBUTTON;
2939                         theme.rect = button;
2940                         if (!(bflags & State_Enabled))
2941                             stateId = TS_DISABLED;
2942                         else if (bflags & State_Sunken)
2943                             stateId = TS_PRESSED;
2944                         else if (bflags & State_MouseOver || !(flags & State_AutoRaise))
2945                             stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
2946                         else if (bflags & State_On)
2947                             stateId = TS_CHECKED;
2948                         else
2949                             stateId = TS_NORMAL;
2950                         if (option->direction == Qt::RightToLeft)
2951                             theme.mirrorHorizontally = true;
2952                         theme.stateId = stateId;
2953                         d->drawBackground(theme);
2954                     } else {
2955                         tool.rect = option->rect;
2956                         tool.state = bflags;
2957                         if (autoRaise) // for tool bars
2958                             proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p, widget);
2959                         else
2960                             proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, widget);
2961                     }
2962                 }
2963             }
2964 
2965             if (toolbutton->state & State_HasFocus) {
2966                 QStyleOptionFocusRect fr;
2967                 fr.QStyleOption::operator=(*toolbutton);
2968                 fr.rect.adjust(3, 3, -3, -3);
2969                 if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup)
2970                     fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator,
2971                                                       toolbutton, widget), 0);
2972                 proxy()->drawPrimitive(PE_FrameFocusRect, &fr, p, widget);
2973             }
2974             QStyleOptionToolButton label = *toolbutton;
2975             label.state = bflags;
2976             int fw = 2;
2977             if (!autoRaise)
2978                 label.state &= ~State_Sunken;
2979             label.rect = button.adjusted(fw, fw, -fw, -fw);
2980             proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget);
2981 
2982             if (toolbutton->subControls & SC_ToolButtonMenu) {
2983                 tool.rect = menuarea;
2984                 tool.state = mflags;
2985                 if (autoRaise) {
2986                     proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, p, widget);
2987                 } else {
2988                     tool.state = mflags;
2989                     menuarea.adjust(-2, 0, 0, 0);
2990                     // Draw menu button
2991                     if ((bflags & State_Sunken) != (mflags & State_Sunken)){
2992                         p->save();
2993                         p->setClipRect(menuarea);
2994                         tool.rect = option->rect;
2995                         proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, nullptr);
2996                         p->restore();
2997                     }
2998                     // Draw arrow
2999                     p->save();
3000                     p->setPen(option->palette.dark().color());
3001                     p->drawLine(menuarea.left(), menuarea.top() + 3,
3002                                 menuarea.left(), menuarea.bottom() - 3);
3003                     p->setPen(option->palette.light().color());
3004                     p->drawLine(menuarea.left() - 1, menuarea.top() + 3,
3005                                 menuarea.left() - 1, menuarea.bottom() - 3);
3006 
3007                     tool.rect = menuarea.adjusted(2, 3, -2, -1);
3008                     proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, p, widget);
3009                     p->restore();
3010                 }
3011             } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) {
3012                 int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget);
3013                 QRect ir = toolbutton->rect;
3014                 QStyleOptionToolButton newBtn = *toolbutton;
3015                 newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5);
3016                 proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
3017             }
3018         }
3019         break;
3020 #endif // QT_CONFIG(toolbutton)
3021 
3022     case CC_TitleBar:
3023         {
3024             if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option))
3025             {
3026                 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
3027                 bool isActive = tb->titleBarState & QStyle::State_Active;
3028                 XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme);
3029                 if (sub & SC_TitleBarLabel) {
3030 
3031                         partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION;
3032                     theme.rect = option->rect;
3033                     if (widget && !widget->isEnabled())
3034                         stateId = CS_DISABLED;
3035                     else if (isActive)
3036                         stateId = CS_ACTIVE;
3037                     else
3038                         stateId = CS_INACTIVE;
3039 
3040                     theme.partId = partId;
3041                     theme.stateId = stateId;
3042                     d->drawBackground(theme);
3043 
3044                     QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget);
3045 
3046                     int result = TST_NONE;
3047                     GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
3048                     if (result != TST_NONE) {
3049                         COLORREF textShadowRef;
3050                         GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
3051                         QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
3052                         p->setPen(textShadow);
3053                         p->drawText(int(ir.x() + 3 * factor), int(ir.y() + 2 * factor),
3054                                     int(ir.width() - 1 * factor), ir.height(),
3055                                     Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
3056                     }
3057                     COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
3058                     QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
3059                     p->setPen(textColor);
3060                     p->drawText(int(ir.x() + 2 * factor), int(ir.y() + 1 * factor),
3061                                 int(ir.width() - 2 * factor), ir.height(),
3062                                 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
3063                 }
3064                 if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
3065                     theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget);
3066                     partId = WP_SYSBUTTON;
3067                     if ((widget && !widget->isEnabled()) || !isActive)
3068                         stateId = SBS_DISABLED;
3069                     else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken))
3070                         stateId = SBS_PUSHED;
3071                     else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver))
3072                         stateId = SBS_HOT;
3073                     else
3074                         stateId = SBS_NORMAL;
3075                     if (!tb->icon.isNull()) {
3076                         tb->icon.paint(p, theme.rect);
3077                     } else {
3078                         theme.partId = partId;
3079                         theme.stateId = stateId;
3080                         if (theme.size().isEmpty()) {
3081                             int iconSize = proxy()->pixelMetric(PM_SmallIconSize, tb, widget);
3082                             QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, tb, widget).pixmap(iconSize, iconSize);
3083                             p->save();
3084                             drawItemPixmap(p, theme.rect, Qt::AlignCenter, pm);
3085                             p->restore();
3086                         } else {
3087                             d->drawBackground(theme);
3088                         }
3089                     }
3090                 }
3091 
3092                 if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint
3093                         && !(tb->titleBarState & Qt::WindowMinimized)) {
3094                     populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMinButton, isActive, WP_MINBUTTON, &theme);
3095                     d->drawBackground(theme);
3096                 }
3097                 if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint
3098                         && !(tb->titleBarState & Qt::WindowMaximized)) {
3099                     populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMaxButton, isActive, WP_MAXBUTTON, &theme);
3100                     d->drawBackground(theme);
3101                 }
3102                 if (sub & SC_TitleBarContextHelpButton
3103                     && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) {
3104                     populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarContextHelpButton, isActive, WP_HELPBUTTON, &theme);
3105                     d->drawBackground(theme);
3106                 }
3107                 bool drawNormalButton = (sub & SC_TitleBarNormalButton)
3108                                         && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
3109                                         && (tb->titleBarState & Qt::WindowMinimized))
3110                                         || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
3111                                         && (tb->titleBarState & Qt::WindowMaximized)));
3112                 if (drawNormalButton) {
3113                     populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarNormalButton, isActive, WP_RESTOREBUTTON, &theme);
3114                     d->drawBackground(theme);
3115                 }
3116                 if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
3117                         && !(tb->titleBarState & Qt::WindowMinimized)) {
3118                     populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarShadeButton, isActive, WP_MINBUTTON, &theme);
3119                     d->drawBackground(theme);
3120                 }
3121                 if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
3122                         && tb->titleBarState & Qt::WindowMinimized) {
3123                     populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarUnshadeButton, isActive, WP_RESTOREBUTTON, &theme);
3124                     d->drawBackground(theme);
3125                 }
3126                 if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
3127                     populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarCloseButton, isActive, WP_CLOSEBUTTON, &theme);
3128                     d->drawBackground(theme);
3129                 }
3130             }
3131         }
3132         break;
3133 
3134 #if QT_CONFIG(mdiarea)
3135     case CC_MdiControls:
3136         {
3137             XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL);
3138             if (Q_UNLIKELY(!theme.isValid()))
3139                 return;
3140 
3141             if (option->subControls.testFlag(SC_MdiCloseButton)) {
3142                 populateMdiButtonTheme(proxy(), widget, option, SC_MdiCloseButton, WP_MDICLOSEBUTTON, &theme);
3143                 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3144             }
3145             if (option->subControls.testFlag(SC_MdiNormalButton)) {
3146                 populateMdiButtonTheme(proxy(), widget, option, SC_MdiNormalButton, WP_MDIRESTOREBUTTON, &theme);
3147                 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3148             }
3149             if (option->subControls.testFlag(QStyle::SC_MdiMinButton)) {
3150                 populateMdiButtonTheme(proxy(), widget, option, SC_MdiMinButton, WP_MDIMINBUTTON, &theme);
3151                 d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget));
3152             }
3153         }
3154         break;
3155 #endif // QT_CONFIG(mdiarea)
3156 #if QT_CONFIG(dial)
3157     case CC_Dial:
3158         if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option))
3159             QStyleHelper::drawDial(dial, p);
3160         break;
3161 #endif // QT_CONFIG(dial)
3162     default:
3163         QWindowsStyle::drawComplexControl(cc, option, p, widget);
3164         break;
3165     }
3166 }
3167 
progressBarOrientation(const QStyleOption * option=nullptr)3168 static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = nullptr)
3169 {
3170     if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
3171         return pb->orientation;
3172     return Qt::Horizontal;
3173 }
3174 
pixelMetricFromSystemDp(QStyle::PixelMetric pm,const QStyleOption * option,const QWidget * widget)3175 int QWindowsXPStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option, const QWidget *widget)
3176 {
3177     switch (pm) {
3178     case QStyle::PM_IndicatorWidth:
3179         return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width();
3180     case QStyle::PM_IndicatorHeight:
3181         return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height();
3182     case QStyle::PM_ExclusiveIndicatorWidth:
3183         return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width();
3184     case QStyle::PM_ExclusiveIndicatorHeight:
3185         return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height();
3186     case QStyle::PM_ProgressBarChunkWidth:
3187         return progressBarOrientation(option) == Qt::Horizontal
3188             ? XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNK).width()
3189             : XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNKVERT).height();
3190     case QStyle::PM_SliderThickness:
3191         return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::TrackBarTheme, TKP_THUMB).height();
3192     case QStyle::PM_TitleBarHeight:
3193         return widget && (widget->windowType() == Qt::Tool)
3194             ? GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME)
3195             : GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME);
3196     case QStyle::PM_MdiSubWindowFrameWidth:
3197         return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width();
3198     case QStyle::PM_DockWidgetFrameWidth:
3199         return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLFRAMERIGHT, FS_ACTIVE).width();
3200     default:
3201         break;
3202     }
3203     return QWindowsXPStylePrivate::InvalidMetric;
3204 }
3205 
3206 /*! \reimp */
pixelMetric(PixelMetric pm,const QStyleOption * option,const QWidget * widget) const3207 int QWindowsXPStyle::pixelMetric(PixelMetric pm, const QStyleOption *option, const QWidget *widget) const
3208 {
3209     if (!QWindowsXPStylePrivate::useXP())
3210         return QWindowsStyle::pixelMetric(pm, option, widget);
3211 
3212     int res = QWindowsXPStylePrivate::pixelMetricFromSystemDp(pm, option, widget);
3213     if (res != QWindowsStylePrivate::InvalidMetric)
3214         return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(widget));
3215 
3216     res = 0;
3217     switch (pm) {
3218     case PM_MenuBarPanelWidth:
3219     case PM_ButtonDefaultIndicator:
3220         res = 0;
3221         break;
3222 
3223     case PM_DefaultFrameWidth:
3224         res = qobject_cast<const QListView*>(widget) ? 2 : 1;
3225         break;
3226     case PM_MenuPanelWidth:
3227     case PM_SpinBoxFrameWidth:
3228         res = 1;
3229         break;
3230 
3231     case PM_TabBarTabOverlap:
3232     case PM_MenuHMargin:
3233     case PM_MenuVMargin:
3234         res = 2;
3235         break;
3236 
3237     case PM_TabBarBaseOverlap:
3238         if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
3239             switch (tab->shape) {
3240             case QTabBar::RoundedNorth:
3241             case QTabBar::TriangularNorth:
3242             case QTabBar::RoundedWest:
3243             case QTabBar::TriangularWest:
3244                 res = 1;
3245                 break;
3246             case QTabBar::RoundedSouth:
3247             case QTabBar::TriangularSouth:
3248                 res = 2;
3249                 break;
3250             case QTabBar::RoundedEast:
3251             case QTabBar::TriangularEast:
3252                 res = 3;
3253                 break;
3254             }
3255         }
3256         break;
3257 
3258     case PM_SplitterWidth:
3259         res = qMax(int(QStyleHelper::dpiScaled(5., option)),
3260                    QApplication::globalStrut().width());
3261         break;
3262 
3263     case PM_MdiSubWindowMinimizedWidth:
3264         res = 160;
3265         break;
3266 
3267 #if QT_CONFIG(toolbar)
3268     case PM_ToolBarHandleExtent:
3269         res = int(QStyleHelper::dpiScaled(8., option));
3270         break;
3271 
3272 #endif // QT_CONFIG(toolbar)
3273     case PM_DockWidgetSeparatorExtent:
3274     case PM_DockWidgetTitleMargin:
3275         res = int(QStyleHelper::dpiScaled(4., option));
3276         break;
3277 
3278     case PM_ButtonShiftHorizontal:
3279     case PM_ButtonShiftVertical:
3280         res = qstyleoption_cast<const QStyleOptionToolButton *>(option) ? 1 : 0;
3281         break;
3282 
3283     default:
3284         res = QWindowsStyle::pixelMetric(pm, option, widget);
3285     }
3286 
3287     return res;
3288 }
3289 
3290 /*
3291   This function is used by subControlRect to check if a button
3292   should be drawn for the given subControl given a set of window flags.
3293 */
buttonVisible(const QStyle::SubControl sc,const QStyleOptionTitleBar * tb)3294 static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){
3295 
3296     bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
3297     bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
3298     const uint flags = tb->titleBarFlags;
3299     bool retVal = false;
3300     switch (sc) {
3301     case QStyle::SC_TitleBarContextHelpButton:
3302         if (flags & Qt::WindowContextHelpButtonHint)
3303             retVal = true;
3304         break;
3305     case QStyle::SC_TitleBarMinButton:
3306         if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint))
3307             retVal = true;
3308         break;
3309     case QStyle::SC_TitleBarNormalButton:
3310         if (isMinimized && (flags & Qt::WindowMinimizeButtonHint))
3311             retVal = true;
3312         else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint))
3313             retVal = true;
3314         break;
3315     case QStyle::SC_TitleBarMaxButton:
3316         if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint))
3317             retVal = true;
3318         break;
3319     case QStyle::SC_TitleBarShadeButton:
3320         if (!isMinimized &&  flags & Qt::WindowShadeButtonHint)
3321             retVal = true;
3322         break;
3323     case QStyle::SC_TitleBarUnshadeButton:
3324         if (isMinimized && flags & Qt::WindowShadeButtonHint)
3325             retVal = true;
3326         break;
3327     case QStyle::SC_TitleBarCloseButton:
3328         if (flags & Qt::WindowSystemMenuHint)
3329             retVal = true;
3330         break;
3331     case QStyle::SC_TitleBarSysMenu:
3332         if (flags & Qt::WindowSystemMenuHint)
3333             retVal = true;
3334         break;
3335     default :
3336         retVal = true;
3337     }
3338     return retVal;
3339 }
3340 
3341 /*!
3342     \reimp
3343 */
subControlRect(ComplexControl cc,const QStyleOptionComplex * option,SubControl subControl,const QWidget * widget) const3344 QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option,
3345                                       SubControl subControl, const QWidget *widget) const
3346 {
3347     if (!QWindowsXPStylePrivate::useXP())
3348         return QWindowsStyle::subControlRect(cc, option, subControl, widget);
3349 
3350     QRect rect;
3351 
3352     switch (cc) {
3353     case CC_TitleBar:
3354         if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
3355             if (!buttonVisible(subControl, tb))
3356                 return rect;
3357             const bool isToolTitle = false;
3358             const int height = tb->rect.height();
3359             const int width = tb->rect.width();
3360             const int buttonMargin = int(QStyleHelper::dpiScaled(4, option));
3361             const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget);
3362             int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor)
3363                 - buttonMargin;
3364             int buttonWidth = qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor)
3365                 - buttonMargin;
3366             const int delta = buttonWidth + 2;
3367             int controlTop = option->rect.bottom() - buttonHeight - 2;
3368             const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget);
3369             const bool sysmenuHint  = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0;
3370             const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0;
3371             const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0;
3372             const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0;
3373             const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0;
3374             bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
3375             bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
3376             int offset = 0;
3377 
3378             switch (subControl) {
3379             case SC_TitleBarLabel:
3380                 rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height);
3381                 if (isToolTitle) {
3382                     if (sysmenuHint) {
3383                         rect.adjust(0, 0, -buttonWidth - 3, 0);
3384                     }
3385                     if (minimizeHint || maximizeHint)
3386                         rect.adjust(0, 0, -buttonWidth - 2, 0);
3387                 } else {
3388                     if (sysmenuHint) {
3389                         const int leftOffset = height - 8;
3390                         rect.adjust(leftOffset, 0, 0, 0);
3391                     }
3392                     if (minimizeHint)
3393                         rect.adjust(0, 0, -buttonWidth - 2, 0);
3394                     if (maximizeHint)
3395                         rect.adjust(0, 0, -buttonWidth - 2, 0);
3396                     if (contextHint)
3397                         rect.adjust(0, 0, -buttonWidth - 2, 0);
3398                     if (shadeHint)
3399                         rect.adjust(0, 0, -buttonWidth - 2, 0);
3400                 }
3401                 break;
3402 
3403             case SC_TitleBarContextHelpButton:
3404                 if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
3405                     offset += delta;
3406                 Q_FALLTHROUGH();
3407             case SC_TitleBarMinButton:
3408                 if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
3409                     offset += delta;
3410                 else if (subControl == SC_TitleBarMinButton)
3411                     break;
3412                 Q_FALLTHROUGH();
3413             case SC_TitleBarNormalButton:
3414                 if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
3415                     offset += delta;
3416                 else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
3417                     offset += delta;
3418                 else if (subControl == SC_TitleBarNormalButton)
3419                     break;
3420                 Q_FALLTHROUGH();
3421             case SC_TitleBarMaxButton:
3422                 if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
3423                     offset += delta;
3424                 else if (subControl == SC_TitleBarMaxButton)
3425                     break;
3426                 Q_FALLTHROUGH();
3427             case SC_TitleBarShadeButton:
3428                 if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
3429                     offset += delta;
3430                 else if (subControl == SC_TitleBarShadeButton)
3431                     break;
3432                 Q_FALLTHROUGH();
3433             case SC_TitleBarUnshadeButton:
3434                 if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
3435                     offset += delta;
3436                 else if (subControl == SC_TitleBarUnshadeButton)
3437                     break;
3438                 Q_FALLTHROUGH();
3439             case SC_TitleBarCloseButton:
3440                 if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
3441                     offset += delta;
3442                 else if (subControl == SC_TitleBarCloseButton)
3443                     break;
3444 
3445                 rect.setRect(width - offset - controlTop + 1, controlTop,
3446                              buttonWidth, buttonHeight);
3447                 break;
3448 
3449             case SC_TitleBarSysMenu:
3450                 {
3451                     const int controlTop = 6;
3452                     const int controlHeight = height - controlTop - 3;
3453                     const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option);
3454                     QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent));
3455                     if (tb->icon.isNull())
3456                         iconSize = QSize(controlHeight, controlHeight);
3457                     int hPad = (controlHeight - iconSize.height())/2;
3458                     int vPad = (controlHeight - iconSize.width())/2;
3459                     rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height());
3460                 }
3461                 break;
3462             default:
3463                 break;
3464             }
3465         }
3466         break;
3467 
3468     case CC_ComboBox:
3469         if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
3470             const int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height();
3471             const int xpos = x + wi - qRound(QStyleHelper::dpiScaled(1 + 16, option));
3472 
3473             switch (subControl) {
3474             case SC_ComboBoxFrame:
3475                 rect = cmb->rect;
3476                 break;
3477 
3478             case SC_ComboBoxArrow: {
3479                 const qreal dpi = QStyleHelper::dpi(option);
3480                 rect = QRect(xpos, y + qRound(QStyleHelper::dpiScaled(1, dpi)),
3481                              qRound(QStyleHelper::dpiScaled(16, dpi)),
3482                              he - qRound(QStyleHelper::dpiScaled(2, dpi)));
3483             }
3484                 break;
3485 
3486             case SC_ComboBoxEditField: {
3487                 const qreal dpi = QStyleHelper::dpi(option);
3488                 const int frame = qRound(QStyleHelper::dpiScaled(2, dpi));
3489                 rect = QRect(x + frame, y + frame,
3490                              wi - qRound(QStyleHelper::dpiScaled(3 + 16, dpi)),
3491                              he - qRound(QStyleHelper::dpiScaled(4, dpi)));
3492             }
3493                 break;
3494 
3495             case SC_ComboBoxListBoxPopup:
3496                 rect = cmb->rect;
3497                 break;
3498 
3499             default:
3500                 break;
3501             }
3502         }
3503         break;
3504 #if QT_CONFIG(mdiarea)
3505     case CC_MdiControls:
3506     {
3507         int numSubControls = 0;
3508         if (option->subControls & SC_MdiCloseButton)
3509             ++numSubControls;
3510         if (option->subControls & SC_MdiMinButton)
3511             ++numSubControls;
3512         if (option->subControls & SC_MdiNormalButton)
3513             ++numSubControls;
3514         if (numSubControls == 0)
3515             break;
3516 
3517         int buttonWidth = option->rect.width() / numSubControls;
3518         int offset = 0;
3519         switch (subControl) {
3520         case SC_MdiCloseButton:
3521             // Only one sub control, no offset needed.
3522             if (numSubControls == 1)
3523                 break;
3524             offset += buttonWidth;
3525             Q_FALLTHROUGH();
3526         case SC_MdiNormalButton:
3527             // No offset needed if
3528             // 1) There's only one sub control
3529             // 2) We have a close button and a normal button (offset already added in SC_MdiClose)
3530             if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton)))
3531                 break;
3532             if (option->subControls & SC_MdiNormalButton)
3533                 offset += buttonWidth;
3534             break;
3535         default:
3536             break;
3537         }
3538         rect = QRect(offset, 0, buttonWidth, option->rect.height());
3539         break;
3540     }
3541 #endif // QT_CONFIG(mdiarea)
3542 
3543     default:
3544         rect = visualRect(option->direction, option->rect,
3545                           QWindowsStyle::subControlRect(cc, option, subControl, widget));
3546         break;
3547     }
3548     return visualRect(option->direction, option->rect, rect);
3549 }
3550 
3551 /*!
3552     \reimp
3553 */
sizeFromContents(ContentsType ct,const QStyleOption * option,const QSize & contentsSize,const QWidget * widget) const3554 QSize QWindowsXPStyle::sizeFromContents(ContentsType ct, const QStyleOption *option,
3555                                         const QSize &contentsSize, const QWidget *widget) const
3556 {
3557     if (!QWindowsXPStylePrivate::useXP())
3558         return QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget);
3559 
3560     QSize sz(contentsSize);
3561     switch (ct) {
3562     case CT_LineEdit:
3563     case CT_ComboBox:
3564         {
3565             XPThemeData buttontheme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL);
3566             if (buttontheme.isValid()) {
3567                 const qreal factor = QWindowsXPStylePrivate::nativeMetricScaleFactor(widget);
3568                 const QMarginsF borderSize = buttontheme.margins() * factor;
3569                 if (!borderSize.isNull()) {
3570                     const qreal margin = qreal(2) * factor;
3571                     sz.rwidth() += qRound(borderSize.left() + borderSize.right() - margin);
3572                     sz.rheight() += int(borderSize.bottom() + borderSize.top() - margin
3573                         + qreal(1) / factor - 1);
3574                 }
3575                 const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, option) + 1);
3576                 sz += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget)
3577                                  + textMargins, 23), 0); //arrow button
3578             }
3579         }
3580         break;
3581     case CT_SpinBox:
3582         {
3583             //Spinbox adds frame twice
3584             sz = QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget);
3585             int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget);
3586             sz -= QSize(2*border, 2*border);
3587         }
3588         break;
3589     case CT_TabWidget:
3590         sz += QSize(6, 6);
3591         break;
3592     case CT_Menu:
3593         sz += QSize(1, 0);
3594         break;
3595 #if QT_CONFIG(menubar)
3596     case CT_MenuBarItem:
3597         if (!sz.isEmpty())
3598             sz += QSize(windowsItemHMargin * 5 + 1, 6);
3599         break;
3600 #endif
3601     case CT_MenuItem:
3602         if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
3603         {
3604             if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) {
3605                 sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
3606                 sz.setHeight(sz.height() - 2);
3607                 return sz;
3608             }
3609         }
3610         sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
3611         break;
3612 
3613     case CT_MdiControls: {
3614         sz.setHeight(int(QStyleHelper::dpiScaled(19, option)));
3615         int width = 54;
3616         if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) {
3617             width = 0;
3618             if (styleOpt->subControls & SC_MdiMinButton)
3619                 width += 17 + 1;
3620             if (styleOpt->subControls & SC_MdiNormalButton)
3621                 width += 17 + 1;
3622             if (styleOpt->subControls & SC_MdiCloseButton)
3623                 width += 17 + 1;
3624         }
3625         sz.setWidth(int(QStyleHelper::dpiScaled(width, option)));
3626     }
3627         break;
3628 
3629     default:
3630         sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget);
3631         break;
3632     }
3633 
3634     return sz;
3635 }
3636 
3637 
3638 /*! \reimp */
styleHint(StyleHint hint,const QStyleOption * option,const QWidget * widget,QStyleHintReturn * returnData) const3639 int QWindowsXPStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,
3640                                QStyleHintReturn *returnData) const
3641 {
3642   QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
3643     if (!QWindowsXPStylePrivate::useXP())
3644         return QWindowsStyle::styleHint(hint, option, widget, returnData);
3645 
3646     int res = 0;
3647     switch (hint) {
3648 
3649     case SH_EtchDisabledText:
3650         res = (qobject_cast<const QLabel*>(widget) != 0);
3651         break;
3652 
3653     case SH_SpinControls_DisableOnBounds:
3654         res = 0;
3655         break;
3656 
3657     case SH_TitleBar_AutoRaise:
3658     case SH_TitleBar_NoBorder:
3659         res = 1;
3660         break;
3661 
3662     case SH_GroupBox_TextLabelColor:
3663         if (!widget || (widget && widget->isEnabled()))
3664             res = d->groupBoxTextColor;
3665         else
3666             res = d->groupBoxTextColorDisabled;
3667         break;
3668 
3669     case SH_Table_GridLineColor:
3670         res = 0xC0C0C0;
3671         break;
3672 
3673     case SH_WindowFrame_Mask:
3674         {
3675             res = 1;
3676             QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData);
3677             const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option);
3678             if (mask && titlebar) {
3679                 // Note certain themes will not return the whole window frame but only the titlebar part when
3680                 // queried This function needs to return the entire window mask, hence we will only fetch the mask for the
3681                 // titlebar itself and add the remaining part of the window rect at the bottom.
3682                 int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option, widget);
3683                 QRect titleBarRect = option->rect;
3684                 titleBarRect.setHeight(tbHeight);
3685                 XPThemeData themeData;
3686                 if (titlebar->titleBarState & Qt::WindowMinimized) {
3687                     themeData = XPThemeData(widget, nullptr,
3688                                             QWindowsXPStylePrivate::WindowTheme,
3689                                             WP_MINCAPTION, CS_ACTIVE, titleBarRect);
3690                 } else
3691                     themeData = XPThemeData(widget, nullptr,
3692                                             QWindowsXPStylePrivate::WindowTheme,
3693                                             WP_CAPTION, CS_ACTIVE, titleBarRect);
3694                 mask->region = d->region(themeData) +
3695                                QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight);
3696             }
3697         }
3698         break;
3699 #if QT_CONFIG(rubberband)
3700     case SH_RubberBand_Mask:
3701         if (qstyleoption_cast<const QStyleOptionRubberBand *>(option))
3702             res = 0;
3703         break;
3704 #endif // QT_CONFIG(rubberband)
3705 
3706     case SH_ItemView_DrawDelegateFrame:
3707         res = 1;
3708         break;
3709 
3710     default:
3711         res =QWindowsStyle::styleHint(hint, option, widget, returnData);
3712     }
3713 
3714     return res;
3715 }
3716 
3717 /*! \reimp */
standardPalette() const3718 QPalette QWindowsXPStyle::standardPalette() const
3719 {
3720     return QWindowsXPStylePrivate::useXP() ? QPalette() : QWindowsStyle::standardPalette();
3721 }
3722 
3723 /*!
3724     \reimp
3725 */
standardPixmap(StandardPixmap standardPixmap,const QStyleOption * option,const QWidget * widget) const3726 QPixmap QWindowsXPStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option,
3727                                         const QWidget *widget) const
3728 {
3729     if (!QWindowsXPStylePrivate::useXP())
3730         return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
3731 
3732     switch(standardPixmap) {
3733     case SP_TitleBarMaxButton:
3734     case SP_TitleBarCloseButton:
3735         if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3736         {
3737             if (widget && widget->isWindow()) {
3738                 XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3739                 if (theme.isValid()) {
3740                     const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
3741                     return QIcon(QWindowsStyle::standardPixmap(standardPixmap, option, widget)).pixmap(size);
3742                 }
3743             }
3744         }
3745         break;
3746     default:
3747         break;
3748     }
3749     return QWindowsStyle::standardPixmap(standardPixmap, option, widget);
3750 }
3751 
3752 /*!
3753     \reimp
3754 */
standardIcon(StandardPixmap standardIcon,const QStyleOption * option,const QWidget * widget) const3755 QIcon QWindowsXPStyle::standardIcon(StandardPixmap standardIcon,
3756                                     const QStyleOption *option,
3757                                     const QWidget *widget) const
3758 {
3759     if (!QWindowsXPStylePrivate::useXP()) {
3760         return QWindowsStyle::standardIcon(standardIcon, option, widget);
3761     }
3762 
3763     QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
3764     switch(standardIcon) {
3765     case SP_TitleBarMaxButton:
3766         if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3767         {
3768             if (d->dockFloat.isNull()) {
3769                 XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3770                                       WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3771                 XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3772                                   WP_MAXBUTTON, MAXBS_NORMAL);
3773                 if (theme.isValid()) {
3774                     const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
3775                     QPixmap pm(size);
3776                     pm.fill(Qt::transparent);
3777                     QPainter p(&pm);
3778                     theme.painter = &p;
3779                     theme.rect = QRect(QPoint(0, 0), size);
3780                     d->drawBackground(theme);
3781                     d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off);    // Normal
3782                     pm.fill(Qt::transparent);
3783                     theme.stateId = MAXBS_PUSHED;
3784                     d->drawBackground(theme);
3785                     d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On);     // Pressed
3786                     pm.fill(Qt::transparent);
3787                     theme.stateId = MAXBS_HOT;
3788                     d->drawBackground(theme);
3789                     d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off);    // Hover
3790                     pm.fill(Qt::transparent);
3791                     theme.stateId = MAXBS_INACTIVE;
3792                     d->drawBackground(theme);
3793                     d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off);  // Disabled
3794                 }
3795             }
3796             if (widget && widget->isWindow())
3797                 return d->dockFloat;
3798 
3799         }
3800         break;
3801     case SP_TitleBarCloseButton:
3802         if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3803         {
3804             if (d->dockClose.isNull()) {
3805                 XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3806                                   WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3807                 if (theme.isValid()) {
3808                     const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
3809                     QPixmap pm(size);
3810                     pm.fill(Qt::transparent);
3811                     QPainter p(&pm);
3812                     theme.painter = &p;
3813                     theme.partId = WP_CLOSEBUTTON; // ####
3814                     theme.rect = QRect(QPoint(0, 0), size);
3815                     d->drawBackground(theme);
3816                     d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::Off);    // Normal
3817                     pm.fill(Qt::transparent);
3818                     theme.stateId = CBS_PUSHED;
3819                     d->drawBackground(theme);
3820                     d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::On);     // Pressed
3821                     pm.fill(Qt::transparent);
3822                     theme.stateId = CBS_HOT;
3823                     d->drawBackground(theme);
3824                     d->dockClose.addPixmap(pm, QIcon::Active, QIcon::Off);    // Hover
3825                     pm.fill(Qt::transparent);
3826                     theme.stateId = CBS_INACTIVE;
3827                     d->drawBackground(theme);
3828                     d->dockClose.addPixmap(pm, QIcon::Disabled, QIcon::Off);  // Disabled
3829                 }
3830             }
3831             if (widget && widget->isWindow())
3832                 return d->dockClose;
3833         }
3834         break;
3835     case SP_TitleBarNormalButton:
3836         if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3837         {
3838             if (d->dockFloat.isNull()) {
3839                 XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3840                                       WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3841                 XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3842                                   WP_RESTOREBUTTON, RBS_NORMAL);
3843                 if (theme.isValid()) {
3844                     const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
3845                     QPixmap pm(size);
3846                     pm.fill(Qt::transparent);
3847                     QPainter p(&pm);
3848                     theme.painter = &p;
3849                     theme.rect = QRect(QPoint(0, 0), size);
3850                     d->drawBackground(theme);
3851                     d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off);    // Normal
3852                     pm.fill(Qt::transparent);
3853                     theme.stateId = RBS_PUSHED;
3854                     d->drawBackground(theme);
3855                     d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On);     // Pressed
3856                     pm.fill(Qt::transparent);
3857                     theme.stateId = RBS_HOT;
3858                     d->drawBackground(theme);
3859                     d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off);    // Hover
3860                     pm.fill(Qt::transparent);
3861                     theme.stateId = RBS_INACTIVE;
3862                     d->drawBackground(theme);
3863                     d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off);  // Disabled
3864                 }
3865             }
3866             if (widget && widget->isWindow())
3867                 return d->dockFloat;
3868 
3869         }
3870         break;
3871     default:
3872         break;
3873     }
3874 
3875     return QWindowsStyle::standardIcon(standardIcon, option, widget);
3876 }
3877 
3878 /*!
3879     \internal
3880 
3881     Constructs a QWindowsXPStyle object.
3882 */
QWindowsXPStyle(QWindowsXPStylePrivate & dd)3883 QWindowsXPStyle::QWindowsXPStyle(QWindowsXPStylePrivate &dd) : QWindowsStyle(dd)
3884 {
3885 }
3886 
3887 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug d,const XPThemeData & t)3888 QDebug operator<<(QDebug d, const XPThemeData &t)
3889 {
3890     QDebugStateSaver saver(d);
3891     d.nospace();
3892     d << "XPThemeData(" << t.widget << ", theme=#" << t.theme << ", " << t.htheme
3893         << ", partId=" << t.partId << ", stateId=" << t.stateId << ", rect=" << t.rect
3894         << ", mirrorHorizontally=" << t.mirrorHorizontally << ", mirrorVertically="
3895         << t.mirrorVertically << ", noBorder=" << t.noBorder << ", noContent=" << t.noContent
3896         << ", rotate=" << t.rotate << ')';
3897     return d;
3898 }
3899 
operator <<(QDebug d,const ThemeMapKey & k)3900 QDebug operator<<(QDebug d, const ThemeMapKey &k)
3901 {
3902     QDebugStateSaver saver(d);
3903     d.nospace();
3904     d << "ThemeMapKey(theme=#" << k.theme
3905         << ", partId=" << k.partId << ", stateId=" << k.stateId
3906         << ", noBorder=" << k.noBorder << ", noContent=" << k.noContent << ')';
3907     return d;
3908 }
3909 
operator <<(QDebug d,const ThemeMapData & td)3910 QDebug operator<<(QDebug d, const ThemeMapData &td)
3911 {
3912     QDebugStateSaver saver(d);
3913     d.nospace();
3914     d << "ThemeMapData(alphaType=" << td.alphaType
3915       << ", dataValid=" << td.dataValid << ", partIsTransparent=" << td.partIsTransparent
3916       << ", hasAlphaChannel=" << td.hasAlphaChannel << ", wasAlphaSwapped=" << td.wasAlphaSwapped
3917       << ", hadInvalidAlpha=" << td.hadInvalidAlpha << ')';
3918     return d;
3919 }
3920 #endif // QT_NO_DEBUG_STREAM
3921 
3922 // Debugging code ---------------------------------------------------------------------[ START ]---
3923 // The code for this point on is not compiled by default, but only used as assisting
3924 // debugging code when you uncomment the DEBUG_XP_STYLE define at the top of the file.
3925 
3926 #ifdef DEBUG_XP_STYLE
3927 // The schema file expects these to be defined by the user.
3928 #define TMT_ENUMDEF 8
3929 #define TMT_ENUMVAL TEXT('A')
3930 #define TMT_ENUM    TEXT('B')
3931 #define SCHEMA_STRINGS // For 2nd pass on schema file
3932 QT_BEGIN_INCLUDE_NAMESPACE
3933 #include <tmschema.h>
3934 QT_END_INCLUDE_NAMESPACE
3935 
3936 // A property's value, type and name combo
3937 struct PropPair {
3938     int propValue;
3939     int propType;
3940     LPCWSTR propName;
3941 };
3942 
3943 // Operator for sorting of PropPairs
operator <(PropPair a,PropPair b)3944 bool operator<(PropPair a, PropPair b) {
3945     return wcscmp(a.propName, b.propName) < 0;
3946 }
3947 
3948 // Our list of all possible properties
3949 static QList<PropPair> all_props;
3950 
3951 
3952 /*! \internal
3953     Dumps a portion of the full native DIB section double buffer.
3954     The DIB section double buffer is only used when doing special
3955     transformations to the theme part, or when the real double
3956     buffer in the paintengine does not have an HDC we may use
3957     directly.
3958     Since we cannot rely on the pixel data we get from Microsoft
3959     when drawing into the DIB section, we use this function to
3960     see the actual data we got, and can determin the appropriate
3961     action.
3962 */
dumpNativeDIB(int w,int h)3963 void QWindowsXPStylePrivate::dumpNativeDIB(int w, int h)
3964 {
3965     if (w && h) {
3966         static int pCount = 0;
3967         DWORD *bufPix = (DWORD*)bufferPixels;
3968 
3969         char *bufferDump = new char[bufferH * bufferW * 16];
3970         char *bufferPos = bufferDump;
3971 
3972         memset(bufferDump, 0, sizeof(bufferDump));
3973         bufferPos += sprintf(bufferPos, "const int pixelBufferW%d = %d;\n", pCount, w);
3974         bufferPos += sprintf(bufferPos, "const int pixelBufferH%d = %d;\n", pCount, h);
3975         bufferPos += sprintf(bufferPos, "const unsigned DWORD pixelBuffer%d[] = {", pCount);
3976         for (int iy = 0; iy < h; ++iy) {
3977             bufferPos += sprintf(bufferPos, "\n    ");
3978             bufPix = (DWORD*)(bufferPixels + (iy * bufferW * 4));
3979             for (int ix = 0; ix < w; ++ix) {
3980                 bufferPos += sprintf(bufferPos, "0x%08x, ", *bufPix);
3981                 ++bufPix;
3982             }
3983         }
3984         bufferPos += sprintf(bufferPos, "\n};\n\n");
3985         printf(bufferDump);
3986 
3987         delete[] bufferDump;
3988         ++pCount;
3989     }
3990 }
3991 
3992 /*! \internal
3993     Shows the value of a given property for a part.
3994 */
showProperty(XPThemeData & themeData,const PropPair & prop)3995 static void showProperty(XPThemeData &themeData, const PropPair &prop)
3996 {
3997     PROPERTYORIGIN origin = PO_NOTFOUND;
3998     GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
3999     const char *originStr;
4000     switch(origin) {
4001     case PO_STATE:
4002         originStr = "State ";
4003         break;
4004     case PO_PART:
4005         originStr = "Part  ";
4006         break;
4007     case PO_CLASS:
4008         originStr = "Class ";
4009         break;
4010     case PO_GLOBAL:
4011         originStr = "Globl ";
4012         break;
4013     case PO_NOTFOUND:
4014     default:
4015         originStr = "Unkwn ";
4016         break;
4017     }
4018 
4019     switch(prop.propType) {
4020     case TMT_STRING:
4021         {
4022             wchar_t buffer[512];
4023             GetThemeString(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512);
4024             printf("  (%sString)  %-20S: %S\n", originStr, prop.propName, buffer);
4025         }
4026         break;
4027     case TMT_ENUM:
4028         {
4029             int result = -1;
4030             GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4031             printf("  (%sEnum)    %-20S: %d\n", originStr, prop.propName, result);
4032         }
4033         break;
4034     case TMT_INT:
4035         {
4036             int result = -1;
4037             GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4038             printf("  (%sint)     %-20S: %d\n", originStr, prop.propName, result);
4039         }
4040         break;
4041     case TMT_BOOL:
4042         {
4043             BOOL result = false;
4044             GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4045             printf("  (%sbool)    %-20S: %d\n", originStr, prop.propName, result);
4046         }
4047         break;
4048     case TMT_COLOR:
4049         {
4050             COLORREF result = 0;
4051             GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4052             printf("  (%scolor)   %-20S: 0x%08X\n", originStr, prop.propName, result);
4053         }
4054         break;
4055     case TMT_MARGINS:
4056         {
4057             MARGINS result;
4058             memset(&result, 0, sizeof(result));
4059             GetThemeMargins(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, 0, &result);
4060             printf("  (%smargins) %-20S: (%d, %d, %d, %d)\n", originStr,
4061                    prop.propName, result.cxLeftWidth, result.cyTopHeight, result.cxRightWidth, result.cyBottomHeight);
4062         }
4063         break;
4064     case TMT_FILENAME:
4065         {
4066             wchar_t buffer[512];
4067             GetThemeFilename(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512);
4068             printf("  (%sfilename)%-20S: %S\n", originStr, prop.propName, buffer);
4069         }
4070         break;
4071     case TMT_SIZE:
4072         {
4073             SIZE result1;
4074             SIZE result2;
4075             SIZE result3;
4076             memset(&result1, 0, sizeof(result1));
4077             memset(&result2, 0, sizeof(result2));
4078             memset(&result3, 0, sizeof(result3));
4079             GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_MIN,  &result1);
4080             GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_TRUE, &result2);
4081             GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_DRAW, &result3);
4082             printf("  (%ssize)    %-20S: Min (%d, %d),  True(%d, %d),  Draw(%d, %d)\n", originStr, prop.propName,
4083                    result1.cx, result1.cy, result2.cx, result2.cy, result3.cx, result3.cy);
4084         }
4085         break;
4086     case TMT_POSITION:
4087         {
4088             POINT result;
4089             memset(&result, 0, sizeof(result));
4090             GetThemePosition(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4091             printf("  (%sPosition)%-20S: (%d, %d)\n", originStr, prop.propName, result.x, result.y);
4092         }
4093         break;
4094     case TMT_RECT:
4095         {
4096             RECT result;
4097             memset(&result, 0, sizeof(result));
4098             GetThemeRect(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4099             printf("  (%sRect)    %-20S: (%d, %d, %d, %d)\n", originStr, prop.propName, result.left, result.top, result.right, result.bottom);
4100         }
4101         break;
4102     case TMT_FONT:
4103         {
4104             LOGFONT result;
4105             memset(&result, 0, sizeof(result));
4106             GetThemeFont(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, &result);
4107             printf("  (%sFont)    %-20S: %S  height(%d)  width(%d)  weight(%d)\n", originStr, prop.propName,
4108                    result.lfFaceName, result.lfHeight, result.lfWidth, result.lfWeight);
4109         }
4110         break;
4111     case TMT_INTLIST:
4112         {
4113             INTLIST result;
4114             memset(&result, 0, sizeof(result));
4115             GetThemeIntList(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result);
4116             printf("  (%sInt list)%-20S: { ", originStr, prop.propName);
4117             for (int i = 0; i < result.iValueCount; ++i)
4118                 printf("%d ", result.iValues[i]);
4119             printf("}\n");
4120         }
4121         break;
4122     default:
4123         printf("    %s%S : Unknown property type (%d)!\n", originStr, prop.propName, prop.propType);
4124     }
4125 }
4126 
4127 /*! \internal
4128     Dump all valid properties for a part.
4129     If it's the first time this function is called, then the name,
4130     enum value and documentation of all properties are shown, as
4131     well as all global properties.
4132 */
showProperties(XPThemeData & themeData)4133 void QWindowsXPStylePrivate::showProperties(XPThemeData &themeData)
4134 {
4135     if (!all_props.count()) {
4136         const TMSCHEMAINFO *infoTable = GetSchemaInfo();
4137         for (int i = 0; i < infoTable->iPropCount; ++i) {
4138             int propType  = infoTable->pPropTable[i].bPrimVal;
4139             int propValue = infoTable->pPropTable[i].sEnumVal;
4140             LPCWSTR propName = infoTable->pPropTable[i].pszName;
4141 
4142             switch(propType) {
4143             case TMT_ENUMDEF:
4144             case TMT_ENUMVAL:
4145                 continue;
4146             default:
4147                 if (propType != propValue) {
4148                     PropPair prop;
4149                     prop.propValue = propValue;
4150                     prop.propName  = propName;
4151                     prop.propType  = propType;
4152                     all_props.append(prop);
4153                 }
4154             }
4155         }
4156         std::sort(all_props.begin(), all_props.end());
4157 
4158         {// List all properties
4159             printf("part properties count = %d:\n", all_props.count());
4160             printf("      Enum  Property Name        Description\n");
4161             printf("-----------------------------------------------------------\n");
4162             wchar_t themeName[256];
4163             pGetCurrentThemeName(themeName, 256, 0, 0, 0, 0);
4164             for (int j = 0; j < all_props.count(); ++j) {
4165                 PropPair prop = all_props.at(j);
4166                 wchar_t buf[500];
4167                 GetThemeDocumentationProperty(themeName, prop.propName, buf, 500);
4168                 printf("%3d: (%4d) %-20S %S\n", j, prop.propValue, prop.propName, buf);
4169             }
4170         }
4171 
4172         {// Show Global values
4173             printf("Global Properties:\n");
4174             for (int j = 0; j < all_props.count(); ++j) {
4175                 PropPair prop = all_props.at(j);
4176                 PROPERTYORIGIN origin = PO_NOTFOUND;
4177                 GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
4178                 if (origin == PO_GLOBAL) {
4179                     showProperty(themeData, prop);
4180                 }
4181             }
4182         }
4183     }
4184 
4185     for (int j = 0; j < all_props.count(); ++j) {
4186         PropPair prop = all_props.at(j);
4187         PROPERTYORIGIN origin = PO_NOTFOUND;
4188         GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin);
4189         if (origin != PO_NOTFOUND)
4190         {
4191             showProperty(themeData, prop);
4192         }
4193     }
4194 }
4195 #endif
4196 // Debugging code -----------------------------------------------------------------------[ END ]---
4197 
4198 
4199 QT_END_NAMESPACE
4200