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