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 \image qwindowsxpstyle.png
1012 \sa QMacStyle, QWindowsStyle, QFusionStyle
1013*/
1014
1015/*!
1016 \internal
1017 Constructs a QWindowsStyle
1018*/
1019QWindowsXPStyle::QWindowsXPStyle()
1020 : QWindowsStyle(*new QWindowsXPStylePrivate)
1021{
1022}
1023
1024/*!
1025 \internal
1026 Destroys the style.
1027*/
1028QWindowsXPStyle::~QWindowsXPStyle() = default;
1029
1030/*!
1031 \internal
1032 \reimp
1033 */
1034QRect QWindowsXPStyle::subElementRect(SubElement sr, const QStyleOption *option) const
1035{
1037 return QWindowsStyle::subElementRect(sr, option);
1038 }
1039
1040 QRect rect(option->rect);
1041 switch (sr) {
1042 case SE_DockWidgetCloseButton:
1043 case SE_DockWidgetFloatButton:
1044 rect = QWindowsStyle::subElementRect(sr, option);
1045 return rect.translated(0, 1);
1046 break;
1047#if 0
1048 case SE_TabWidgetTabContents:
1049 if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))
1050 {
1051 rect = QWindowsStyle::subElementRect(sr, option);
1052 if (sr == SE_TabWidgetTabContents) {
1053 if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) {
1054 if (tabWidget->documentMode())
1055 break;
1056 }
1057
1058 rect.adjust(0, 0, -2, -2);
1059 }
1060 }
1061 break;
1062 case SE_TabWidgetTabBar: {
1063 rect = QWindowsStyle::subElementRect(sr, option);
1064 const QStyleOptionTabWidgetFrame *twfOption =
1065 qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option);
1066 if (twfOption && twfOption->direction == Qt::RightToLeft
1067 && (twfOption->shape == QTabBar::RoundedNorth
1068 || twfOption->shape == QTabBar::RoundedSouth))
1069 {
1070 QStyleOptionTab otherOption;
1071 otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth
1072 ? QTabBar::RoundedEast : QTabBar::RoundedSouth);
1073 int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption);
1074 int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option);
1075 rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0);
1076 }
1077 break;}
1078#endif
1079 case SE_PushButtonContents:
1080 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) {
1081 MARGINS borderSize;
1082 if (option->window) {
1083 XPThemeData buttontheme(option->window, nullptr, QWindowsXPStylePrivate::ButtonTheme);
1084 HTHEME theme = buttontheme.handle();
1085 if (theme) {
1086 int stateId;
1087 if (!(option->state & State_Enabled))
1088 stateId = PBS_DISABLED;
1089 else if (option->state & State_Sunken)
1090 stateId = PBS_PRESSED;
1091 else if (option->state & State_MouseOver)
1092 stateId = PBS_HOT;
1093 else if (btn->features & QStyleOptionButton::DefaultButton)
1094 stateId = PBS_DEFAULTED;
1095 else
1096 stateId = PBS_NORMAL;
1097
1098 int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn);
1099 rect = option->rect.adjusted(border, border, -border, -border);
1100
1101 if (SUCCEEDED(GetThemeMargins(theme, nullptr, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, nullptr, &borderSize))) {
1102 rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight,
1103 -borderSize.cxRightWidth, -borderSize.cyBottomHeight);
1104 rect = visualRect(option->direction, option->rect, rect);
1105 }
1106 }
1107 }
1108 }
1109 break;
1110 case SE_ProgressBarContents:
1111 rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option);
1112 if (option->state & QStyle::State_Horizontal)
1113 rect.adjust(4, 3, -4, -3);
1114 else
1115 rect.adjust(3, 2, -3, -2);
1116 break;
1117 default:
1118 rect = QWindowsStyle::subElementRect(sr, option);
1119 }
1120 return rect;
1121}
1122
1123/*!
1124 \internal
1125 \reimp
1126*/
1127void QWindowsXPStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p) const
1128{
1129 QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
1130
1132 QWindowsStyle::drawPrimitive(pe, option, p);
1133 return;
1134 }
1135
1136 int themeNumber = -1;
1137 int partId = 0;
1138 int stateId = 0;
1139 QRect rect = option->rect;
1140 State flags = option->state;
1141 bool hMirrored = false;
1142 bool vMirrored = false;
1143 bool noBorder = false;
1144 bool noContent = false;
1145 int rotate = 0;
1146
1147 switch (pe) {
1148#if 0
1149 case PE_FrameTabBarBase:
1150 if (const QStyleOptionTabBarBase *tbb
1151 = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) {
1152 p->save();
1153 switch (tbb->shape) {
1154 case QTabBar::RoundedNorth:
1155 p->setPen(QPen(tbb->palette.dark(), 0));
1156 p->drawLine(tbb->rect.topLeft(), tbb->rect.topRight());
1157 break;
1158 case QTabBar::RoundedWest:
1159 p->setPen(QPen(tbb->palette.dark(), 0));
1160 p->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom());
1161 break;
1162 case QTabBar::RoundedSouth:
1163 p->setPen(QPen(tbb->palette.dark(), 0));
1164 p->drawLine(tbb->rect.left(), tbb->rect.top(),
1165 tbb->rect.right(), tbb->rect.top());
1166 break;
1167 case QTabBar::RoundedEast:
1168 p->setPen(QPen(tbb->palette.dark(), 0));
1169 p->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft());
1170 break;
1171 case QTabBar::TriangularNorth:
1172 case QTabBar::TriangularEast:
1173 case QTabBar::TriangularWest:
1174 case QTabBar::TriangularSouth:
1175 p->restore();
1176 QWindowsStyle::drawPrimitive(pe, option, p);
1177 return;
1178 }
1179 p->restore();
1180 }
1181 return;
1182#endif
1183 case PE_PanelButtonBevel:
1184 themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1185 partId = BP_PUSHBUTTON;
1186 if (!(flags & State_Enabled))
1187 stateId = PBS_DISABLED;
1188 else if ((flags & State_Sunken) || (flags & State_On))
1189 stateId = PBS_PRESSED;
1190 else if (flags & State_MouseOver)
1191 stateId = PBS_HOT;
1192 //else if (flags & State_ButtonDefault)
1193 // stateId = PBS_DEFAULTED;
1194 else
1195 stateId = PBS_NORMAL;
1196 break;
1197
1198 case PE_PanelButtonTool:
1199// if (widget && widget->inherits("QDockWidgetTitleButton")) {
1200// if (const QWidget *dw = widget->parentWidget())
1201// if (dw->isWindow())
1202// return;
1203// }
1204 themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
1205 partId = TP_BUTTON;
1206 if (!(flags & State_Enabled))
1207 stateId = TS_DISABLED;
1208 else if (flags & State_Sunken)
1209 stateId = TS_PRESSED;
1210 else if (flags & State_MouseOver)
1211 stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
1212 else if (flags & State_On)
1213 stateId = TS_CHECKED;
1214 else if (!(flags & State_AutoRaise))
1215 stateId = TS_HOT;
1216 else
1217 stateId = TS_NORMAL;
1218 break;
1219
1220 case PE_IndicatorButtonDropDown:
1221 themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
1222 partId = TP_SPLITBUTTONDROPDOWN;
1223 if (!(flags & State_Enabled))
1224 stateId = TS_DISABLED;
1225 else if (flags & State_Sunken)
1226 stateId = TS_PRESSED;
1227 else if (flags & State_MouseOver)
1228 stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
1229 else if (flags & State_On)
1230 stateId = TS_CHECKED;
1231 else if (!(flags & State_AutoRaise))
1232 stateId = TS_HOT;
1233 else
1234 stateId = TS_NORMAL;
1235 if (option->direction == Qt::RightToLeft)
1236 hMirrored = true;
1237 break;
1238
1239 case PE_IndicatorCheckBox:
1240 themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1241 partId = BP_CHECKBOX;
1242 if (!(flags & State_Enabled))
1243 stateId = CBS_UNCHECKEDDISABLED;
1244 else if (flags & State_Sunken)
1245 stateId = CBS_UNCHECKEDPRESSED;
1246 else if (flags & State_MouseOver)
1247 stateId = CBS_UNCHECKEDHOT;
1248 else
1249 stateId = CBS_UNCHECKEDNORMAL;
1250
1251 if (flags & State_On)
1252 stateId += CBS_CHECKEDNORMAL-1;
1253 else if (flags & State_NoChange)
1254 stateId += CBS_MIXEDNORMAL-1;
1255
1256 break;
1257
1258 case PE_IndicatorRadioButton:
1259 themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1260 partId = BP_RADIOBUTTON;
1261 if (!(flags & State_Enabled))
1262 stateId = RBS_UNCHECKEDDISABLED;
1263 else if (flags & State_Sunken)
1264 stateId = RBS_UNCHECKEDPRESSED;
1265 else if (flags & State_MouseOver)
1266 stateId = RBS_UNCHECKEDHOT;
1267 else
1268 stateId = RBS_UNCHECKEDNORMAL;
1269
1270 if (flags & State_On)
1271 stateId += RBS_CHECKEDNORMAL-1;
1272 break;
1273
1274 case PE_IndicatorDockWidgetResizeHandle:
1275 return;
1276
1277case PE_Frame:
1278 {
1279 if (flags & State_Raised)
1280 return;
1281 themeNumber = QWindowsXPStylePrivate::ListViewTheme;
1282 partId = LVP_LISTGROUP;
1283 XPThemeData theme(option->window, nullptr, themeNumber, partId);
1284
1285 if (!(flags & State_Enabled))
1286 stateId = ETS_DISABLED;
1287 else
1288 stateId = ETS_NORMAL;
1289 int fillType;
1290 if (GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) {
1291 if (fillType == BT_BORDERFILL) {
1292 COLORREF bcRef;
1293 GetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef);
1294 QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef)));
1295 QPen oldPen = p->pen();
1296 // int borderSize = 1;
1297 // GetThemeInt(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &borderSize);
1298
1299 // Inner white border
1300 p->setPen(QPen(option->palette.base().color(), 0));
1301 const qreal dpi = QStyleHelper::dpi(option);
1302 const auto topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi);
1303 const auto bottomRightAdjustment = QStyleHelper::dpiScaled(-1, dpi);
1304 p->drawRect(QRectF(option->rect).adjusted(topLevelAdjustment, topLevelAdjustment,
1305 bottomRightAdjustment, bottomRightAdjustment));
1306 // Outer dark border
1307 p->setPen(QPen(bordercolor, 0));
1308 p->drawRect(QRectF(option->rect).adjusted(0, 0, -topLevelAdjustment, -topLevelAdjustment));
1309 p->setPen(oldPen);
1310 return;
1311 }
1312 if (fillType == BT_NONE)
1313 return;
1314 }
1315 break;
1316 }
1317 case PE_FrameLineEdit: {
1318 // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class.
1319 /*
1320 if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) {
1321 QPen oldPen = p->pen();
1322 // Inner white border
1323 p->setPen(QPen(option->palette.base().color(), 1));
1324 p->drawRect(option->rect.adjusted(1, 1, -2, -2));
1325 // Outer dark border
1326 p->setPen(QPen(option->palette.shadow().color(), 1));
1327 p->drawRect(option->rect.adjusted(0, 0, -1, -1));
1328 p->setPen(oldPen);
1329 return;
1330 }
1331 */
1332 if (qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1333 themeNumber = QWindowsXPStylePrivate::EditTheme;
1334 partId = EP_EDITTEXT;
1335 noContent = true;
1336 if (!(flags & State_Enabled))
1337 stateId = ETS_DISABLED;
1338 else
1339 stateId = ETS_NORMAL;
1340 }
1341 break;
1342 }
1343
1344 case PE_PanelLineEdit:
1345 if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1346 themeNumber = QWindowsXPStylePrivate::EditTheme;
1347 partId = EP_EDITTEXT;
1348 noBorder = true;
1349 bool isEnabled = flags & State_Enabled;
1350
1351 stateId = isEnabled ? ETS_NORMAL : ETS_DISABLED;
1352
1353 /*if (QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget)) {
1354 p->fillRect(panel->rect, panel->palette.brush(QPalette::Base));
1355 } else*/{
1356 XPThemeData theme(nullptr, p, themeNumber, partId, stateId, rect);
1357 if (!theme.isValid()) {
1358 QWindowsStyle::drawPrimitive(pe, option, p);
1359 return;
1360 }
1361 int bgType;
1362 GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType);
1363 if ( bgType == BT_IMAGEFILE ) {
1364 theme.mirrorHorizontally = hMirrored;
1365 theme.mirrorVertically = vMirrored;
1366 theme.noBorder = noBorder;
1367 theme.noContent = noContent;
1368 theme.rotate = rotate;
1369 d->drawBackground(theme);
1370 } else {
1371 QBrush fillColor = option->palette.brush(QPalette::Base);
1372
1373 if (!isEnabled) {
1374 PROPERTYORIGIN origin = PO_NOTFOUND;
1375 GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin);
1376 // Use only if the fill property comes from our part
1377 if ((origin == PO_PART || origin == PO_STATE)) {
1378 COLORREF bgRef;
1379 GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef);
1380 fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef)));
1381 }
1382 }
1383 p->fillRect(option->rect, fillColor);
1384 }
1385 }
1386
1387 if (panel->lineWidth > 0)
1388 proxy()->drawPrimitive(PE_FrameLineEdit, panel, p);
1389 return;
1390 }
1391 break;
1392#if 0
1393 case PE_FrameTabWidget:
1394 if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option))
1395 {
1396 themeNumber = QWindowsXPStylePrivate::TabTheme;
1397 partId = TABP_PANE;
1398
1399 if (option->window) {
1400 bool useGradient = true;
1401 const int maxlength = 256;
1402 wchar_t themeFileName[maxlength];
1403 wchar_t themeColor[maxlength];
1404 // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it
1405 if (GetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, nullptr, 0) == S_OK) {
1406 wchar_t *offset = nullptr;
1407 if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != nullptr) {
1408 offset++;
1409 if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic")) {
1410 useGradient = false;
1411 }
1412 }
1413 }
1414 // This should work, but currently there's an error in the ::drawBackgroundDirectly()
1415 // code, when using the HDC directly..
1416 if (useGradient) {
1417 QStyleOptionTabWidgetFrame frameOpt = *tab;
1418 //frameOpt.rect = widget->rect();
1419
1420 QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt);
1421 QRegion reg = option->rect;
1422 reg -= contentsRect;
1423 p->setClipRegion(reg);
1424 XPThemeData theme(option->window, p, themeNumber, partId, stateId, rect);
1425 theme.mirrorHorizontally = hMirrored;
1426 theme.mirrorVertically = vMirrored;
1427 d->drawBackground(theme);
1428 p->setClipRect(contentsRect);
1429 partId = TABP_BODY;
1430 }
1431 }
1432 switch (tab->shape) {
1433 case QTabBar::RoundedNorth:
1434 case QTabBar::TriangularNorth:
1435 break;
1436 case QTabBar::RoundedSouth:
1437 case QTabBar::TriangularSouth:
1438 vMirrored = true;
1439 break;
1440 case QTabBar::RoundedEast:
1441 case QTabBar::TriangularEast:
1442 rotate = 90;
1443 break;
1444 case QTabBar::RoundedWest:
1445 case QTabBar::TriangularWest:
1446 rotate = 90;
1447 hMirrored = true;
1448 break;
1449 default:
1450 break;
1451 }
1452 }
1453 break;
1454#endif
1455 case PE_FrameMenu:
1456 p->save();
1457 p->setPen(option->palette.dark().color());
1458 p->drawRect(rect.adjusted(0, 0, -1, -1));
1459 p->restore();
1460 return;
1461
1462 case PE_PanelMenuBar:
1463 break;
1464
1465 case PE_FrameDockWidget:
1466 if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option))
1467 {
1468 themeNumber = QWindowsXPStylePrivate::WindowTheme;
1469 if (flags & State_Active)
1470 stateId = FS_ACTIVE;
1471 else
1472 stateId = FS_INACTIVE;
1473
1474 int fwidth = proxy()->pixelMetric(PM_DockWidgetFrameWidth, frm);
1475
1476 XPThemeData theme(option->window, p, themeNumber, 0, stateId);
1477 if (!theme.isValid())
1478 break;
1479 theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth); theme.partId = WP_SMALLFRAMELEFT;
1480 d->drawBackground(theme);
1481 theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth);
1482 theme.partId = WP_SMALLFRAMERIGHT;
1483 d->drawBackground(theme);
1484 theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth);
1485 theme.partId = WP_SMALLFRAMEBOTTOM;
1486 d->drawBackground(theme);
1487 return;
1488 }
1489 break;
1490
1491 case PE_IndicatorHeaderArrow:
1492 {
1493#if 0 // XP theme engine doesn't know about this :(
1494 name = QWindowsXPStylePrivate::HeaderTheme;
1495 partId = HP_HEADERSORTARROW;
1496 if (flags & State_Down)
1497 stateId = HSAS_SORTEDDOWN;
1498 else
1499 stateId = HSAS_SORTEDUP;
1500#else
1501 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
1502 p->save();
1503 p->setPen(option->palette.dark().color());
1504 p->translate(0, option->rect.height()/2 - 4);
1505 if (header->sortIndicator & QStyleOptionHeader::SortUp) { // invert logic to follow Windows style guide
1506 p->drawLine(option->rect.x(), option->rect.y(), option->rect.x()+8, option->rect.y());
1507 p->drawLine(option->rect.x()+1, option->rect.y()+1, option->rect.x()+7, option->rect.y()+1);
1508 p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2);
1509 p->drawLine(option->rect.x()+3, option->rect.y()+3, option->rect.x()+5, option->rect.y()+3);
1510 p->drawPoint(option->rect.x()+4, option->rect.y()+4);
1511 } else if (header->sortIndicator & QStyleOptionHeader::SortDown) {
1512 p->drawLine(option->rect.x(), option->rect.y()+4, option->rect.x()+8, option->rect.y()+4);
1513 p->drawLine(option->rect.x()+1, option->rect.y()+3, option->rect.x()+7, option->rect.y()+3);
1514 p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2);
1515 p->drawLine(option->rect.x()+3, option->rect.y()+1, option->rect.x()+5, option->rect.y()+1);
1516 p->drawPoint(option->rect.x()+4, option->rect.y());
1517 }
1518 p->restore();
1519 return;
1520 }
1521#endif
1522 }
1523 break;
1524
1525 case PE_FrameStatusBarItem:
1526 themeNumber = QWindowsXPStylePrivate::StatusTheme;
1527 partId = SP_PANE;
1528 break;
1529
1530 case PE_FrameGroupBox:
1531 themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1532 partId = BP_GROUPBOX;
1533 if (!(flags & State_Enabled))
1534 stateId = GBS_DISABLED;
1535 else
1536 stateId = GBS_NORMAL;
1537 if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
1538 if (frame->features & QStyleOptionFrame::Flat) {
1539 // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style
1540 QRect fr = frame->rect;
1541 QPoint p1(fr.x(), fr.y() + 1);
1542 QPoint p2(fr.x() + fr.width(), p1.y() + 1);
1543 rect = QRect(p1, p2);
1544 themeNumber = -1;
1545 }
1546 }
1547 break;
1548
1549 case PE_IndicatorProgressChunk:
1550 {
1551 Qt::Orientation orient = Qt::Horizontal;
1552 bool inverted = false;
1553 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
1554 orient = pb->state & QStyle::State_Horizontal ? Qt::Horizontal : Qt::Vertical;
1555 inverted = pb->invertedAppearance;
1556 }
1557 if (orient == Qt::Horizontal) {
1558 partId = PP_CHUNK;
1559 rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height() );
1560 if (inverted && option->direction == Qt::LeftToRight)
1561 hMirrored = true;
1562 } else {
1563 partId = PP_CHUNKVERT;
1564 rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height());
1565 }
1566 themeNumber = QWindowsXPStylePrivate::ProgressTheme;
1567 stateId = 1;
1568 }
1569 break;
1570
1571 case PE_FrameWindow:
1572 if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option))
1573 {
1574 themeNumber = QWindowsXPStylePrivate::WindowTheme;
1575 if (flags & State_Active)
1576 stateId = FS_ACTIVE;
1577 else
1578 stateId = FS_INACTIVE;
1579
1580 int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(option));
1581
1582 XPThemeData theme(option->window, p, themeNumber, 0, stateId);
1583 if (!theme.isValid())
1584 break;
1585
1586 // May fail due to too-large buffers for large widgets, fall back to Windows style.
1587 theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth);
1588 theme.partId = WP_FRAMELEFT;
1589 if (!d->drawBackground(theme)) {
1590 QWindowsStyle::drawPrimitive(pe, option, p);
1591 return;
1592 }
1593 theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth);
1594 theme.partId = WP_FRAMERIGHT;
1595 if (!d->drawBackground(theme)) {
1596 QWindowsStyle::drawPrimitive(pe, option, p);
1597 return;
1598 }
1599 theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth);
1600 theme.partId = WP_FRAMEBOTTOM;
1601 if (!d->drawBackground(theme)) {
1602 QWindowsStyle::drawPrimitive(pe, option, p);
1603 return;
1604 }
1605 theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth);
1606 theme.partId = WP_CAPTION;
1607 if (!d->drawBackground(theme))
1608 QWindowsStyle::drawPrimitive(pe, option, p);
1609 return;
1610 }
1611 break;
1612
1613 case PE_IndicatorBranch:
1614 {
1615 static const int decoration_size = 9;
1616 int mid_h = option->rect.x() + option->rect.width() / 2;
1617 int mid_v = option->rect.y() + option->rect.height() / 2;
1618 int bef_h = mid_h;
1619 int bef_v = mid_v;
1620 int aft_h = mid_h;
1621 int aft_v = mid_v;
1622 QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern);
1623 if (option->state & State_Item) {
1624 if (option->direction == Qt::RightToLeft)
1625 p->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush);
1626 else
1627 p->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush);
1628 }
1629 if (option->state & State_Sibling)
1630 p->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush);
1631 if (option->state & (State_Open | State_Children | State_Item | State_Sibling))
1632 p->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush);
1633 if (option->state & State_Children) {
1634 int delta = decoration_size / 2;
1635 bef_h -= delta;
1636 bef_v -= delta;
1637 aft_h += delta;
1638 aft_v += delta;
1639 XPThemeData theme(nullptr, p, QWindowsXPStylePrivate::XpTreeViewTheme);
1640 theme.rect = QRect(bef_h, bef_v, decoration_size, decoration_size);
1641 theme.partId = TVP_GLYPH;
1642 theme.stateId = flags & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED;
1643 d->drawBackground(theme);
1644 }
1645 }
1646 return;
1647
1648 case PE_IndicatorToolBarSeparator:
1649 if (option->rect.height() < 3) {
1650 // XP style requires a few pixels for the separator
1651 // to be visible.
1652 QWindowsStyle::drawPrimitive(pe, option, p);
1653 return;
1654 }
1655 themeNumber = QWindowsXPStylePrivate::ToolBarTheme;
1656 partId = TP_SEPARATOR;
1657
1658 if (option->state & State_Horizontal)
1659 partId = TP_SEPARATOR;
1660 else
1661 partId = TP_SEPARATORVERT;
1662
1663 break;
1664
1665 case PE_IndicatorToolBarHandle:
1666
1667 themeNumber = QWindowsXPStylePrivate::RebarTheme;
1668 partId = RP_GRIPPER;
1669 if (option->state & State_Horizontal) {
1670 partId = RP_GRIPPER;
1671 rect.adjust(0, 0, -2, 0);
1672 }
1673 else {
1674 partId = RP_GRIPPERVERT;
1675 rect.adjust(0, 0, 0, -2);
1676 }
1677 break;
1678
1679 case PE_IndicatorItemViewItemCheck: {
1680 QStyleOptionButton button;
1681 button.QStyleOption::operator=(*option);
1682 button.state &= ~State_MouseOver;
1683 proxy()->drawPrimitive(PE_IndicatorCheckBox, &button, p);
1684 return;
1685 }
1686
1687 default:
1688 break;
1689 }
1690
1691 XPThemeData theme(option->window, p, themeNumber, partId, stateId, rect);
1692 if (!theme.isValid()) {
1693 QWindowsStyle::drawPrimitive(pe, option, p);
1694 return;
1695 }
1696 theme.mirrorHorizontally = hMirrored;
1697 theme.mirrorVertically = vMirrored;
1698 theme.noBorder = noBorder;
1699 theme.noContent = noContent;
1700 theme.rotate = rotate;
1701 d->drawBackground(theme);
1702}
1703
1704/*!
1705 \internal
1706 \reimp
1707*/
1708void QWindowsXPStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *p) const
1709{
1710 QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
1712 QWindowsStyle::drawControl(element, option, p);
1713 return;
1714 }
1715
1716 QRect rect(option->rect);
1717 State flags = option->state;
1718
1719 int rotate = 0;
1720 bool hMirrored = false;
1721 bool vMirrored = false;
1722
1723 int themeNumber = -1;
1724 int partId = 0;
1725 int stateId = 0;
1726 switch (element) {
1727 case CE_SizeGrip:
1728 {
1729 themeNumber = QWindowsXPStylePrivate::StatusTheme;
1730 partId = SP_GRIPPER;
1731 XPThemeData theme(nullptr, p, themeNumber, partId);
1732 QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(option)).toSize();
1733 size.rheight()--;
1734 if (const QStyleOptionSizeGrip *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(option)) {
1735 switch (sg->corner) {
1736 case Qt::BottomRightCorner:
1737 rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size);
1738 break;
1739 case Qt::BottomLeftCorner:
1740 rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size);
1741 hMirrored = true;
1742 break;
1743 case Qt::TopRightCorner:
1744 rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size);
1745 vMirrored = true;
1746 break;
1747 case Qt::TopLeftCorner:
1748 rect = QRect(rect.topLeft() + QPoint(1, 1), size);
1749 hMirrored = vMirrored = true;
1750 }
1751 }
1752 }
1753 break;
1754
1755 case CE_HeaderSection:
1756 themeNumber = QWindowsXPStylePrivate::HeaderTheme;
1757 partId = HP_HEADERITEM;
1758 if (flags & State_Sunken)
1759 stateId = HIS_PRESSED;
1760 else if (flags & State_MouseOver)
1761 stateId = HIS_HOT;
1762 else
1763 stateId = HIS_NORMAL;
1764 break;
1765
1766 case CE_Splitter:
1767 p->eraseRect(option->rect);
1768 return;
1769
1770 case CE_PushButtonBevel:
1771 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option))
1772 {
1773 themeNumber = QWindowsXPStylePrivate::ButtonTheme;
1774 partId = BP_PUSHBUTTON;
1775 bool justFlat = ((btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken)))
1776 || ((btn->features & QStyleOptionButton::CommandLinkButton)
1777 && !(flags & State_MouseOver)
1778 && !(btn->features & QStyleOptionButton::DefaultButton));
1779 if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat))
1780 stateId = PBS_DISABLED;
1781 else if (justFlat)
1782 ;
1783 else if (flags & (State_Sunken | State_On))
1784 stateId = PBS_PRESSED;
1785 else if (flags & State_MouseOver)
1786 stateId = PBS_HOT;
1787 else if (btn->features & QStyleOptionButton::DefaultButton)
1788 stateId = PBS_DEFAULTED;
1789 else
1790 stateId = PBS_NORMAL;
1791
1792 if (!justFlat) {
1793 XPThemeData theme(option->window, p, themeNumber, partId, stateId, rect);
1794 d->drawBackground(theme);
1795 }
1796
1797 if (btn->features & QStyleOptionButton::HasMenu) {
1798 int mbiw = 0, mbih = 0;
1799 XPThemeData theme(option->window, nullptr,
1800 QWindowsXPStylePrivate::ToolBarTheme,
1801 TP_SPLITBUTTONDROPDOWN);
1802 if (theme.isValid()) {
1803 const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(option)).toSize();
1804 mbiw = size.width();
1805 mbih = size.height();
1806 }
1807
1808 QRect ir = btn->rect;
1809 QStyleOptionButton newBtn = *btn;
1810 newBtn.rect = QRect(ir.right() - mbiw - 1, 1 + (ir.height()/2) - (mbih/2), mbiw, mbih);
1811 proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p);
1812 }
1813 return;
1814 }
1815 break;
1816 case CE_TabBarTab:
1817 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
1818 {
1819 stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED;
1820 }
1821 break;
1822#if 0
1823 case CE_TabBarTabShape:
1824 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option))
1825 {
1826 themeNumber = QWindowsXPStylePrivate::TabTheme;
1827 bool isDisabled = !(tab->state & State_Enabled);
1828 bool hasFocus = tab->state & State_HasFocus;
1829 bool isHot = tab->state & State_MouseOver;
1830 bool selected = tab->state & State_Selected;
1831 bool lastTab = tab->position == QStyleOptionTab::End;
1832 bool firstTab = tab->position == QStyleOptionTab::Beginning;
1833 bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab;
1834 bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab) == Qt::AlignLeft;
1835 bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab) == Qt::AlignCenter;
1836 int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option);
1837 int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option);
1838
1839 if (isDisabled)
1840 stateId = TIS_DISABLED;
1841 else if (selected)
1842 stateId = TIS_SELECTED;
1843 else if (hasFocus)
1844 stateId = TIS_FOCUSED;
1845 else if (isHot)
1846 stateId = TIS_HOT;
1847 else
1848 stateId = TIS_NORMAL;
1849
1850 // Selecting proper part depending on position
1851 if (firstTab || onlyOne) {
1852 if (leftAligned) {
1853 partId = TABP_TABITEMLEFTEDGE;
1854 } else if (centerAligned) {
1855 partId = TABP_TABITEM;
1856 } else { // rightAligned
1857 partId = TABP_TABITEMRIGHTEDGE;
1858 }
1859 } else {
1860 partId = TABP_TABITEM;
1861 }
1862
1863 if (tab->direction == Qt::RightToLeft
1864 && (tab->shape == QTabBar::RoundedNorth
1865 || tab->shape == QTabBar::RoundedSouth)) {
1866 bool temp = firstTab;
1867 firstTab = lastTab;
1868 lastTab = temp;
1869 }
1870 bool begin = firstTab || onlyOne;
1871 bool end = lastTab || onlyOne;
1872 switch (tab->shape) {
1873 case QTabBar::RoundedNorth:
1874 if (selected)
1875 rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness);
1876 else
1877 rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0);
1878 break;
1879 case QTabBar::RoundedSouth:
1880 //vMirrored = true;
1881 rotate = 180; // Not 100% correct, but works
1882 if (selected)
1883 rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0);
1884 else
1885 rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap);
1886 break;
1887 case QTabBar::RoundedEast:
1888 rotate = 90;
1889 if (selected) {
1890 rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap);
1891 }else{
1892 rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0);
1893 }
1894 break;
1895 case QTabBar::RoundedWest:
1896 hMirrored = true;
1897 rotate = 90;
1898 if (selected) {
1899 rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap);
1900 }else{
1901 rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0);
1902 }
1903 break;
1904 default:
1905 themeNumber = -1; // Do our own painting for triangular
1906 break;
1907 }
1908
1909 if (!selected) {
1910 switch (tab->shape) {
1911 case QTabBar::RoundedNorth:
1912 rect.adjust(0,0, 0,-1);
1913 break;
1914 case QTabBar::RoundedSouth:
1915 rect.adjust(0,1, 0,0);
1916 break;
1917 case QTabBar::RoundedEast:
1918 rect.adjust( 1,0, 0,0);
1919 break;
1920 case QTabBar::RoundedWest:
1921 rect.adjust(0,0, -1,0);
1922 break;
1923 default:
1924 break;
1925 }
1926 }
1927 }
1928 break;
1929#endif
1930 case CE_ProgressBarGroove:
1931 {
1932 Qt::Orientation orient = Qt::Horizontal;
1933 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
1934 orient = pb->state & QStyle::State_Horizontal ? Qt::Horizontal : Qt::Vertical;
1935 partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT;
1936 themeNumber = QWindowsXPStylePrivate::ProgressTheme;
1937 stateId = 1;
1938 }
1939 break;
1940
1941 case CE_MenuEmptyArea:
1942 case CE_MenuItem:
1943 if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
1944 {
1945 int tab = menuitem->tabWidth;
1946 bool dis = !(menuitem->state & State_Enabled);
1947 bool act = menuitem->state & State_Selected;
1948 bool checkable = menuitem->menuHasCheckableItems;
1949 bool checked = checkable ? menuitem->checked : false;
1950
1951 // windows always has a check column, regardless whether we have an icon or not
1952 int checkcol = qMax(menuitem->maxIconWidth, 12);
1953
1954 int x, y, w, h;
1955 rect.getRect(&x, &y, &w, &h);
1956
1957 QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button);
1958 p->fillRect(rect, fill);
1959
1960 if (element == CE_MenuEmptyArea)
1961 break;
1962
1963 // draw separator -------------------------------------------------
1964 if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) {
1965 int yoff = y-1 + h / 2;
1966 p->setPen(menuitem->palette.dark().color());
1967 p->drawLine(x, yoff, x+w, yoff);
1968 ++yoff;
1969 p->setPen(menuitem->palette.light().color());
1970 p->drawLine(x, yoff, x+w, yoff);
1971 return;
1972 }
1973
1974 int xpos = x;
1975
1976 // draw icon ------------------------------------------------------
1977 if (!menuitem->icon.isNull()) {
1978 QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
1979 if (act && !dis)
1980 mode = QIcon::Active;
1981 QPixmap pixmap = checked ?
1982 menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option), mode, QIcon::On) :
1983 menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option), mode);
1984 const int pixw = pixmap.width() / pixmap.devicePixelRatio();
1985 const int pixh = pixmap.height() / pixmap.devicePixelRatio();
1986 QRect iconRect(0, 0, pixw, pixh);
1987 iconRect.moveCenter(QRect(xpos, y, checkcol, h).center());
1988 QRect vIconRect = visualRect(option->direction, option->rect, iconRect);
1989 p->setPen(menuitem->palette.text().color());
1990 p->setBrush(Qt::NoBrush);
1991 if (checked)
1992 p->drawRect(vIconRect.adjusted(-1, -1, 0, 0));
1993 p->drawPixmap(vIconRect.topLeft(), pixmap);
1994
1995 // draw checkmark -------------------------------------------------
1996 } else if (checked) {
1997 QStyleOptionMenuItem newMi = *menuitem;
1998 newMi.state = State_None;
1999 if (!dis)
2000 newMi.state |= State_Enabled;
2001 if (act)
2002 newMi.state |= State_On;
2003
2004 QRect checkMarkRect = QRect(menuitem->rect.x() + windowsItemFrame,
2005 menuitem->rect.y() + windowsItemFrame,
2006 checkcol - 2 * windowsItemFrame,
2007 menuitem->rect.height() - 2*windowsItemFrame);
2008 newMi.rect = visualRect(option->direction, option->rect, checkMarkRect);
2009 proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p);
2010 }
2011
2012 QColor textColor = dis ? menuitem->palette.text().color() :
2013 act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color();
2014 p->setPen(textColor);
2015
2016 // draw text ------------------------------------------------------
2017 int xm = windowsItemFrame + checkcol + windowsItemHMargin;
2018 xpos = menuitem->rect.x() + xm;
2019 QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
2020 QRect vTextRect = visualRect(option->direction, option->rect, textRect);
2021 QString s = menuitem->text;
2022 if (!s.isEmpty()) {
2023 p->save();
2024 int t = s.indexOf(QLatin1Char('\t'));
2025 int text_flags = Qt::AlignVCenter|Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine | Qt::AlignLeft;
2026 if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem))
2027 text_flags |= Qt::TextHideMnemonic;
2028 // draw tab text ----------------
2029 if (t >= 0) {
2030 QRect vShortcutRect = visualRect(option->direction, option->rect, QRect(textRect.topRight(), menuitem->rect.bottomRight()));
2031 if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option)) {
2032 p->setPen(menuitem->palette.light().color());
2033 p->drawText(vShortcutRect.adjusted(1,1,1,1), text_flags, s.mid(t + 1));
2034 p->setPen(textColor);
2035 }
2036 p->drawText(vShortcutRect, text_flags, s.mid(t + 1));
2037 s = s.left(t);
2038 }
2039 QFont font = menuitem->font;
2040 if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem)
2041 font.setBold(true);
2042 p->setFont(font);
2043 if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option)) {
2044 p->setPen(menuitem->palette.light().color());
2045 p->drawText(vTextRect.adjusted(1,1,1,1), text_flags, s.left(t));
2046 p->setPen(textColor);
2047 }
2048 p->drawText(vTextRect, text_flags, s);
2049 p->restore();
2050 }
2051
2052 // draw sub menu arrow --------------------------------------------
2053 if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {
2054 int dim = (h - 2) / 2;
2055 PrimitiveElement arrow;
2056 arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
2057 xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
2058 QRect vSubMenuRect = visualRect(option->direction, option->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
2059 QStyleOptionMenuItem newMI = *menuitem;
2060 newMI.rect = vSubMenuRect;
2061 newMI.state = dis ? State_None : State_Enabled;
2062 if (act)
2063 newMI.palette.setColor(QPalette::ButtonText, newMI.palette.highlightedText().color());
2064 proxy()->drawPrimitive(arrow, &newMI, p);
2065 }
2066 }
2067 return;
2068
2069 case CE_MenuBarItem:
2070 if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
2071 {
2072 if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem)
2073 break;
2074
2075 bool act = mbi->state & State_Selected;
2076 bool dis = !(mbi->state & State_Enabled);
2077
2078 QBrush fill = mbi->palette.brush(act ? QPalette::Highlight : QPalette::Button);
2079 QPalette::ColorRole textRole = dis ? QPalette::Text:
2080 act ? QPalette::HighlightedText : QPalette::ButtonText;
2081 QPixmap pix = mbi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option), QIcon::Normal);
2082
2083 uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
2084 if (!proxy()->styleHint(SH_UnderlineShortcut, mbi))
2085 alignment |= Qt::TextHideMnemonic;
2086
2087 p->fillRect(rect, fill);
2088 if (!pix.isNull())
2089 drawItemPixmap(p, mbi->rect, alignment, pix);
2090 else
2091 drawItemText(p, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole);
2092 }
2093 return;
2094#if 0 && QT_CONFIG(dockwidget)
2095 case CE_DockWidgetTitle:
2096 if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option))
2097 {
2098 int buttonMargin = 4;
2099 int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt);
2100 int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt);
2101 bool isFloating = false; // widget && widget->isWindow();
2102 bool isActive = dwOpt->state & State_Active;
2103
2104 const bool verticalTitleBar = dwOpt->verticalTitleBar;
2105
2106 if (verticalTitleBar) {
2107 rect = rect.transposed();
2108
2109 p->translate(rect.left() - 1, rect.top() + rect.width());
2110 p->rotate(-90);
2111 p->translate(-rect.left() + 1, -rect.top());
2112 }
2113 QRect r = rect.adjusted(0, 2, -1, -3);
2114 QRect titleRect = r;
2115
2116 if (dwOpt->closable) {
2117 QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt).actualSize(QSize(10, 10));
2118 titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
2119 }
2120
2121 if (dwOpt->floatable) {
2122 QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt).actualSize(QSize(10, 10));
2123 titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
2124 }
2125
2126 if (isFloating) {
2127 titleRect.adjust(0, -fw, 0, 0);
2128 if (option->window && option->window->icon().cacheKey() != QApplication::windowIcon().cacheKey())
2129 titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
2130 } else {
2131 titleRect.adjust(mw, 0, 0, 0);
2132 if (!dwOpt->floatable && !dwOpt->closable)
2133 titleRect.adjust(0, 0, -mw, 0);
2134 }
2135
2136 if (!verticalTitleBar)
2137 titleRect = visualRect(dwOpt->direction, r, titleRect);
2138
2139 if (!isFloating) {
2140 QPen oldPen = p->pen();
2141 QString titleText = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
2142 p->setPen(dwOpt->palette.color(QPalette::Dark));
2143 p->drawRect(r);
2144
2145 if (!titleText.isEmpty()) {
2146 drawItemText(p, titleRect,
2147 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette,
2148 dwOpt->state & State_Enabled, titleText,
2149 QPalette::WindowText);
2150 }
2151
2152 p->setPen(oldPen);
2153 } else {
2154 themeNumber = QWindowsXPStylePrivate::WindowTheme;
2155 if (isActive)
2156 stateId = CS_ACTIVE;
2157 else
2158 stateId = CS_INACTIVE;
2159
2160 int titleHeight = rect.height() - 2;
2161 rect = rect.adjusted(-fw, -fw, fw, 0);
2162
2163 XPThemeData theme(option->window, p, themeNumber, 0, stateId);
2164 if (!theme.isValid())
2165 break;
2166
2167 // Draw small type title bar
2168 theme.rect = rect;
2169 theme.partId = WP_SMALLCAPTION;
2170 d->drawBackground(theme);
2171
2172 // Figure out maximal button space on title bar
2173
2174 QIcon ico = option->window->icon();
2175 bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey());
2176 if (hasIcon) {
2177 QPixmap pxIco = ico.pixmap(titleHeight);
2178 if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft)
2179 p->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco);
2180 else
2181 p->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco);
2182 }
2183 if (!dwOpt->title.isEmpty()) {
2184 QPen oldPen = p->pen();
2185 QFont oldFont = p->font();
2186 QFont titleFont = oldFont;
2187 titleFont.setBold(true);
2188 p->setFont(titleFont);
2189 QString titleText
2190 = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
2191
2192 int result = TST_NONE;
2193 GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
2194 if (result != TST_NONE) {
2195 COLORREF textShadowRef;
2196 GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
2197 QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
2198 p->setPen(textShadow);
2199 drawItemText(p, titleRect.adjusted(1, 1, 1, 1),
2200 Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
2201 dwOpt->state & State_Enabled, titleText);
2202 }
2203
2204 COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
2205 QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
2206 p->setPen(textColor);
2207 drawItemText(p, titleRect,
2208 Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette,
2209 dwOpt->state & State_Enabled, titleText);
2210 p->setFont(oldFont);
2211 p->setPen(oldPen);
2212 }
2213
2214 }
2215
2216 return;
2217 }
2218 break;
2219#endif // QT_CONFIG(dockwidget)
2220#if 0 && QT_CONFIG(rubberband)
2221 case CE_RubberBand:
2222 if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) {
2223 QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight).toRgb();
2224 p->save();
2225 p->setPen(highlight.darker(120));
2226 QColor dimHighlight(qMin(highlight.red()/2 + 110, 255),
2227 qMin(highlight.green()/2 + 110, 255),
2228 qMin(highlight.blue()/2 + 110, 255),
2229 127);
2230 //(widget && widget->isTopLevel())? 255 : 127);
2231 p->setBrush(dimHighlight);
2232 p->drawRect(option->rect.adjusted(0, 0, -1, -1));
2233 p->restore();
2234 return;
2235 }
2236 break;
2237#endif // QT_CONFIG(rubberband)
2238 case CE_HeaderEmptyArea:
2239 if (option->state & State_Horizontal)
2240 {
2241 themeNumber = QWindowsXPStylePrivate::HeaderTheme;
2242 stateId = HIS_NORMAL;
2243 }
2244 else {
2245 QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, p);
2246 return;
2247 }
2248 break;
2249 default:
2250 break;
2251 }
2252
2253 XPThemeData theme(option->window, p, themeNumber, partId, stateId, rect);
2254 if (!theme.isValid()) {
2255 QWindowsStyle::drawControl(element, option, p);
2256 return;
2257 }
2258
2259 theme.rotate = rotate;
2260 theme.mirrorHorizontally = hMirrored;
2261 theme.mirrorVertically = vMirrored;
2262 d->drawBackground(theme);
2263}
2264
2265QRect QWindowsXPStylePrivate::scrollBarGripperBounds(QStyle::State flags, XPThemeData *theme)
2266{
2267 const bool horizontal = flags & QStyle::State_Horizontal;
2268 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(theme->window);
2269 const QMargins contentsMargin =
2270 (theme->margins(theme->rect, TMT_SIZINGMARGINS) * factor).toMargins();
2271 theme->partId = horizontal ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT;
2272 const QSize size = (theme->size() * factor).toSize();
2273
2274 const int hSpace = theme->rect.width() - size.width();
2275 const int vSpace = theme->rect.height() - size.height();
2276 const bool sufficientSpace = (horizontal && hSpace > (contentsMargin.left() + contentsMargin.right()))
2277 || vSpace > contentsMargin.top() + contentsMargin.bottom();
2278 return sufficientSpace ? QRect(theme->rect.topLeft() + QPoint(hSpace, vSpace) / 2, size) : QRect();
2279}
2280
2281#if 0 && QT_CONFIG(mdiarea)
2282// Helper for drawing MDI buttons into the corner widget of QMenuBar in case a
2283// QMdiSubWindow is maximized.
2284static void populateMdiButtonTheme(const QStyle *proxy,
2288{
2289 theme->partId = part;
2297 else
2299}
2300
2301// Calculate an small (max 2), empirical correction factor for scaling up
2302// WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON, which are too
2303// small on High DPI screens (QTBUG-75927).
2305{
2306 const auto dpr = pd ? pd->devicePixelRatioF() : qApp->devicePixelRatio();
2307 const QSizeF nativeSize = QSizeF(theme.size()) / dpr;
2309 const auto rawFactor = qMin(requestedSize.width() / nativeSize.width(),
2311 const auto factor = rawFactor >= qreal(2) ? qreal(2) : qreal(1);
2312 return factor;
2313}
2314#endif // QT_CONFIG(mdiarea)
2315
2316static void populateTitleBarButtonTheme(const QStyle *proxy,
2317 const QStyleOptionComplex *option,
2318 QStyle::SubControl subControl,
2319 bool isTitleBarActive, int part,
2320 XPThemeData *theme)
2321{
2322 theme->rect = proxy->subControlRect(QStyle::CC_TitleBar, option, subControl);
2323 theme->partId = part;
2324 if (!(option->state & QStyle::State_Enabled))
2325 theme->stateId = RBS_DISABLED;
2326 else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_Sunken))
2327 theme->stateId = RBS_PUSHED;
2328 else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_MouseOver))
2329 theme->stateId = RBS_HOT;
2330 else if (!isTitleBarActive)
2331 theme->stateId = RBS_INACTIVE;
2332 else
2333 theme->stateId = RBS_NORMAL;
2334}
2335
2336/*!
2337 \internal
2338 \reimp
2339*/
2340void QWindowsXPStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option,
2341 QPainter *p) const
2342{
2343 QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
2344
2346 QWindowsStyle::drawComplexControl(cc, option, p);
2347 return;
2348 }
2349
2350 State flags = option->state;
2351 SubControls sub = option->subControls;
2352 QRect r = option->rect;
2353
2354 int partId = 0;
2355 int stateId = 0;
2356 if (option->window && option->window->isActive())
2357 flags |= State_MouseOver;
2358
2359 switch (cc) {
2360//#if QT_CONFIG(spinbox)
2361 case CC_SpinBox:
2362 if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option))
2363 {
2364 XPThemeData theme(option->window, p, QWindowsXPStylePrivate::SpinTheme);
2365
2366 if (sb->frame && (sub & SC_SpinBoxFrame)) {
2367 partId = EP_EDITTEXT;
2368 if (!(flags & State_Enabled))
2369 stateId = ETS_DISABLED;
2370 else if (flags & State_HasFocus)
2371 stateId = ETS_FOCUSED;
2372 else
2373 stateId = ETS_NORMAL;
2374
2375 XPThemeData ftheme(option->window, p, QWindowsXPStylePrivate::EditTheme,
2376 partId, stateId, r);
2377 ftheme.noContent = true;
2378 d->drawBackground(ftheme);
2379 }
2380 if (sub & SC_SpinBoxUp) {
2381 theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp);
2382 partId = SPNP_UP;
2383 if (!(sb->stepEnabled & QStyleOptionSpinBox::StepUpEnabled) || !(flags & State_Enabled))
2384 stateId = UPS_DISABLED;
2385 else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken))
2386 stateId = UPS_PRESSED;
2387 else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver))
2388 stateId = UPS_HOT;
2389 else
2390 stateId = UPS_NORMAL;
2391 theme.partId = partId;
2392 theme.stateId = stateId;
2393 d->drawBackground(theme);
2394 }
2395 if (sub & SC_SpinBoxDown) {
2396 theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown);
2397 partId = SPNP_DOWN;
2398 if (!(sb->stepEnabled & QStyleOptionSpinBox::StepDownEnabled) || !(flags & State_Enabled))
2399 stateId = DNS_DISABLED;
2400 else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken))
2401 stateId = DNS_PRESSED;
2402 else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver))
2403 stateId = DNS_HOT;
2404 else
2405 stateId = DNS_NORMAL;
2406 theme.partId = partId;
2407 theme.stateId = stateId;
2408 d->drawBackground(theme);
2409 }
2410 }
2411 break;
2412//#endif // QT_CONFIG(spinbox)
2413//#if QT_CONFIG(searchfield)
2414 case CC_SearchField:
2415 if (const QStyleOptionSearchField *sf = qstyleoption_cast<const QStyleOptionSearchField *>(option))
2416 {
2417 if (sf->frame && (sub & SC_SearchFieldFrame)) {
2418 partId = EP_EDITBORDER_NOSCROLL;
2419 if (!(flags & State_Enabled))
2420 stateId = ETS_DISABLED;
2421 else if (flags & State_MouseOver)
2422 stateId = ETS_HOT;
2423 else if (flags & State_HasFocus)
2424 stateId = ETS_FOCUSED;
2425 else
2426 stateId = ETS_NORMAL;
2427
2428 XPThemeData theme(option->window, p,
2429 QWindowsXPStylePrivate::EditTheme,
2430 partId, stateId, r);
2431
2432 d->drawBackground(theme);
2433 }
2434 }
2435 break;
2436//#endif QT_CONFIG(searchfield)
2437//#if QT_CONFIG(combobox)
2438 case CC_ComboBox:
2439 if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option))
2440 {
2441 if (cmb->editable) {
2442 if (sub & SC_ComboBoxEditField) {
2443 partId = EP_EDITBORDER_NOSCROLL;
2444 if (!(flags & State_Enabled))
2445 stateId = ETS_DISABLED;
2446 else if (flags & State_MouseOver)
2447 stateId = ETS_HOT;
2448 else if (flags & State_HasFocus)
2449 stateId = ETS_FOCUSED;
2450 else
2451 stateId = ETS_NORMAL;
2452
2453 XPThemeData theme(option->window, p,
2454 QWindowsXPStylePrivate::EditTheme,
2455 partId, stateId, r);
2456
2457 d->drawBackground(theme);
2458 }
2459 if (sub & SC_ComboBoxArrow) {
2460 QRect subRect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow);
2461 XPThemeData theme(option->window, p, QWindowsXPStylePrivate::ComboboxTheme);
2462 theme.rect = subRect;
2463 partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;
2464
2465 if (!(cmb->state & State_Enabled))
2466 stateId = CBXS_DISABLED;
2467 else if (cmb->state & State_Sunken || cmb->state & State_On)
2468 stateId = CBXS_PRESSED;
2469 else if (cmb->state & State_MouseOver && option->activeSubControls & SC_ComboBoxArrow)
2470 stateId = CBXS_HOT;
2471 else
2472 stateId = CBXS_NORMAL;
2473
2474 theme.partId = partId;
2475 theme.stateId = stateId;
2476 d->drawBackground(theme);
2477 }
2478
2479 } else {
2480 if (sub & SC_ComboBoxFrame) {
2481 XPThemeData theme(option->window, p, QWindowsXPStylePrivate::ComboboxTheme);
2482 theme.rect = option->rect;
2483 theme.partId = CP_READONLY;
2484 if (!(cmb->state & State_Enabled))
2485 theme.stateId = CBXS_DISABLED;
2486 else if (cmb->state & State_Sunken || cmb->state & State_On)
2487 theme.stateId = CBXS_PRESSED;
2488 else if (cmb->state & State_MouseOver)
2489 theme.stateId = CBXS_HOT;
2490 else
2491 theme.stateId = CBXS_NORMAL;
2492 d->drawBackground(theme);
2493 }
2494 if (sub & SC_ComboBoxArrow) {
2495 XPThemeData theme(option->window, p, QWindowsXPStylePrivate::ComboboxTheme);
2496 theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow);
2497 theme.partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT;
2498 if (!(cmb->state & State_Enabled))
2499 theme.stateId = CBXS_DISABLED;
2500 else
2501 theme.stateId = CBXS_NORMAL;
2502 d->drawBackground(theme);
2503 }
2504 if ((sub & SC_ComboBoxEditField) && (flags & State_HasFocus)) {
2505 QStyleOptionFocusRect fropt;
2506 fropt.QStyleOption::operator=(*cmb);
2507 fropt.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField);
2508 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p);
2509 }
2510 }
2511 }
2512 break;
2513//#endif // QT_CONFIG(combobox)
2514 case CC_ScrollBar:
2515 if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option))
2516 {
2517 XPThemeData theme(option->window, p, QWindowsXPStylePrivate::ScrollBarTheme);
2518 bool maxedOut = (scrollbar->maximum == scrollbar->minimum);
2519 if (maxedOut)
2520 flags &= ~State_Enabled;
2521
2522 bool isHorz = flags & State_Horizontal;
2523 bool isRTL = option->direction == Qt::RightToLeft;
2524 if (sub & SC_ScrollBarAddLine) {
2525 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine);
2526 partId = SBP_ARROWBTN;
2527 if (!(flags & State_Enabled))
2528 stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED);
2529 else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken))
2530 stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED);
2531 else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver))
2532 stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT);
2533 else
2534 stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL);
2535 theme.partId = partId;
2536 theme.stateId = stateId;
2537 d->drawBackground(theme);
2538 }
2539 if (sub & SC_ScrollBarSubLine) {
2540 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine);
2541 partId = SBP_ARROWBTN;
2542 if (!(flags & State_Enabled))
2543 stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED);
2544 else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken))
2545 stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED);
2546 else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver))
2547 stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT);
2548 else
2549 stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL);
2550 theme.partId = partId;
2551 theme.stateId = stateId;
2552 d->drawBackground(theme);
2553 }
2554 if (maxedOut) {
2555 if (sub & SC_ScrollBarSlider) {
2556 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider);
2557 theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage));
2558 theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage));
2559 partId = scrollbar->orientation == Qt::Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
2560 stateId = SCRBS_DISABLED;
2561 theme.partId = partId;
2562 theme.stateId = stateId;
2563 d->drawBackground(theme);
2564 }
2565 } else {
2566 if (sub & SC_ScrollBarSubPage) {
2567 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage);
2568 partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT;
2569 if (!(flags & State_Enabled))
2570 stateId = SCRBS_DISABLED;
2571 else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken))
2572 stateId = SCRBS_PRESSED;
2573 else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver))
2574 stateId = SCRBS_HOT;
2575 else
2576 stateId = SCRBS_NORMAL;
2577 theme.partId = partId;
2578 theme.stateId = stateId;
2579 d->drawBackground(theme);
2580 }
2581 if (sub & SC_ScrollBarAddPage) {
2582 theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage);
2583 partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT;
2584 if (!(flags & State_Enabled))
2585 stateId = SCRBS_DISABLED;
2586 else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken))
2587 stateId = SCRBS_PRESSED;
2588 else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver))
2589 stateId = SCRBS_HOT;
2590 else
2591 stateId = SCRBS_NORMAL;
2592 theme.partId = partId;
2593 theme.stateId = stateId;
2594 d->drawBackground(theme);
2595 }
2596 if (sub & SC_ScrollBarSlider) {
2597 // The style paint the slider handle so that it is surrounded by transparent areas
2598 // on each side. These areas have the same size as the Left/Right (or Top/Left) buttons.
2599 // This is probaly done in order for the handle to travel all along the geometry
2600 // of the slider, while the handle still not occluding the buttons.
2601 // We do not want those transparent areas, so we clip them off here.
2602 const int extentForButton = proxy()->pixelMetric(PM_ScrollBarExtent, scrollbar);
2603 QSize extend(extentForButton, 0);
2604 if (scrollbar->orientation == Qt::Vertical)
2605 extend.transpose();
2606
2607 QRect rect = r; // 'r' is the rect for the scrollbar handle
2608 rect.setSize(rect.size() + 2 * extend); // 'rect' is the rect for the whole scrollbar
2609 p->setClipRect(r); // clip off button areas
2610 p->translate(-extend.width(), -extend.height()); // translate left button area away
2611
2612 theme.rect = rect;
2613 if (!(flags & State_Enabled))
2614 stateId = SCRBS_DISABLED;
2615 else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken))
2616 stateId = SCRBS_PRESSED;
2617 else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver))
2618 stateId = SCRBS_HOT;
2619 else
2620 stateId = SCRBS_NORMAL;
2621
2622 // Draw handle
2623 theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
2624 theme.stateId = stateId;
2625 d->drawBackground(theme);
2626
2627 const QRect gripperBounds = QWindowsXPStylePrivate::scrollBarGripperBounds(flags, &theme);
2628 // Draw gripper if there is enough space
2629 if (!gripperBounds.isEmpty()) {
2630 p->save();
2631 theme.rect = gripperBounds;
2632 p->setClipRegion(d->region(theme));// Only change inside the region of the gripper
2633 d->drawBackground(theme); // Transparent gripper ontop of background
2634 p->restore();
2635 }
2636 }
2637 }
2638 }
2639 break;
2640
2641//#if QT_CONFIG(slider)
2642 case CC_Slider:
2643 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option))
2644 {
2645 XPThemeData theme(option->window, p, QWindowsXPStylePrivate::TrackBarTheme);
2646 QRect slrect = slider->rect;
2647 QRegion tickreg = slrect;
2648 if (sub & SC_SliderGroove) {
2649 theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove);
2650 if (slider->orientation == Qt::Horizontal) {
2651 partId = TKP_TRACK;
2652 stateId = TRS_NORMAL;
2653 theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4);
2654 } else {
2655 partId = TKP_TRACKVERT;
2656 stateId = TRVS_NORMAL;
2657 theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height());
2658 }
2659 theme.partId = partId;
2660 theme.stateId = stateId;
2661 d->drawBackground(theme);
2662 tickreg -= theme.rect;
2663 }
2664 if (sub & SC_SliderTickmarks) {
2665 int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider);
2666 int ticks = slider->tickPosition;
2667 int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider);
2668 int len = proxy()->pixelMetric(PM_SliderLength, slider);
2669 int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider);
2670 int interval = slider->tickInterval;
2671 if (interval <= 0) {
2672 interval = slider->singleStep;
2673 if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval,
2674 available)
2675 - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
2676 0, available) < 3)
2677 interval = slider->pageStep;
2678 }
2679 if (!interval)
2680 interval = 1;
2681 int fudge = len / 2;
2682 int pos;
2683 int bothOffset = (ticks & QStyleOptionSlider::TicksAbove && ticks & QStyleOptionSlider::TicksBelow) ? 1 : 0;
2684 p->setPen(d->sliderTickColor);
2685 QVarLengthArray<QLine, 32> lines;
2686 int v = slider->minimum;
2687 while (v <= slider->maximum + 1) {
2688 if (v == slider->maximum + 1 && interval == 1)
2689 break;
2690 const int v_ = qMin(v, slider->maximum);
2691 int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3;
2692 pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum,
2693 v_, available) + fudge;
2694 if (slider->orientation == Qt::Horizontal) {
2695 if (ticks & QStyleOptionSlider::TicksAbove)
2696 lines.append(QLine(pos, tickOffset - 1 - bothOffset,
2697 pos, tickOffset - 1 - bothOffset - tickLength));
2698
2699 if (ticks & QStyleOptionSlider::TicksBelow)
2700 lines.append(QLine(pos, tickOffset + thickness + bothOffset,
2701 pos, tickOffset + thickness + bothOffset + tickLength));
2702 } else {
2703 if (ticks & QStyleOptionSlider::TicksAbove)
2704 lines.append(QLine(tickOffset - 1 - bothOffset, pos,
2705 tickOffset - 1 - bothOffset - tickLength, pos));
2706
2707 if (ticks & QStyleOptionSlider::TicksBelow)
2708 lines.append(QLine(tickOffset + thickness + bothOffset, pos,
2709 tickOffset + thickness + bothOffset + tickLength, pos));
2710 }
2711 // in the case where maximum is max int
2712 int nextInterval = v + interval;
2713 if (nextInterval < v)
2714 break;
2715 v = nextInterval;
2716 }
2717 if (!lines.isEmpty()) {
2718 p->save();
2719 p->translate(slrect.topLeft());
2720 p->drawLines(lines.constData(), lines.size());
2721 p->restore();
2722 }
2723 }
2724 if (sub & SC_SliderHandle) {
2725 theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle);
2726 if (slider->orientation == Qt::Horizontal) {
2727 if (slider->tickPosition == QStyleOptionSlider::TicksAbove)
2728 partId = TKP_THUMBTOP;
2729 else if (slider->tickPosition == QStyleOptionSlider::TicksBelow)
2730 partId = TKP_THUMBBOTTOM;
2731 else
2732 partId = TKP_THUMB;
2733
2734 if (!(slider->state & State_Enabled))
2735 stateId = TUS_DISABLED;
2736 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
2737 stateId = TUS_PRESSED;
2738 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
2739 stateId = TUS_HOT;
2740 else if (flags & State_HasFocus)
2741 stateId = TUS_FOCUSED;
2742 else
2743 stateId = TUS_NORMAL;
2744 } else {
2745 if (slider->tickPosition == QStyleOptionSlider::TicksLeft)
2746 partId = TKP_THUMBLEFT;
2747 else if (slider->tickPosition == QStyleOptionSlider::TicksRight)
2748 partId = TKP_THUMBRIGHT;
2749 else
2750 partId = TKP_THUMBVERT;
2751
2752 if (!(slider->state & State_Enabled))
2753 stateId = TUVS_DISABLED;
2754 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken))
2755 stateId = TUVS_PRESSED;
2756 else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver))
2757 stateId = TUVS_HOT;
2758 else if (flags & State_HasFocus)
2759 stateId = TUVS_FOCUSED;
2760 else
2761 stateId = TUVS_NORMAL;
2762 }
2763 theme.partId = partId;
2764 theme.stateId = stateId;
2765 d->drawBackground(theme);
2766 }
2767 if (sub & SC_SliderGroove && slider->state & State_HasFocus) {
2768 QStyleOptionFocusRect fropt;
2769 fropt.QStyleOption::operator=(*slider);
2770 fropt.rect = subElementRect(SE_SliderFocusRect, slider);
2771 proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p);
2772 }
2773 }
2774 break;
2775//#endif
2776#if 0 && QT_CONFIG(toolbutton)
2777 case CC_ToolButton:
2778 if (const QStyleOptionToolButton *toolbutton
2779 = qstyleoption_cast<const QStyleOptionToolButton *>(option)) {
2780 QRect button, menuarea;
2781 button = proxy()->subControlRect(cc, toolbutton, SC_ToolButton);
2782 menuarea = proxy()->subControlRect(cc, toolbutton, SC_ToolButtonMenu);
2783
2784 State bflags = toolbutton->state & ~State_Sunken;
2785 State mflags = bflags;
2786 bool autoRaise = flags & State_AutoRaise;
2787 if (autoRaise) {
2788 if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) {
2789 bflags &= ~State_Raised;
2790 }
2791 }
2792
2793 if (toolbutton->state & State_Sunken) {
2794 if (toolbutton->activeSubControls & SC_ToolButton) {
2795 bflags |= State_Sunken;
2796 mflags |= State_MouseOver | State_Sunken;
2797 } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) {
2798 mflags |= State_Sunken;
2799 bflags |= State_MouseOver;
2800 }
2801 }
2802
2803 QStyleOption tool = *toolbutton;
2804 if (toolbutton->subControls & SC_ToolButton) {
2805 if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) {
2806 if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) {
2807 XPThemeData theme(option->window, p, QWindowsXPStylePrivate::ToolBarTheme);
2808 theme.partId = TP_SPLITBUTTON;
2809 theme.rect = button;
2810 if (!(bflags & State_Enabled))
2811 stateId = TS_DISABLED;
2812 else if (bflags & State_Sunken)
2813 stateId = TS_PRESSED;
2814 else if (bflags & State_MouseOver || !(flags & State_AutoRaise))
2815 stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT;
2816 else if (bflags & State_On)
2817 stateId = TS_CHECKED;
2818 else
2819 stateId = TS_NORMAL;
2820 if (option->direction == Qt::RightToLeft)
2821 theme.mirrorHorizontally = true;
2822 theme.stateId = stateId;
2823 d->drawBackground(theme);
2824 } else {
2825 tool.rect = option->rect;
2826 tool.state = bflags;
2827 if (autoRaise) // for tool bars
2828 proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p);
2829 else
2830 proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p);
2831 }
2832 }
2833 }
2834
2835 if (toolbutton->state & State_HasFocus) {
2836 QStyleOptionFocusRect fr;
2837 fr.QStyleOption::operator=(*toolbutton);
2838 fr.rect.adjust(3, 3, -3, -3);
2839 if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup)
2840 fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator,
2841 toolbutton), 0);
2842 proxy()->drawPrimitive(PE_FrameFocusRect, &fr, p);
2843 }
2844 QStyleOptionToolButton label = *toolbutton;
2845 label.state = bflags;
2846 int fw = 2;
2847 if (!autoRaise)
2848 label.state &= ~State_Sunken;
2849 label.rect = button.adjusted(fw, fw, -fw, -fw);
2850 proxy()->drawControl(CE_ToolButtonLabel, &label, p);
2851
2852 if (toolbutton->subControls & SC_ToolButtonMenu) {
2853 tool.rect = menuarea;
2854 tool.state = mflags;
2855 if (autoRaise) {
2856 proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, p);
2857 } else {
2858 tool.state = mflags;
2859 menuarea.adjust(-2, 0, 0, 0);
2860 // Draw menu button
2861 if ((bflags & State_Sunken) != (mflags & State_Sunken)){
2862 p->save();
2863 p->setClipRect(menuarea);
2864 tool.rect = option->rect;
2865 proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p);
2866 p->restore();
2867 }
2868 // Draw arrow
2869 p->save();
2870 p->setPen(option->palette.dark().color());
2871 p->drawLine(menuarea.left(), menuarea.top() + 3,
2872 menuarea.left(), menuarea.bottom() - 3);
2873 p->setPen(option->palette.light().color());
2874 p->drawLine(menuarea.left() - 1, menuarea.top() + 3,
2875 menuarea.left() - 1, menuarea.bottom() - 3);
2876
2877 tool.rect = menuarea.adjusted(2, 3, -2, -1);
2878 proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, p);
2879 p->restore();
2880 }
2881 } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) {
2882 int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton);
2883 QRect ir = toolbutton->rect;
2884 QStyleOptionToolButton newBtn = *toolbutton;
2885 newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5);
2886 proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p);
2887 }
2888 }
2889 break;
2890#endif // QT_CONFIG(toolbutton)
2891
2892 case CC_TitleBar:
2893 {
2894 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option))
2895 {
2896 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(option);
2897 bool isActive = tb->titleBarState & QStyle::State_Active;
2898 XPThemeData theme(option->window, p, QWindowsXPStylePrivate::WindowTheme);
2899 if (sub & SC_TitleBarLabel) {
2900
2901 partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION;
2902 theme.rect = option->rect;
2903 if (!(option->state & QStyle::State_Enabled))
2904 stateId = CS_DISABLED;
2905 else if (isActive)
2906 stateId = CS_ACTIVE;
2907 else
2908 stateId = CS_INACTIVE;
2909
2910 theme.partId = partId;
2911 theme.stateId = stateId;
2912 d->drawBackground(theme);
2913
2914 QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel);
2915
2916 int result = TST_NONE;
2917 GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result);
2918 if (result != TST_NONE) {
2919 COLORREF textShadowRef;
2920 GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef);
2921 QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef));
2922 p->setPen(textShadow);
2923 p->drawText(int(ir.x() + 3 * factor), int(ir.y() + 2 * factor),
2924 int(ir.width() - 1 * factor), ir.height(),
2925 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
2926 }
2927 COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT);
2928 QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText));
2929 p->setPen(textColor);
2930 p->drawText(int(ir.x() + 2 * factor), int(ir.y() + 1 * factor),
2931 int(ir.width() - 2 * factor), ir.height(),
2932 Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text);
2933 }
2934 if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
2935 theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu);
2936 partId = WP_SYSBUTTON;
2937 if (!(option->state & QStyle::State_Enabled) || !isActive)
2938 stateId = SBS_DISABLED;
2939 else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken))
2940 stateId = SBS_PUSHED;
2941 else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver))
2942 stateId = SBS_HOT;
2943 else
2944 stateId = SBS_NORMAL;
2945 if (!tb->icon.isNull()) {
2946 tb->icon.paint(p, theme.rect);
2947 } else {
2948 theme.partId = partId;
2949 theme.stateId = stateId;
2950 if (theme.size().isEmpty()) {
2951 int iconSize = proxy()->pixelMetric(PM_SmallIconSize, tb);
2952 QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, tb).pixmap(iconSize, iconSize);
2953 p->save();
2954 drawItemPixmap(p, theme.rect, Qt::AlignCenter, pm);
2955 p->restore();
2956 } else {
2957 d->drawBackground(theme);
2958 }
2959 }
2960 }
2961
2962 if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint
2963 && !(tb->titleBarState & Qt::WindowMinimized)) {
2964 populateTitleBarButtonTheme(proxy(), option, SC_TitleBarMinButton, isActive, WP_MINBUTTON, &theme);
2965 d->drawBackground(theme);
2966 }
2967 if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint
2968 && !(tb->titleBarState & Qt::WindowMaximized)) {
2969 populateTitleBarButtonTheme(proxy(), option, SC_TitleBarMaxButton, isActive, WP_MAXBUTTON, &theme);
2970 d->drawBackground(theme);
2971 }
2972 if (sub & SC_TitleBarContextHelpButton
2973 && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) {
2974 populateTitleBarButtonTheme(proxy(), option, SC_TitleBarContextHelpButton, isActive, WP_HELPBUTTON, &theme);
2975 d->drawBackground(theme);
2976 }
2977 bool drawNormalButton = (sub & SC_TitleBarNormalButton)
2978 && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint)
2979 && (tb->titleBarState & Qt::WindowMinimized))
2980 || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint)
2981 && (tb->titleBarState & Qt::WindowMaximized)));
2982 if (drawNormalButton) {
2983 populateTitleBarButtonTheme(proxy(), option, SC_TitleBarNormalButton, isActive, WP_RESTOREBUTTON, &theme);
2984 d->drawBackground(theme);
2985 }
2986 if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
2987 && !(tb->titleBarState & Qt::WindowMinimized)) {
2988 populateTitleBarButtonTheme(proxy(), option, SC_TitleBarShadeButton, isActive, WP_MINBUTTON, &theme);
2989 d->drawBackground(theme);
2990 }
2991 if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
2992 && tb->titleBarState & Qt::WindowMinimized) {
2993 populateTitleBarButtonTheme(proxy(), option, SC_TitleBarUnshadeButton, isActive, WP_RESTOREBUTTON, &theme);
2994 d->drawBackground(theme);
2995 }
2996 if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
2997 populateTitleBarButtonTheme(proxy(), option, SC_TitleBarCloseButton, isActive, WP_CLOSEBUTTON, &theme);
2998 d->drawBackground(theme);
2999 }
3000 }
3001 }
3002 break;
3003
3004#if 0 && QT_CONFIG(mdiarea)
3005 case CC_MdiControls:
3006 {
3007 XPThemeData theme(option->window, p, QWindowsXPStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL);
3008 if (Q_UNLIKELY(!theme.isValid()))
3009 return;
3010
3011 if (option->subControls.testFlag(SC_MdiCloseButton)) {
3012 populateMdiButtonTheme(proxy(), option, SC_MdiCloseButton, WP_MDICLOSEBUTTON, &theme);
3013 d->drawBackground(theme, mdiButtonCorrectionFactor(theme));
3014 }
3015 if (option->subControls.testFlag(SC_MdiNormalButton)) {
3016 populateMdiButtonTheme(proxy(), option, SC_MdiNormalButton, WP_MDIRESTOREBUTTON, &theme);
3017 d->drawBackground(theme, mdiButtonCorrectionFactor(theme));
3018 }
3019 if (option->subControls.testFlag(QStyle::SC_MdiMinButton)) {
3020 populateMdiButtonTheme(proxy(), option, SC_MdiMinButton, WP_MDIMINBUTTON, &theme);
3021 d->drawBackground(theme, mdiButtonCorrectionFactor(theme));
3022 }
3023 }
3024 break;
3025#endif // QT_CONFIG(mdiarea)
3026#if 0 && QT_CONFIG(dial)
3027 case CC_Dial:
3028 if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option))
3029 QStyleHelper::drawDial(dial, p);
3030 break;
3031#endif // QT_CONFIG(dial)
3032 default:
3033 QWindowsStyle::drawComplexControl(cc, option, p);
3034 break;
3035 }
3036}
3037
3038static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = nullptr)
3039{
3040 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
3041 return pb->state & QStyle::State_Horizontal ? Qt::Horizontal : Qt::Vertical;
3042 return Qt::Horizontal;
3043}
3044
3045int QWindowsXPStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option)
3046{
3047 switch (pm) {
3048 case QStyle::PM_IndicatorWidth:
3049 return XPThemeData::themeSize(option->window, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width();
3050 case QStyle::PM_IndicatorHeight:
3051 return XPThemeData::themeSize(option->window, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height();
3052 case QStyle::PM_ExclusiveIndicatorWidth:
3053 return XPThemeData::themeSize(option->window, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width();
3054 case QStyle::PM_ExclusiveIndicatorHeight:
3055 return XPThemeData::themeSize(option->window, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height();
3056 case QStyle::PM_ProgressBarChunkWidth:
3057 return progressBarOrientation(option) == Qt::Horizontal
3058 ? XPThemeData::themeSize(option->window, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNK).width()
3059 : XPThemeData::themeSize(option->window, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNKVERT).height();
3060 case QStyle::PM_SliderThickness:
3061 return XPThemeData::themeSize(option->window, nullptr, QWindowsXPStylePrivate::TrackBarTheme, TKP_THUMB).height();
3062 case QStyle::PM_TitleBarHeight:
3063 return option->window && (option->window->type() == Qt::Tool)
3064 ? GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME)
3065 : GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME);
3066 case QStyle::PM_MdiSubWindowFrameWidth:
3067 return XPThemeData::themeSize(option->window, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width();
3068 case QStyle::PM_DockWidgetFrameWidth:
3069 return XPThemeData::themeSize(option->window, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLFRAMERIGHT, FS_ACTIVE).width();
3070 default:
3071 break;
3072 }
3073 return QWindowsXPStylePrivate::InvalidMetric;
3074}
3075
3076/*!
3077 \internal
3078 \reimp
3079 */
3080int QWindowsXPStyle::pixelMetric(PixelMetric pm, const QStyleOption *option) const
3081{
3082 if (!QWindowsXPStylePrivate::useXP())
3083 return QWindowsStyle::pixelMetric(pm, option);
3084
3085 int res = QWindowsXPStylePrivate::pixelMetricFromSystemDp(pm, option);
3086 if (res != QWindowsStylePrivate::InvalidMetric)
3087 return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(option));
3088
3089 res = 0;
3090 switch (pm) {
3091 case PM_MenuBarPanelWidth:
3092 case PM_ButtonDefaultIndicator:
3093 res = 0;
3094 break;
3095
3096 case PM_DefaultFrameWidth:
3097 res = 1;
3098 //res = qobject_cast<const QListView*>(widget) ? 2 : 1;
3099 break;
3100 case PM_MenuPanelWidth:
3101 case PM_SpinBoxFrameWidth:
3102 case PM_SearchFieldFrameWidth:
3103 res = 1;
3104 break;
3105
3106 case PM_TabBarTabOverlap:
3107 case PM_MenuHMargin:
3108 case PM_MenuVMargin:
3109 res = 2;
3110 break;
3111#if 0
3112 case PM_TabBarBaseOverlap:
3113 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) {
3114 switch (tab->shape) {
3115 case QTabBar::RoundedNorth:
3116 case QTabBar::TriangularNorth:
3117 case QTabBar::RoundedWest:
3118 case QTabBar::TriangularWest:
3119 res = 1;
3120 break;
3121 case QTabBar::RoundedSouth:
3122 case QTabBar::TriangularSouth:
3123 res = 2;
3124 break;
3125 case QTabBar::RoundedEast:
3126 case QTabBar::TriangularEast:
3127 res = 3;
3128 break;
3129 }
3130 }
3131 break;
3132#endif
3133 case PM_SplitterWidth:
3134 res = QStyleHelper::dpiScaled(5., option);
3135 break;
3136
3137 case PM_MdiSubWindowMinimizedWidth:
3138 res = 160;
3139 break;
3140
3141#if 0 && QT_CONFIG(toolbar)
3142 case PM_ToolBarHandleExtent:
3143 res = int(QStyleHelper::dpiScaled(8., option));
3144 break;
3145
3146#endif // QT_CONFIG(toolbar)
3147 case PM_DockWidgetSeparatorExtent:
3148 case PM_DockWidgetTitleMargin:
3149 res = int(QStyleHelper::dpiScaled(4., option));
3150 break;
3151
3152 case PM_ButtonShiftHorizontal:
3153 case PM_ButtonShiftVertical:
3154 res = qstyleoption_cast<const QStyleOptionToolButton *>(option) ? 1 : 0;
3155 break;
3156
3157 default:
3158 res = QWindowsStyle::pixelMetric(pm, option);
3159 }
3160
3161 return res;
3162}
3163
3164/*
3165 This function is used by subControlRect to check if a button
3166 should be drawn for the given subControl given a set of window flags.
3167*/
3168static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){
3169
3170 bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
3171 bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
3172 const uint flags = tb->titleBarFlags;
3173 bool retVal = false;
3174 switch (sc) {
3175 case QStyle::SC_TitleBarContextHelpButton:
3176 if (flags & Qt::WindowContextHelpButtonHint)
3177 retVal = true;
3178 break;
3179 case QStyle::SC_TitleBarMinButton:
3180 if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint))
3181 retVal = true;
3182 break;
3183 case QStyle::SC_TitleBarNormalButton:
3184 if (isMinimized && (flags & Qt::WindowMinimizeButtonHint))
3185 retVal = true;
3186 else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint))
3187 retVal = true;
3188 break;
3189 case QStyle::SC_TitleBarMaxButton:
3190 if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint))
3191 retVal = true;
3192 break;
3193 case QStyle::SC_TitleBarShadeButton:
3194 if (!isMinimized && flags & Qt::WindowShadeButtonHint)
3195 retVal = true;
3196 break;
3197 case QStyle::SC_TitleBarUnshadeButton:
3198 if (isMinimized && flags & Qt::WindowShadeButtonHint)
3199 retVal = true;
3200 break;
3201 case QStyle::SC_TitleBarCloseButton:
3202 if (flags & Qt::WindowSystemMenuHint)
3203 retVal = true;
3204 break;
3205 case QStyle::SC_TitleBarSysMenu:
3206 if (flags & Qt::WindowSystemMenuHint)
3207 retVal = true;
3208 break;
3209 default :
3210 retVal = true;
3211 }
3212 return retVal;
3213}
3214
3215/*!
3216 \internal
3217 \reimp
3218*/
3219QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option,
3220 SubControl subControl) const
3221{
3222 if (!QWindowsXPStylePrivate::useXP())
3223 return QWindowsStyle::subControlRect(cc, option, subControl);
3224
3225 QRect rect;
3226
3227 switch (cc) {
3228 case CC_GroupBox:
3229 rect = visualRect(option->direction, option->rect,
3230 QWindowsStyle::subControlRect(cc, option, subControl));
3231 if (subControl == QStyle::SC_GroupBoxContents) {
3232 // This will add the margins that was added by QLayouts in QtWidgets
3233 // (default to 9 for layouts inside a QGroupBox)
3234 rect.adjust(9, 9, -9, -9);
3235 }
3236 break;
3237 case CC_TitleBar:
3238 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) {
3239 if (!buttonVisible(subControl, tb))
3240 return rect;
3241 const bool isToolTitle = false;
3242 const int height = tb->rect.height();
3243 const int width = tb->rect.width();
3244 const int buttonMargin = int(QStyleHelper::dpiScaled(4, option));
3245 const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(option);
3246 int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor)
3247 - buttonMargin;
3248 int buttonWidth = qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor)
3249 - buttonMargin;
3250 const int delta = buttonWidth + 2;
3251 int controlTop = option->rect.bottom() - buttonHeight - 2;
3252 const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option);
3253 const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0;
3254 const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0;
3255 const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0;
3256 const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0;
3257 const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0;
3258 bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
3259 bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
3260 int offset = 0;
3261
3262 switch (subControl) {
3263 case SC_TitleBarLabel:
3264 rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height);
3265 if (isToolTitle) {
3266 if (sysmenuHint) {
3267 rect.adjust(0, 0, -buttonWidth - 3, 0);
3268 }
3269 if (minimizeHint || maximizeHint)
3270 rect.adjust(0, 0, -buttonWidth - 2, 0);
3271 } else {
3272 if (sysmenuHint) {
3273 const int leftOffset = height - 8;
3274 rect.adjust(leftOffset, 0, 0, 0);
3275 }
3276 if (minimizeHint)
3277 rect.adjust(0, 0, -buttonWidth - 2, 0);
3278 if (maximizeHint)
3279 rect.adjust(0, 0, -buttonWidth - 2, 0);
3280 if (contextHint)
3281 rect.adjust(0, 0, -buttonWidth - 2, 0);
3282 if (shadeHint)
3283 rect.adjust(0, 0, -buttonWidth - 2, 0);
3284 }
3285 break;
3286
3287 case SC_TitleBarContextHelpButton:
3288 if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint)
3289 offset += delta;
3290 Q_FALLTHROUGH();
3291 case SC_TitleBarMinButton:
3292 if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
3293 offset += delta;
3294 else if (subControl == SC_TitleBarMinButton)
3295 break;
3296 Q_FALLTHROUGH();
3297 case SC_TitleBarNormalButton:
3298 if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
3299 offset += delta;
3300 else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
3301 offset += delta;
3302 else if (subControl == SC_TitleBarNormalButton)
3303 break;
3304 Q_FALLTHROUGH();
3305 case SC_TitleBarMaxButton:
3306 if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
3307 offset += delta;
3308 else if (subControl == SC_TitleBarMaxButton)
3309 break;
3310 Q_FALLTHROUGH();
3311 case SC_TitleBarShadeButton:
3312 if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
3313 offset += delta;
3314 else if (subControl == SC_TitleBarShadeButton)
3315 break;
3316 Q_FALLTHROUGH();
3317 case SC_TitleBarUnshadeButton:
3318 if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint))
3319 offset += delta;
3320 else if (subControl == SC_TitleBarUnshadeButton)
3321 break;
3322 Q_FALLTHROUGH();
3323 case SC_TitleBarCloseButton:
3324 if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
3325 offset += delta;
3326 else if (subControl == SC_TitleBarCloseButton)
3327 break;
3328
3329 rect.setRect(width - offset - controlTop + 1, controlTop,
3330 buttonWidth, buttonHeight);
3331 break;
3332
3333 case SC_TitleBarSysMenu:
3334 {
3335 const int controlTop = 6;
3336 const int controlHeight = height - controlTop - 3;
3337 const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option);
3338 QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent));
3339 if (tb->icon.isNull())
3340 iconSize = QSize(controlHeight, controlHeight);
3341 int hPad = (controlHeight - iconSize.height())/2;
3342 int vPad = (controlHeight - iconSize.width())/2;
3343 rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height());
3344 }
3345 break;
3346 default:
3347 break;
3348 }
3349 }
3350 break;
3351 case CC_ComboBox:
3352 if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
3353 const int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height();
3354 const int xpos = x + wi - qRound(QStyleHelper::dpiScaled(1 + 16, option));
3355
3356 switch (subControl) {
3357 case SC_ComboBoxFrame:
3358 rect = cmb->rect;
3359 break;
3360
3361 case SC_ComboBoxArrow: {
3362 const qreal dpi = QStyleHelper::dpi(option);
3363 rect = QRect(xpos, y + qRound(QStyleHelper::dpiScaled(1, dpi)),
3364 qRound(QStyleHelper::dpiScaled(16, dpi)),
3365 he - qRound(QStyleHelper::dpiScaled(2, dpi)));
3366 }
3367 break;
3368
3369 case SC_ComboBoxEditField: {
3370 const qreal dpi = QStyleHelper::dpi(option);
3371 const int frame = qRound(QStyleHelper::dpiScaled(2, dpi));
3372 rect = QRect(x + frame, y + frame,
3373 wi - qRound(QStyleHelper::dpiScaled(3 + 16, dpi)),
3374 he - qRound(QStyleHelper::dpiScaled(4, dpi)));
3375 }
3376 break;
3377
3378 case SC_ComboBoxListBoxPopup:
3379 rect = cmb->rect;
3380 break;
3381
3382 default:
3383 break;
3384 }
3385 }
3386 break;
3387#if 0 && QT_CONFIG(mdiarea)
3388 case CC_MdiControls:
3389 {
3390 int numSubControls = 0;
3391 if (option->subControls & SC_MdiCloseButton)
3392 ++numSubControls;
3393 if (option->subControls & SC_MdiMinButton)
3394 ++numSubControls;
3395 if (option->subControls & SC_MdiNormalButton)
3396 ++numSubControls;
3397 if (numSubControls == 0)
3398 break;
3399
3400 int buttonWidth = option->rect.width() / numSubControls;
3401 int offset = 0;
3402 switch (subControl) {
3403 case SC_MdiCloseButton:
3404 // Only one sub control, no offset needed.
3405 if (numSubControls == 1)
3406 break;
3407 offset += buttonWidth;
3408 Q_FALLTHROUGH();
3409 case SC_MdiNormalButton:
3410 // No offset needed if
3411 // 1) There's only one sub control
3412 // 2) We have a close button and a normal button (offset already added in SC_MdiClose)
3413 if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton)))
3414 break;
3415 if (option->subControls & SC_MdiNormalButton)
3416 offset += buttonWidth;
3417 break;
3418 default:
3419 break;
3420 }
3421 rect = QRect(offset, 0, buttonWidth, option->rect.height());
3422 break;
3423 }
3424#endif // QT_CONFIG(mdiarea)
3425
3426 default:
3427 rect = visualRect(option->direction, option->rect,
3428 QWindowsStyle::subControlRect(cc, option, subControl));
3429 break;
3430 }
3431 return visualRect(option->direction, option->rect, rect);
3432}
3433
3434/*!
3435 \internal
3436 \reimp
3437*/
3438QSize QWindowsXPStyle::sizeFromContents(ContentsType ct, const QStyleOption *option,
3439 const QSize &contentsSize) const
3440{
3441 if (!QWindowsXPStylePrivate::useXP())
3442 return QWindowsStyle::sizeFromContents(ct, option, contentsSize);
3443
3444 QSize sz(contentsSize);
3445 switch (ct) {
3446 case CT_LineEdit:
3447 case CT_ComboBox:
3448 {
3449 if (contentsSize.isEmpty()) {
3450 // Minimum size
3451 return QSize(20, 20);
3452 }
3453 XPThemeData buttontheme(option->window, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL);
3454 if (buttontheme.isValid()) {
3455 const QMarginsF borderSize = buttontheme.margins();
3456 if (!borderSize.isNull()) {
3457 const qreal margin = qreal(2);
3458 sz.rwidth() += qRound(borderSize.left() + borderSize.right() - margin);
3459 sz.rheight() += int(borderSize.bottom() + borderSize.top() - margin);
3460 }
3461 const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, option) + 1);
3462 sz += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option)
3463 + textMargins, 23), 0); //arrow button
3464 }
3465 }
3466 break;
3467 case CT_TabWidget:
3468 sz += QSize(6, 6);
3469 break;
3470 case CT_Menu:
3471 sz += QSize(1, 0);
3472 break;
3473#if 0 && QT_CONFIG(menubar)
3474 case CT_MenuBarItem:
3475 if (!sz.isEmpty())
3476 sz += QSize(windowsItemHMargin * 5 + 1, 6);
3477 break;
3478#endif
3479 case CT_MenuItem:
3480 if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option))
3481 {
3482 if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) {
3483 sz = QWindowsStyle::sizeFromContents(ct, option, sz);
3484 sz.setHeight(sz.height() - 2);
3485 return sz;
3486 }
3487 }
3488 sz = QWindowsStyle::sizeFromContents(ct, option, sz);
3489 break;
3490
3491 case CT_MdiControls: {
3492 sz.setHeight(int(QStyleHelper::dpiScaled(19, option)));
3493 int width = 54;
3494 if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) {
3495 width = 0;
3496 if (styleOpt->subControls & SC_MdiMinButton)
3497 width += 17 + 1;
3498 if (styleOpt->subControls & SC_MdiNormalButton)
3499 width += 17 + 1;
3500 if (styleOpt->subControls & SC_MdiCloseButton)
3501 width += 17 + 1;
3502 }
3503 sz.setWidth(int(QStyleHelper::dpiScaled(width, option)));
3504 }
3505 break;
3506
3507 case CT_Slider:
3508 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
3509 QStyle::SubControls sub = slider->subControls;
3510 const int TickSpace = 5;
3511 int thick = proxy()->pixelMetric(QStyle::PM_SliderThickness, slider);
3512
3513 if (slider->tickPosition & QStyleOptionSlider::TicksAbove)
3514 thick += TickSpace;
3515 if (slider->tickPosition & QStyleOptionSlider::TicksBelow)
3516 thick += TickSpace;
3517 sz.setWidth(thick);
3518
3519 if (sub & SC_SliderGroove) {
3520 const int SliderLength = 84;
3521 sz.setHeight(SliderLength);
3522 }
3523 if (slider->orientation == Qt::Horizontal)
3524 sz.transpose();
3525 if (sub & SC_SliderHandle) {
3526 const QSize s = proxy()->subControlRect(CC_Slider, slider, SC_SliderHandle).size();
3527 sz = sz.expandedTo(s);
3528 }
3529 }
3530 break;
3531 case CT_ScrollBar :
3532 // Make sure that the scroll bar is large enough to display the thumb indicator.
3533 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) {
3534 const int scrollBarHeight = proxy()->pixelMetric(QStyle::PM_ScrollBarExtent, slider);
3535 const int scrollBarSliderMin = proxy()->pixelMetric(QStyle::PM_ScrollBarSliderMin, slider);
3536 int &szw = slider->orientation == Qt::Horizontal ? sz.rwidth() : sz.rheight();
3537 int &szh = slider->orientation == Qt::Horizontal ? sz.rheight() : sz.rwidth();
3538 if (slider->subControls & SC_ScrollBarSlider) {
3539 szw = qMax(szw, scrollBarSliderMin);
3540 szh = scrollBarHeight;
3541 } else if (slider->subControls & SC_ScrollBarGroove) {
3542 szw = qMax(szw, scrollBarSliderMin + 2 * scrollBarHeight);
3543 szh = scrollBarHeight;
3544 } else if (slider->subControls & (SC_ScrollBarAddLine| SC_ScrollBarSubLine)) {
3545 // Assume that the AddLine and SubLine buttons have the same size, and just query
3546 // for the size of AddLine
3547 const int sbextent = proxy()->pixelMetric(PM_ScrollBarExtent, slider);
3548 szw = qMax(szw, sbextent);
3549 szh = scrollBarHeight;
3550 }
3551 }
3552 break;
3553 default:
3554 sz = QWindowsStyle::sizeFromContents(ct, option, sz);
3555 break;
3556 }
3557
3558 return sz;
3559}
3560
3561
3562/*!
3563 \internal
3564 \reimp
3565 */
3566int QWindowsXPStyle::styleHint(StyleHint hint, const QStyleOption *option,
3567 QStyleHintReturn *returnData) const
3568{
3569 QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
3570 if (!QWindowsXPStylePrivate::useXP())
3571 return QWindowsStyle::styleHint(hint, option, returnData);
3572
3573 int res = 0;
3574 switch (hint) {
3575
3576 case SH_EtchDisabledText:
3577 //res = (qobject_cast<const QLabel*>(widget) != 0);
3578 res = 0;
3579 break;
3580
3581 case SH_SpinControls_DisableOnBounds:
3582 res = 0;
3583 break;
3584
3585 case SH_TitleBar_AutoRaise:
3586 case SH_TitleBar_NoBorder:
3587 res = 1;
3588 break;
3589
3590 case SH_GroupBox_TextLabelColor:
3591 if (option->state & QStyle::State_Enabled)
3592 res = d->groupBoxTextColor;
3593 else
3594 res = d->groupBoxTextColorDisabled;
3595 break;
3596
3597 case SH_Table_GridLineColor:
3598 res = 0xC0C0C0;
3599 break;
3600
3601 case SH_WindowFrame_Mask:
3602 {
3603 res = 1;
3604 QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData);
3605 const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option);
3606 if (mask && titlebar) {
3607 // Note certain themes will not return the whole window frame but only the titlebar part when
3608 // queried This function needs to return the entire window mask, hence we will only fetch the mask for the
3609 // titlebar itself and add the remaining part of the window rect at the bottom.
3610 int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option);
3611 QRect titleBarRect = option->rect;
3612 titleBarRect.setHeight(tbHeight);
3613 XPThemeData themeData;
3614 if (titlebar->titleBarState & Qt::WindowMinimized) {
3615 themeData = XPThemeData(option->window, nullptr,
3616 QWindowsXPStylePrivate::WindowTheme,
3617 WP_MINCAPTION, CS_ACTIVE, titleBarRect);
3618 } else {
3619 themeData = XPThemeData(option->window, nullptr,
3620 QWindowsXPStylePrivate::WindowTheme,
3621 WP_CAPTION, CS_ACTIVE, titleBarRect);
3622 }
3623 mask->region = d->region(themeData) +
3624 QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight);
3625 }
3626 }
3627 break;
3628#if 0 && QT_CONFIG(rubberband)
3629 case SH_RubberBand_Mask:
3630 if (qstyleoption_cast<const QStyleOptionRubberBand *>(option))
3631 res = 0;
3632 break;
3633#endif // QT_CONFIG(rubberband)
3634
3635 case SH_ItemView_DrawDelegateFrame:
3636 res = 1;
3637 break;
3638
3639 default:
3640 res =QWindowsStyle::styleHint(hint, option, returnData);
3641 }
3642
3643 return res;
3644}
3645
3646QMargins QWindowsXPStyle::ninePatchMargins(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const
3647{
3648 QMargins margins;
3649
3650 switch (cc) {
3651 case CC_ScrollBar: {
3652 margins = QWindowsStyle::ninePatchMargins(cc, opt, imageSize);
3653 if (const auto option = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
3654 if (option->orientation == Qt::Horizontal) {
3655 margins.setTop(-1);
3656 margins.setBottom(-1);
3657 } else {
3658 margins.setLeft(-1);
3659 margins.setRight(-1);
3660 }
3661 }
3662 break; }
3663 default:
3664 margins = QWindowsStyle::ninePatchMargins(cc, opt, imageSize);
3665 break;
3666 }
3667
3668 return margins;
3669}
3670
3671
3672/*!
3673 \internal
3674 \reimp
3675 */
3677{
3678 return QWindowsXPStylePrivate::useXP() ? QPalette() : QWindowsStyle::standardPalette();
3679}
3680
3681/*!
3682 \internal
3683 \reimp
3684*/
3685QPixmap QWindowsXPStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option) const
3686{
3687 if (!QWindowsXPStylePrivate::useXP())
3688 return QWindowsStyle::standardPixmap(standardPixmap, option);
3689
3690#if 0
3691 switch (standardPixmap) {
3692 case SP_TitleBarMaxButton:
3693 case SP_TitleBarCloseButton:
3694 if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3695 {
3696 if (widget && widget->isWindow()) {
3697 XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3698 if (theme.isValid()) {
3699 const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(option)).toSize();
3700 return QIcon(QWindowsStyle::standardPixmap(standardPixmap, option, widget)).pixmap(size);
3701 }
3702 }
3703 }
3704 break;
3705 default:
3706 break;
3707 }
3708#endif
3709 return QWindowsStyle::standardPixmap(standardPixmap, option);
3710}
3711
3712/*!
3713 \internal
3714 \reimp
3715*/
3716QIcon QWindowsXPStyle::standardIcon(StandardPixmap standardIcon,
3717 const QStyleOption *option) const
3718{
3720 return QWindowsStyle::standardIcon(standardIcon, option);
3721 }
3722#if 0
3723 QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func());
3724 switch (standardIcon) {
3725 case SP_TitleBarMaxButton:
3726 if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3727 {
3728 if (d->dockFloat.isNull()) {
3729 XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3730 WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3731 XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3732 WP_MAXBUTTON, MAXBS_NORMAL);
3733 if (theme.isValid()) {
3734 const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(option)).toSize();
3735 QPixmap pm(size);
3736 pm.fill(Qt::transparent);
3737 QPainter p(&pm);
3738 theme.painter = &p;
3739 theme.rect = QRect(QPoint(0, 0), size);
3740 d->drawBackground(theme);
3741 d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
3742 pm.fill(Qt::transparent);
3743 theme.stateId = MAXBS_PUSHED;
3744 d->drawBackground(theme);
3745 d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
3746 pm.fill(Qt::transparent);
3747 theme.stateId = MAXBS_HOT;
3748 d->drawBackground(theme);
3749 d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
3750 pm.fill(Qt::transparent);
3751 theme.stateId = MAXBS_INACTIVE;
3752 d->drawBackground(theme);
3753 d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
3754 }
3755 }
3756 if (widget && widget->isWindow())
3757 return d->dockFloat;
3758
3759 }
3760 break;
3761 case SP_TitleBarCloseButton:
3762 if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3763 {
3764 if (d->dockClose.isNull()) {
3765 XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3766 WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3767 if (theme.isValid()) {
3768 const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(option)).toSize();
3769 QPixmap pm(size);
3770 pm.fill(Qt::transparent);
3771 QPainter p(&pm);
3772 theme.painter = &p;
3773 theme.partId = WP_CLOSEBUTTON; // ####
3774 theme.rect = QRect(QPoint(0, 0), size);
3775 d->drawBackground(theme);
3776 d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
3777 pm.fill(Qt::transparent);
3778 theme.stateId = CBS_PUSHED;
3779 d->drawBackground(theme);
3780 d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
3781 pm.fill(Qt::transparent);
3782 theme.stateId = CBS_HOT;
3783 d->drawBackground(theme);
3784 d->dockClose.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
3785 pm.fill(Qt::transparent);
3786 theme.stateId = CBS_INACTIVE;
3787 d->drawBackground(theme);
3788 d->dockClose.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
3789 }
3790 }
3791 if (widget && widget->isWindow())
3792 return d->dockClose;
3793 }
3794 break;
3795 case SP_TitleBarNormalButton:
3796 if (qstyleoption_cast<const QStyleOptionDockWidget *>(option))
3797 {
3798 if (d->dockFloat.isNull()) {
3799 XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3800 WP_SMALLCLOSEBUTTON, CBS_NORMAL);
3801 XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme,
3802 WP_RESTOREBUTTON, RBS_NORMAL);
3803 if (theme.isValid()) {
3804 const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(option)).toSize();
3805 QPixmap pm(size);
3806 pm.fill(Qt::transparent);
3807 QPainter p(&pm);
3808 theme.painter = &p;
3809 theme.rect = QRect(QPoint(0, 0), size);
3810 d->drawBackground(theme);
3811 d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal
3812 pm.fill(Qt::transparent);
3813 theme.stateId = RBS_PUSHED;
3814 d->drawBackground(theme);
3815 d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed
3816 pm.fill(Qt::transparent);
3817 theme.stateId = RBS_HOT;
3818 d->drawBackground(theme);
3819 d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover
3820 pm.fill(Qt::transparent);
3821 theme.stateId = RBS_INACTIVE;
3822 d->drawBackground(theme);
3823 d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled
3824 }
3825 }
3826 if (widget && widget->isWindow())
3827 return d->dockFloat;
3828
3829 }
3830 break;
3831 default:
3832 break;
3833 }
3834#endif
3835 return QWindowsStyle::standardIcon(standardIcon, option);
3836}
3837
3838/*!
3839 \internal
3840
3841 Constructs a QWindowsXPStyle object.
3842*/
3846
3847#ifndef QT_NO_DEBUG_STREAM
3848QDebug operator<<(QDebug d, const XPThemeData &t)
3849{
3850 QDebugStateSaver saver(d);
3851 d.nospace();
3852 d << "XPThemeData(" << t.window << ", theme=#" << t.theme << ", " << t.htheme
3853 << ", partId=" << t.partId << ", stateId=" << t.stateId << ", rect=" << t.rect
3854 << ", mirrorHorizontally=" << t.mirrorHorizontally << ", mirrorVertically="
3855 << t.mirrorVertically << ", noBorder=" << t.noBorder << ", noContent=" << t.noContent
3856 << ", rotate=" << t.rotate << ')';
3857 return d;
3858}
3859
3860QDebug operator<<(QDebug d, const ThemeMapKey &k)
3861{
3862 QDebugStateSaver saver(d);
3863 d.nospace();
3864 d << "ThemeMapKey(theme=#" << k.theme
3865 << ", partId=" << k.partId << ", stateId=" << k.stateId
3866 << ", noBorder=" << k.noBorder << ", noContent=" << k.noContent << ')';
3867 return d;
3868}
3869
3870QDebug operator<<(QDebug d, const ThemeMapData &td)
3871{
3872 QDebugStateSaver saver(d);
3873 d.nospace();
3874 d << "ThemeMapData(alphaType=" << td.alphaType
3875 << ", dataValid=" << td.dataValid << ", partIsTransparent=" << td.partIsTransparent
3876 << ", hasAlphaChannel=" << td.hasAlphaChannel << ", wasAlphaSwapped=" << td.wasAlphaSwapped
3877 << ", hadInvalidAlpha=" << td.hadInvalidAlpha << ')';
3878 return d;
3879}
3880#endif // QT_NO_DEBUG_STREAM
3881
3882// Debugging code ---------------------------------------------------------------------[ START ]---
3883// The code for this point on is not compiled by default, but only used as assisting
3884// debugging code when you uncomment the DEBUG_XP_STYLE define at the top of the file.
3885
3886#ifdef DEBUG_XP_STYLE
3887// The schema file expects these to be defined by the user.
3888#define TMT_ENUMDEF 8
3889#define TMT_ENUMVAL TEXT('A')
3890#define TMT_ENUM TEXT('B')
3891#define SCHEMA_STRINGS // For 2nd pass on schema file
3893#include <tmschema.h>
3895
3896// A property's value, type and name combo
3897struct PropPair {
3898 int propValue;
3899 int propType;
3901};
3902
3903// Operator for sorting of PropPairs
3904bool operator<(PropPair a, PropPair b) {
3905 return wcscmp(a.propName, b.propName) < 0;
3906}
3907
3908// Our list of all possible properties
3909static QList<PropPair> all_props;
3910
3911
3912/*! \internal
3913 Dumps a portion of the full native DIB section double buffer.
3914 The DIB section double buffer is only used when doing special
3915 transformations to the theme part, or when the real double
3916 buffer in the paintengine does not have an HDC we may use
3917 directly.
3918 Since we cannot rely on the pixel data we get from Microsoft
3919 when drawing into the DIB section, we use this function to
3920 see the actual data we got, and can determin the appropriate
3921 action.
3922*/
3924{
3925 if (w && h) {
3926 static int pCount = 0;
3928
3929 const unsigned int bufferSize = bufferH * bufferW * 16;
3930 char *bufferDump = new char[bufferSize];
3932 char *bufferPos = bufferDump;
3933
3934 memset(bufferDump, 0, sizeof(bufferDump));
3935 bufferPos += snprintf(bufferPos, bufferEndAdress - bufferPos, "const int pixelBufferW%d = %d;\n", pCount, w);
3936 bufferPos += snprintf(bufferPos, bufferEndAdress - bufferPos, "const int pixelBufferH%d = %d;\n", pCount, h);
3937 bufferPos += snprintf(bufferPos, bufferEndAdress - bufferPos, "const unsigned DWORD pixelBuffer%d[] = {", pCount);
3938 for (int iy = 0; iy < h; ++iy) {
3940 bufPix = (DWORD*)(bufferPixels + (iy * bufferW * 4));
3941 for (int ix = 0; ix < w; ++ix) {
3943 ++bufPix;
3944 }
3945 }
3948
3949 delete[] bufferDump;
3950 ++pCount;
3951 }
3952}
3953
3954/*! \internal
3955 Shows the value of a given property for a part.
3956*/
3957static void showProperty(XPThemeData &themeData, const PropPair &prop)
3958{
3961 const char *originStr;
3962 switch (origin) {
3963 case PO_STATE:
3964 originStr = "State ";
3965 break;
3966 case PO_PART:
3967 originStr = "Part ";
3968 break;
3969 case PO_CLASS:
3970 originStr = "Class ";
3971 break;
3972 case PO_GLOBAL:
3973 originStr = "Globl ";
3974 break;
3975 case PO_NOTFOUND:
3976 default:
3977 originStr = "Unkwn ";
3978 break;
3979 }
3980
3981 switch (prop.propType) {
3982 case TMT_STRING:
3983 {
3984 wchar_t buffer[512];
3986 printf(" (%sString) %-20S: %S\n", originStr, prop.propName, buffer);
3987 }
3988 break;
3989 case TMT_ENUM:
3990 {
3991 int result = -1;
3993 printf(" (%sEnum) %-20S: %d\n", originStr, prop.propName, result);
3994 }
3995 break;
3996 case TMT_INT:
3997 {
3998 int result = -1;
4000 printf(" (%sint) %-20S: %d\n", originStr, prop.propName, result);
4001 }
4002 break;
4003 case TMT_BOOL:
4004 {
4005 BOOL result = false;
4007 printf(" (%sbool) %-20S: %d\n", originStr, prop.propName, result);
4008 }
4009 break;
4010 case TMT_COLOR:
4011 {
4012 COLORREF result = 0;
4014 printf(" (%scolor) %-20S: 0x%08X\n", originStr, prop.propName, result);
4015 }
4016 break;
4017 case TMT_MARGINS:
4018 {
4020 memset(&result, 0, sizeof(result));
4022 printf(" (%smargins) %-20S: (%d, %d, %d, %d)\n", originStr,
4024 }
4025 break;
4026 case TMT_FILENAME:
4027 {
4028 wchar_t buffer[512];
4030 printf(" (%sfilename)%-20S: %S\n", originStr, prop.propName, buffer);
4031 }
4032 break;
4033 case TMT_SIZE:
4034 {
4035 SIZE result1;
4036 SIZE result2;
4037 SIZE result3;
4038 memset(&result1, 0, sizeof(result1));
4039 memset(&result2, 0, sizeof(result2));
4040 memset(&result3, 0, sizeof(result3));
4044 printf(" (%ssize) %-20S: Min (%d, %d), True(%d, %d), Draw(%d, %d)\n", originStr, prop.propName,
4046 }
4047 break;
4048 case TMT_POSITION:
4049 {
4050 POINT result;
4051 memset(&result, 0, sizeof(result));
4053 printf(" (%sPosition)%-20S: (%d, %d)\n", originStr, prop.propName, result.x, result.y);
4054 }
4055 break;
4056 case TMT_RECT:
4057 {
4058 RECT result;
4059 memset(&result, 0, sizeof(result));
4061 printf(" (%sRect) %-20S: (%d, %d, %d, %d)\n", originStr, prop.propName, result.left, result.top, result.right, result.bottom);
4062 }
4063 break;
4064 case TMT_FONT:
4065 {
4067 memset(&result, 0, sizeof(result));
4069 printf(" (%sFont) %-20S: %S height(%d) width(%d) weight(%d)\n", originStr, prop.propName,
4071 }
4072 break;
4073 case TMT_INTLIST:
4074 {
4076 memset(&result, 0, sizeof(result));
4078 printf(" (%sInt list)%-20S: { ", originStr, prop.propName);
4079 for (int i = 0; i < result.iValueCount; ++i)
4080 printf("%d ", result.iValues[i]);
4081 printf("}\n");
4082 }
4083 break;
4084 default:
4085 printf(" %s%S : Unknown property type (%d)!\n", originStr, prop.propName, prop.propType);
4086 }
4087}
4088
4089/*! \internal
4090 Dump all valid properties for a part.
4091 If it's the first time this function is called, then the name,
4092 enum value and documentation of all properties are shown, as
4093 well as all global properties.
4094*/
4096{
4097 if (!all_props.count()) {
4099 for (int i = 0; i < infoTable->iPropCount; ++i) {
4103
4104 switch (propType) {
4105 case TMT_ENUMDEF:
4106 case TMT_ENUMVAL:
4107 continue;
4108 default:
4109 if (propType != propValue) {
4110 PropPair prop;
4115 }
4116 }
4117 }
4119
4120 {// List all properties
4121 printf("part properties count = %d:\n", all_props.count());
4122 printf(" Enum Property Name Description\n");
4123 printf("-----------------------------------------------------------\n");
4124 wchar_t themeName[256];
4125 pGetCurrentThemeName(themeName, 256, 0, 0, 0, 0);
4126 for (int j = 0; j < all_props.count(); ++j) {
4128 wchar_t buf[500];
4130 printf("%3d: (%4d) %-20S %S\n", j, prop.propValue, prop.propName, buf);
4131 }
4132 }
4133
4134 {// Show Global values
4135 printf("Global Properties:\n");
4136 for (int j = 0; j < all_props.count(); ++j) {
4140 if (origin == PO_GLOBAL) {
4142 }
4143 }
4144 }
4145 }
4146
4147 for (int j = 0; j < all_props.count(); ++j) {
4151 if (origin != PO_NOTFOUND)
4152 {
4154 }
4155 }
4156}
4157#endif
4158// Debugging code -----------------------------------------------------------------------[ END ]---
4159
4160} //namespace QQC2
4161
4162QT_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)