Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40// SHSTOCKICONINFO is only available since Vista
41#if _WIN32_WINNT < 0x0601
42# undef _WIN32_WINNT
43# define _WIN32_WINNT 0x0601
44#endif
45
46#include "qwindowstheme.h"
47#include "qwindowsmenu.h"
48#include "qwindowsdialoghelpers.h"
49#include "qwindowscontext.h"
50#include "qwindowsintegration.h"
51#if QT_CONFIG(systemtrayicon)
52# include "qwindowssystemtrayicon.h"
53#endif
54#include "qt_windows.h"
55#include <commctrl.h>
56#include <objbase.h>
57#ifndef Q_CC_MINGW
58# include <commoncontrols.h>
59#endif
60#include <shellapi.h>
61
62#include <QtCore/qvariant.h>
63#include <QtCore/qcoreapplication.h>
64#include <QtCore/qdebug.h>
65#include <QtCore/qtextstream.h>
66#include <QtCore/qsysinfo.h>
67#include <QtCore/qcache.h>
68#include <QtCore/qthread.h>
69#include <QtCore/qmutex.h>
70#include <QtCore/qwaitcondition.h>
71#include <QtGui/qcolor.h>
72#include <QtGui/qpalette.h>
73#include <QtGui/qguiapplication.h>
74#include <QtGui/qpainter.h>
75#include <QtGui/qpixmapcache.h>
76#include <qpa/qwindowsysteminterface.h>
77#include <QtThemeSupport/private/qabstractfileiconengine_p.h>
78#include <QtFontDatabaseSupport/private/qwindowsfontdatabase_p.h>
79#include <private/qhighdpiscaling_p.h>
80#include <private/qsystemlibrary_p.h>
81
82#include <algorithm>
83
84#if defined(__IImageList_INTERFACE_DEFINED__) && defined(__IID_DEFINED__)
85# define USE_IIMAGELIST
86#endif
87
88QT_BEGIN_NAMESPACE
89
90static inline QColor COLORREFToQColor(COLORREF cr)
91{
92 return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr));
93}
94
95static inline QTextStream& operator<<(QTextStream &str, const QColor &c)
96{
97 str.setIntegerBase(16);
98 str.setFieldWidth(2);
99 str.setPadChar(QLatin1Char('0'));
100 str << " rgb: #" << c.red() << c.green() << c.blue();
101 str.setIntegerBase(10);
102 str.setFieldWidth(0);
103 return str;
104}
105
106static inline bool booleanSystemParametersInfo(UINT what, bool defaultValue)
107{
108 BOOL result;
109 if (SystemParametersInfo(what, 0, &result, 0))
110 return result != FALSE;
111 return defaultValue;
112}
113
114static inline DWORD dWordSystemParametersInfo(UINT what, DWORD defaultValue)
115{
116 DWORD result;
117 if (SystemParametersInfo(what, 0, &result, 0))
118 return result;
119 return defaultValue;
120}
121
122static inline QColor mixColors(const QColor &c1, const QColor &c2)
123{
124 return {(c1.red() + c2.red()) / 2,
125 (c1.green() + c2.green()) / 2,
126 (c1.blue() + c2.blue()) / 2};
127}
128
129static inline QColor getSysColor(int index)
130{
131 return COLORREFToQColor(GetSysColor(index));
132}
133
134// QTBUG-48823/Windows 10: SHGetFileInfo() (as called by item views on file system
135// models has been observed to trigger a WM_PAINT on the mainwindow. Suppress the
136// behavior by running it in a thread.
137
138struct QShGetFileInfoParams
139{
140 QShGetFileInfoParams(const QString &fn, DWORD a, SHFILEINFO *i, UINT f, bool *r)
141 : fileName(fn), attributes(a), flags(f), info(i), result(r)
142 { }
143
144 const QString &fileName;
145 const DWORD attributes;
146 const UINT flags;
147 SHFILEINFO *const info;
148 bool *const result;
149};
150
151class QShGetFileInfoThread : public QThread
152{
153public:
154 explicit QShGetFileInfoThread()
155 : QThread(), m_params(nullptr)
156 {
157 connect(this, &QThread::finished, this, &QObject::deleteLater);
158 }
159
160 void run() override
161 {
162 m_init = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
163
164 QMutexLocker readyLocker(&m_readyMutex);
165 while (!m_cancelled.loadRelaxed()) {
166 if (!m_params && !m_cancelled.loadRelaxed()
167 && !m_readyCondition.wait(&m_readyMutex, 1000))
168 continue;
169
170 if (m_params) {
171 const QString fileName = m_params->fileName;
172 SHFILEINFO info;
173 const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(fileName.utf16()),
174 m_params->attributes, &info, sizeof(SHFILEINFO),
175 m_params->flags);
176 m_doneMutex.lock();
177 if (!m_cancelled.loadRelaxed()) {
178 *m_params->result = result;
179 memcpy(m_params->info, &info, sizeof(SHFILEINFO));
180 }
181 m_params = nullptr;
182
183 m_doneCondition.wakeAll();
184 m_doneMutex.unlock();
185 }
186 }
187
188 if (m_init != S_FALSE)
189 CoUninitialize();
190 }
191
192 bool runWithParams(QShGetFileInfoParams *params, unsigned long timeOutMSecs)
193 {
194 QMutexLocker doneLocker(&m_doneMutex);
195
196 m_readyMutex.lock();
197 m_params = params;
198 m_readyCondition.wakeAll();
199 m_readyMutex.unlock();
200
201 return m_doneCondition.wait(&m_doneMutex, timeOutMSecs);
202 }
203
204 void cancel()
205 {
206 QMutexLocker doneLocker(&m_doneMutex);
207 m_cancelled.storeRelaxed(1);
208 m_readyCondition.wakeAll();
209 }
210
211private:
212 HRESULT m_init;
213 QShGetFileInfoParams *m_params;
214 QAtomicInt m_cancelled;
215 QWaitCondition m_readyCondition;
216 QWaitCondition m_doneCondition;
217 QMutex m_readyMutex;
218 QMutex m_doneMutex;
219};
220
221static bool shGetFileInfoBackground(const QString &fileName, DWORD attributes,
222 SHFILEINFO *info, UINT flags,
223 unsigned long timeOutMSecs = 5000)
224{
225 static QShGetFileInfoThread *getFileInfoThread = nullptr;
226 if (!getFileInfoThread) {
227 getFileInfoThread = new QShGetFileInfoThread;
228 getFileInfoThread->start();
229 }
230
231 bool result = false;
232 QShGetFileInfoParams params(fileName, attributes, info, flags, &result);
233 if (!getFileInfoThread->runWithParams(&params, timeOutMSecs)) {
234 // Cancel and reset getFileInfoThread. It'll
235 // be reinitialized the next time we get called.
236 getFileInfoThread->cancel();
237 getFileInfoThread = nullptr;
238 qWarning().noquote() << "SHGetFileInfo() timed out for " << fileName;
239 return false;
240 }
241 return result;
242}
243
244// from QStyle::standardPalette
245static inline QPalette standardPalette()
246{
247 QColor backgroundColor(0xd4, 0xd0, 0xc8); // win 2000 grey
248 QColor lightColor(backgroundColor.lighter());
249 QColor darkColor(backgroundColor.darker());
250 const QBrush darkBrush(darkColor);
251 QColor midColor(Qt::gray);
252 QPalette palette(Qt::black, backgroundColor, lightColor, darkColor,
253 midColor, Qt::black, Qt::white);
254 palette.setBrush(QPalette::Disabled, QPalette::WindowText, darkBrush);
255 palette.setBrush(QPalette::Disabled, QPalette::Text, darkBrush);
256 palette.setBrush(QPalette::Disabled, QPalette::ButtonText, darkBrush);
257 palette.setBrush(QPalette::Disabled, QPalette::Base, QBrush(backgroundColor));
258 return palette;
259}
260
261static inline QPalette systemPalette()
262{
263 QPalette result = standardPalette();
264 result.setColor(QPalette::WindowText, getSysColor(COLOR_WINDOWTEXT));
265 result.setColor(QPalette::Button, getSysColor(COLOR_BTNFACE));
266 result.setColor(QPalette::Light, getSysColor(COLOR_BTNHIGHLIGHT));
267 result.setColor(QPalette::Dark, getSysColor(COLOR_BTNSHADOW));
268 result.setColor(QPalette::Mid, result.button().color().darker(150));
269 result.setColor(QPalette::Text, getSysColor(COLOR_WINDOWTEXT));
270 result.setColor(QPalette::BrightText, getSysColor(COLOR_BTNHIGHLIGHT));
271 result.setColor(QPalette::Base, getSysColor(COLOR_WINDOW));
272 result.setColor(QPalette::Window, getSysColor(COLOR_BTNFACE));
273 result.setColor(QPalette::ButtonText, getSysColor(COLOR_BTNTEXT));
274 result.setColor(QPalette::Midlight, getSysColor(COLOR_3DLIGHT));
275 result.setColor(QPalette::Shadow, getSysColor(COLOR_3DDKSHADOW));
276 result.setColor(QPalette::Highlight, getSysColor(COLOR_HIGHLIGHT));
277 result.setColor(QPalette::HighlightedText, getSysColor(COLOR_HIGHLIGHTTEXT));
278 result.setColor(QPalette::Link, Qt::blue);
279 result.setColor(QPalette::LinkVisited, Qt::magenta);
280 result.setColor(QPalette::Inactive, QPalette::Button, result.button().color());
281 result.setColor(QPalette::Inactive, QPalette::Window, result.window().color());
282 result.setColor(QPalette::Inactive, QPalette::Light, result.light().color());
283 result.setColor(QPalette::Inactive, QPalette::Dark, result.dark().color());
284
285 if (result.midlight() == result.button())
286 result.setColor(QPalette::Midlight, result.button().color().lighter(110));
287 if (result.window() != result.base()) {
288 result.setColor(QPalette::Inactive, QPalette::Highlight, result.color(QPalette::Inactive, QPalette::Window));
289 result.setColor(QPalette::Inactive, QPalette::HighlightedText, result.color(QPalette::Inactive, QPalette::Text));
290 }
291
292 const QColor disabled =
293 mixColors(result.windowText().color(), result.button().color());
294
295 result.setColorGroup(QPalette::Disabled, result.windowText(), result.button(),
296 result.light(), result.dark(), result.mid(),
297 result.text(), result.brightText(), result.base(),
298 result.window());
299 result.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
300 result.setColor(QPalette::Disabled, QPalette::Text, disabled);
301 result.setColor(QPalette::Disabled, QPalette::ButtonText, disabled);
302 result.setColor(QPalette::Disabled, QPalette::Highlight,
303 getSysColor(COLOR_HIGHLIGHT));
304 result.setColor(QPalette::Disabled, QPalette::HighlightedText,
305 getSysColor(COLOR_HIGHLIGHTTEXT));
306 result.setColor(QPalette::Disabled, QPalette::Base,
307 result.window().color());
308 return result;
309}
310
311static inline QPalette toolTipPalette(const QPalette &systemPalette)
312{
313 QPalette result(systemPalette);
314 const QColor tipBgColor(getSysColor(COLOR_INFOBK));
315 const QColor tipTextColor(getSysColor(COLOR_INFOTEXT));
316
317 result.setColor(QPalette::All, QPalette::Button, tipBgColor);
318 result.setColor(QPalette::All, QPalette::Window, tipBgColor);
319 result.setColor(QPalette::All, QPalette::Text, tipTextColor);
320 result.setColor(QPalette::All, QPalette::WindowText, tipTextColor);
321 result.setColor(QPalette::All, QPalette::ButtonText, tipTextColor);
322 result.setColor(QPalette::All, QPalette::Button, tipBgColor);
323 result.setColor(QPalette::All, QPalette::Window, tipBgColor);
324 result.setColor(QPalette::All, QPalette::Text, tipTextColor);
325 result.setColor(QPalette::All, QPalette::WindowText, tipTextColor);
326 result.setColor(QPalette::All, QPalette::ButtonText, tipTextColor);
327 result.setColor(QPalette::All, QPalette::ToolTipBase, tipBgColor);
328 result.setColor(QPalette::All, QPalette::ToolTipText, tipTextColor);
329 const QColor disabled =
330 mixColors(result.windowText().color(), result.button().color());
331 result.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
332 result.setColor(QPalette::Disabled, QPalette::Text, disabled);
333 result.setColor(QPalette::Disabled, QPalette::ToolTipText, disabled);
334 result.setColor(QPalette::Disabled, QPalette::Base, Qt::white);
335 result.setColor(QPalette::Disabled, QPalette::BrightText, Qt::white);
336 result.setColor(QPalette::Disabled, QPalette::ToolTipBase, Qt::white);
337 return result;
338}
339
340static inline QPalette menuPalette(const QPalette &systemPalette)
341{
342 QPalette result(systemPalette);
343 const QColor menuColor(getSysColor(COLOR_MENU));
344 const QColor menuTextColor(getSysColor(COLOR_MENUTEXT));
345 const QColor disabled(getSysColor(COLOR_GRAYTEXT));
346 // we might need a special color group for the result.
347 result.setColor(QPalette::Active, QPalette::Button, menuColor);
348 result.setColor(QPalette::Active, QPalette::Text, menuTextColor);
349 result.setColor(QPalette::Active, QPalette::WindowText, menuTextColor);
350 result.setColor(QPalette::Active, QPalette::ButtonText, menuTextColor);
351 result.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
352 result.setColor(QPalette::Disabled, QPalette::Text, disabled);
353 const bool isFlat = booleanSystemParametersInfo(SPI_GETFLATMENU, false);
354 result.setColor(QPalette::Disabled, QPalette::Highlight,
355 getSysColor(isFlat ? COLOR_MENUHILIGHT : COLOR_HIGHLIGHT));
356 result.setColor(QPalette::Disabled, QPalette::HighlightedText, disabled);
357 result.setColor(QPalette::Disabled, QPalette::Button,
358 result.color(QPalette::Active, QPalette::Button));
359 result.setColor(QPalette::Inactive, QPalette::Button,
360 result.color(QPalette::Active, QPalette::Button));
361 result.setColor(QPalette::Inactive, QPalette::Text,
362 result.color(QPalette::Active, QPalette::Text));
363 result.setColor(QPalette::Inactive, QPalette::WindowText,
364 result.color(QPalette::Active, QPalette::WindowText));
365 result.setColor(QPalette::Inactive, QPalette::ButtonText,
366 result.color(QPalette::Active, QPalette::ButtonText));
367 result.setColor(QPalette::Inactive, QPalette::Highlight,
368 result.color(QPalette::Active, QPalette::Highlight));
369 result.setColor(QPalette::Inactive, QPalette::HighlightedText,
370 result.color(QPalette::Active, QPalette::HighlightedText));
371 result.setColor(QPalette::Inactive, QPalette::ButtonText,
372 systemPalette.color(QPalette::Inactive, QPalette::Dark));
373 return result;
374}
375
376static inline QPalette *menuBarPalette(const QPalette &menuPalette)
377{
378 QPalette *result = nullptr;
379 if (booleanSystemParametersInfo(SPI_GETFLATMENU, false)) {
380 result = new QPalette(menuPalette);
381 const QColor menubar(getSysColor(COLOR_MENUBAR));
382 result->setColor(QPalette::Active, QPalette::Button, menubar);
383 result->setColor(QPalette::Disabled, QPalette::Button, menubar);
384 result->setColor(QPalette::Inactive, QPalette::Button, menubar);
385 }
386 return result;
387}
388
389const char *QWindowsTheme::name = "windows";
390QWindowsTheme *QWindowsTheme::m_instance = nullptr;
391
392QWindowsTheme::QWindowsTheme()
393{
394 m_instance = this;
395 std::fill(m_fonts, m_fonts + NFonts, nullptr);
396 std::fill(m_palettes, m_palettes + NPalettes, nullptr);
397 refresh();
398 refreshIconPixmapSizes();
399}
400
401QWindowsTheme::~QWindowsTheme()
402{
403 clearPalettes();
404 clearFonts();
405 m_instance = nullptr;
406}
407
408static inline QStringList iconThemeSearchPaths()
409{
410 const QFileInfo appDir(QCoreApplication::applicationDirPath() + QLatin1String("/icons"));
411 return appDir.isDir() ? QStringList(appDir.absoluteFilePath()) : QStringList();
412}
413
414static inline QStringList styleNames()
415{
416 return { QStringLiteral("WindowsVista"), QStringLiteral("Windows") };
417}
418
419static inline int uiEffects()
420{
421 int result = QPlatformTheme::HoverEffect;
422 if (booleanSystemParametersInfo(SPI_GETUIEFFECTS, false))
423 result |= QPlatformTheme::GeneralUiEffect;
424 if (booleanSystemParametersInfo(SPI_GETMENUANIMATION, false))
425 result |= QPlatformTheme::AnimateMenuUiEffect;
426 if (booleanSystemParametersInfo(SPI_GETMENUFADE, false))
427 result |= QPlatformTheme::FadeMenuUiEffect;
428 if (booleanSystemParametersInfo(SPI_GETCOMBOBOXANIMATION, false))
429 result |= QPlatformTheme::AnimateComboUiEffect;
430 if (booleanSystemParametersInfo(SPI_GETTOOLTIPANIMATION, false))
431 result |= QPlatformTheme::AnimateTooltipUiEffect;
432 return result;
433}
434
435QVariant QWindowsTheme::themeHint(ThemeHint hint) const
436{
437 switch (hint) {
438 case UseFullScreenForPopupMenu:
439 return QVariant(true);
440 case DialogButtonBoxLayout:
441 return QVariant(QPlatformDialogHelper::WinLayout);
442 case IconThemeSearchPaths:
443 return QVariant(iconThemeSearchPaths());
444 case StyleNames:
445 return QVariant(styleNames());
446 case TextCursorWidth:
447 return QVariant(int(dWordSystemParametersInfo(SPI_GETCARETWIDTH, 1u)));
448 case DropShadow:
449 return QVariant(booleanSystemParametersInfo(SPI_GETDROPSHADOW, false));
450 case MaximumScrollBarDragDistance:
451 return QVariant(qRound(qreal(QWindowsContext::instance()->defaultDPI()) * 1.375));
452 case KeyboardScheme:
453 return QVariant(int(WindowsKeyboardScheme));
454 case UiEffects:
455 return QVariant(uiEffects());
456 case IconPixmapSizes:
457 return QVariant::fromValue(m_fileIconSizes);
458 case DialogSnapToDefaultButton:
459 return QVariant(booleanSystemParametersInfo(SPI_GETSNAPTODEFBUTTON, false));
460 case ContextMenuOnMouseRelease:
461 return QVariant(true);
462 case WheelScrollLines: {
463 int result = 3;
464 const DWORD scrollLines = dWordSystemParametersInfo(SPI_GETWHEELSCROLLLINES, DWORD(result));
465 if (scrollLines != DWORD(-1)) // Special value meaning "scroll one screen", unimplemented in Qt.
466 result = int(scrollLines);
467 return QVariant(result);
468 }
469 default:
470 break;
471 }
472 return QPlatformTheme::themeHint(hint);
473}
474
475void QWindowsTheme::clearPalettes()
476{
477 qDeleteAll(m_palettes, m_palettes + NPalettes);
478 std::fill(m_palettes, m_palettes + NPalettes, nullptr);
479}
480
481void QWindowsTheme::refreshPalettes()
482{
483
484 if (!QGuiApplication::desktopSettingsAware())
485 return;
486 m_palettes[SystemPalette] = new QPalette(systemPalette());
487 m_palettes[ToolTipPalette] = new QPalette(toolTipPalette(*m_palettes[SystemPalette]));
488 m_palettes[MenuPalette] = new QPalette(menuPalette(*m_palettes[SystemPalette]));
489 m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette]);
490}
491
492void QWindowsTheme::clearFonts()
493{
494 qDeleteAll(m_fonts, m_fonts + NFonts);
495 std::fill(m_fonts, m_fonts + NFonts, nullptr);
496}
497
498void QWindowsTheme::refreshFonts()
499{
500 clearFonts();
501 if (!QGuiApplication::desktopSettingsAware())
502 return;
503 NONCLIENTMETRICS ncm;
504 QWindowsContext::nonClientMetrics(&ncm);
505 const QFont menuFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMenuFont);
506 const QFont messageBoxFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
507 const QFont statusFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfStatusFont);
508 const QFont titleFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfCaptionFont);
509 QFont fixedFont(QStringLiteral("Courier New"), messageBoxFont.pointSize());
510 fixedFont.setStyleHint(QFont::TypeWriter);
511
512 LOGFONT lfIconTitleFont;
513 SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIconTitleFont), &lfIconTitleFont, 0);
514 const QFont iconTitleFont = QWindowsFontDatabase::LOGFONT_to_QFont(lfIconTitleFont);
515
516 m_fonts[SystemFont] = new QFont(QWindowsFontDatabase::systemDefaultFont());
517 m_fonts[MenuFont] = new QFont(menuFont);
518 m_fonts[MenuBarFont] = new QFont(menuFont);
519 m_fonts[MessageBoxFont] = new QFont(messageBoxFont);
520 m_fonts[TipLabelFont] = new QFont(statusFont);
521 m_fonts[StatusBarFont] = new QFont(statusFont);
522 m_fonts[MdiSubWindowTitleFont] = new QFont(titleFont);
523 m_fonts[DockWidgetTitleFont] = new QFont(titleFont);
524 m_fonts[ItemViewFont] = new QFont(iconTitleFont);
525 m_fonts[FixedFont] = new QFont(fixedFont);
526}
527
528enum FileIconSize {
529 // Standard icons obtainable via shGetFileInfo(), SHGFI_SMALLICON, SHGFI_LARGEICON
530 SmallFileIcon, LargeFileIcon,
531 // Larger icons obtainable via SHGetImageList()
532 ExtraLargeFileIcon,
533 JumboFileIcon, // Vista onwards
534 FileIconSizeCount
535};
536
537bool QWindowsTheme::usePlatformNativeDialog(DialogType type) const
538{
539 return QWindowsDialogs::useHelper(type);
540}
541
542QPlatformDialogHelper *QWindowsTheme::createPlatformDialogHelper(DialogType type) const
543{
544 return QWindowsDialogs::createHelper(type);
545}
546
547#if QT_CONFIG(systemtrayicon)
548QPlatformSystemTrayIcon *QWindowsTheme::createPlatformSystemTrayIcon() const
549{
550 return new QWindowsSystemTrayIcon;
551}
552#endif
553
554void QWindowsTheme::windowsThemeChanged(QWindow * window)
555{
556 refresh();
557 QWindowSystemInterface::handleThemeChange(window);
558}
559
560static int fileIconSizes[FileIconSizeCount];
561
562void QWindowsTheme::refreshIconPixmapSizes()
563{
564 // Standard sizes: 16, 32, 48, 256
565 fileIconSizes[SmallFileIcon] = GetSystemMetrics(SM_CXSMICON); // corresponds to SHGFI_SMALLICON);
566 fileIconSizes[LargeFileIcon] = GetSystemMetrics(SM_CXICON); // corresponds to SHGFI_LARGEICON
567 fileIconSizes[ExtraLargeFileIcon] =
568 fileIconSizes[LargeFileIcon] + fileIconSizes[LargeFileIcon] / 2;
569 fileIconSizes[JumboFileIcon] = 8 * fileIconSizes[LargeFileIcon]; // empirical, has not been observed to work
570
571#ifdef USE_IIMAGELIST
572 int *availEnd = fileIconSizes + JumboFileIcon + 1;
573#else
574 int *availEnd = fileIconSizes + LargeFileIcon + 1;
575#endif // USE_IIMAGELIST
576 m_fileIconSizes = QAbstractFileIconEngine::toSizeList(fileIconSizes, availEnd);
577 qCDebug(lcQpaWindows) << __FUNCTION__ << m_fileIconSizes;
578}
579
580// Defined in qpixmap_win.cpp
581Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon);
582
583static QPixmap loadIconFromShell32(int resourceId, QSizeF size)
584{
585 if (const HMODULE hmod = QSystemLibrary::load(L"shell32")) {
586 auto iconHandle =
587 static_cast<HICON>(LoadImage(hmod, MAKEINTRESOURCE(resourceId),
588 IMAGE_ICON, int(size.width()), int(size.height()), 0));
589 if (iconHandle) {
590 QPixmap iconpixmap = qt_pixmapFromWinHICON(iconHandle);
591 DestroyIcon(iconHandle);
592 return iconpixmap;
593 }
594 }
595 return QPixmap();
596}
597
598QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &pixmapSize) const
599{
600 int resourceId = -1;
601 SHSTOCKICONID stockId = SIID_INVALID;
602 UINT stockFlags = 0;
603 LPCTSTR iconName = nullptr;
604 switch (sp) {
605 case DriveCDIcon:
606 stockId = SIID_DRIVECD;
607 resourceId = 12;
608 break;
609 case DriveDVDIcon:
610 stockId = SIID_DRIVEDVD;
611 resourceId = 12;
612 break;
613 case DriveNetIcon:
614 stockId = SIID_DRIVENET;
615 resourceId = 10;
616 break;
617 case DriveHDIcon:
618 stockId = SIID_DRIVEFIXED;
619 resourceId = 9;
620 break;
621 case DriveFDIcon:
622 stockId = SIID_DRIVE35;
623 resourceId = 7;
624 break;
625 case FileLinkIcon:
626 stockFlags = SHGSI_LINKOVERLAY;
627 Q_FALLTHROUGH();
628 case FileIcon:
629 stockId = SIID_DOCNOASSOC;
630 resourceId = 1;
631 break;
632 case DirLinkIcon:
633 stockFlags = SHGSI_LINKOVERLAY;
634 Q_FALLTHROUGH();
635 case DirClosedIcon:
636 case DirIcon:
637 stockId = SIID_FOLDER;
638 resourceId = 4;
639 break;
640 case DesktopIcon:
641 resourceId = 35;
642 break;
643 case ComputerIcon:
644 resourceId = 16;
645 break;
646 case DirLinkOpenIcon:
647 stockFlags = SHGSI_LINKOVERLAY;
648 Q_FALLTHROUGH();
649 case DirOpenIcon:
650 stockId = SIID_FOLDEROPEN;
651 resourceId = 5;
652 break;
653 case FileDialogNewFolder:
654 stockId = SIID_FOLDER;
655 resourceId = 319;
656 break;
657 case DirHomeIcon:
658 resourceId = 235;
659 break;
660 case TrashIcon:
661 stockId = SIID_RECYCLER;
662 resourceId = 191;
663 break;
664 case MessageBoxInformation:
665 stockId = SIID_INFO;
666 iconName = IDI_INFORMATION;
667 break;
668 case MessageBoxWarning:
669 stockId = SIID_WARNING;
670 iconName = IDI_WARNING;
671 break;
672 case MessageBoxCritical:
673 stockId = SIID_ERROR;
674 iconName = IDI_ERROR;
675 break;
676 case MessageBoxQuestion:
677 stockId = SIID_HELP;
678 iconName = IDI_QUESTION;
679 break;
680 case VistaShield:
681 stockId = SIID_SHIELD;
682 break;
683 default:
684 break;
685 }
686
687 if (stockId != SIID_INVALID) {
688 QPixmap pixmap;
689 SHSTOCKICONINFO iconInfo;
690 memset(&iconInfo, 0, sizeof(iconInfo));
691 iconInfo.cbSize = sizeof(iconInfo);
692 stockFlags |= (pixmapSize.width() > 16 ? SHGFI_LARGEICON : SHGFI_SMALLICON);
693 if (SHGetStockIconInfo(stockId, SHGFI_ICON | stockFlags, &iconInfo) == S_OK) {
694 pixmap = qt_pixmapFromWinHICON(iconInfo.hIcon);
695 DestroyIcon(iconInfo.hIcon);
696 return pixmap;
697 }
698 }
699
700 if (resourceId != -1) {
701 QPixmap pixmap = loadIconFromShell32(resourceId, pixmapSize);
702 if (!pixmap.isNull()) {
703 if (sp == FileLinkIcon || sp == DirLinkIcon || sp == DirLinkOpenIcon) {
704 QPainter painter(&pixmap);
705 QPixmap link = loadIconFromShell32(30, pixmapSize);
706 painter.drawPixmap(0, 0, int(pixmapSize.width()), int(pixmapSize.height()), link);
707 }
708 return pixmap;
709 }
710 }
711
712 if (iconName) {
713 HICON iconHandle = LoadIcon(nullptr, iconName);
714 QPixmap pixmap = qt_pixmapFromWinHICON(iconHandle);
715 DestroyIcon(iconHandle);
716 if (!pixmap.isNull())
717 return pixmap;
718 }
719
720 return QPlatformTheme::standardPixmap(sp, pixmapSize);
721}
722
723enum { // Shell image list ids
724 sHIL_EXTRALARGE = 0x2, // 48x48 or user-defined
725 sHIL_JUMBO = 0x4 // 256x256 (Vista or later)
726};
727
728static QString dirIconPixmapCacheKey(int iIcon, int iconSize, int imageListSize)
729{
730 QString key = QLatin1String("qt_dir_") + QString::number(iIcon);
731 if (iconSize == SHGFI_LARGEICON)
732 key += QLatin1Char('l');
733 switch (imageListSize) {
734 case sHIL_EXTRALARGE:
735 key += QLatin1Char('e');
736 break;
737 case sHIL_JUMBO:
738 key += QLatin1Char('j');
739 break;
740 }
741 return key;
742}
743
744template <typename T>
745class FakePointer
746{
747public:
748
749 Q_STATIC_ASSERT_X(sizeof(T) <= sizeof(void *), "FakePointers can only go that far.");
750
751 static FakePointer *create(T thing)
752 {
753 return reinterpret_cast<FakePointer *>(qintptr(thing));
754 }
755
756 T operator * () const
757 {
758 return T(qintptr(this));
759 }
760
761 void operator delete (void *) {}
762};
763
764// Shell image list helper functions.
765
766static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info)
767{
768 QPixmap result;
769#ifdef USE_IIMAGELIST
770 // For MinGW:
771 static const IID iID_IImageList = {0x46eb5926, 0x582e, 0x4017, {0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x9, 0x50}};
772
773 IImageList *imageList = nullptr;
774 HRESULT hr = SHGetImageList(iImageList, iID_IImageList, reinterpret_cast<void **>(&imageList));
775 if (hr != S_OK)
776 return result;
777 HICON hIcon;
778 hr = imageList->GetIcon(info.iIcon, ILD_TRANSPARENT, &hIcon);
779 if (hr == S_OK) {
780 result = qt_pixmapFromWinHICON(hIcon);
781 DestroyIcon(hIcon);
782 }
783 imageList->Release();
784#else
785 Q_UNUSED(iImageList)
786 Q_UNUSED(info)
787#endif // USE_IIMAGELIST
788 return result;
789}
790
791class QWindowsFileIconEngine : public QAbstractFileIconEngine
792{
793public:
794 explicit QWindowsFileIconEngine(const QFileInfo &info, QPlatformTheme::IconOptions opts) :
795 QAbstractFileIconEngine(info, opts) {}
796
797 QList<QSize> availableSizes(QIcon::Mode = QIcon::Normal, QIcon::State = QIcon::Off) const override
798 { return QWindowsTheme::instance()->availableFileIconSizes(); }
799
800protected:
801 QString cacheKey() const override;
802 QPixmap filePixmap(const QSize &size, QIcon::Mode mode, QIcon::State) override;
803};
804
805QString QWindowsFileIconEngine::cacheKey() const
806{
807 // Cache directories unless custom or drives, which have custom icons depending on type
808 if ((options() & QPlatformTheme::DontUseCustomDirectoryIcons) && fileInfo().isDir() && !fileInfo().isRoot())
809 return QStringLiteral("qt_/directory/");
810 if (!fileInfo().isFile())
811 return QString();
812 // Return "" for .exe, .lnk and .ico extensions.
813 // It is faster to just look at the file extensions;
814 // avoiding slow QFileInfo::isExecutable() (QTBUG-13182)
815 QString suffix = fileInfo().suffix();
816 if (!suffix.compare(QLatin1String("exe"), Qt::CaseInsensitive)
817 || !suffix.compare(QLatin1String("lnk"), Qt::CaseInsensitive)
818 || !suffix.compare(QLatin1String("ico"), Qt::CaseInsensitive)) {
819 return QString();
820 }
821 return QLatin1String("qt_.")
822 + (suffix.isEmpty() ? fileInfo().fileName() : std::move(suffix).toUpper()); // handle "Makefile" ;)
823}
824
825QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon::State)
826{
827 /* We don't use the variable, but by storing it statically, we
828 * ensure CoInitialize is only called once. */
829 static HRESULT comInit = CoInitialize(nullptr);
830 Q_UNUSED(comInit);
831
832 static QCache<QString, FakePointer<int> > dirIconEntryCache(1000);
833 static QMutex mx;
834 static int defaultFolderIIcon = -1;
835 const bool useDefaultFolderIcon = options() & QPlatformTheme::DontUseCustomDirectoryIcons;
836
837 QPixmap pixmap;
838 const QString filePath = QDir::toNativeSeparators(fileInfo().filePath());
839 const int width = int(size.width());
840 const int iconSize = width > fileIconSizes[SmallFileIcon] ? SHGFI_LARGEICON : SHGFI_SMALLICON;
841 const int requestedImageListSize =
842#ifdef USE_IIMAGELIST
843 width > fileIconSizes[ExtraLargeFileIcon]
844 ? sHIL_JUMBO
845 : (width > fileIconSizes[LargeFileIcon] ? sHIL_EXTRALARGE : 0);
846#else
847 0;
848#endif // !USE_IIMAGELIST
849 bool cacheableDirIcon = fileInfo().isDir() && !fileInfo().isRoot();
850 if (cacheableDirIcon) {
851 QMutexLocker locker(&mx);
852 int iIcon = (useDefaultFolderIcon && defaultFolderIIcon >= 0) ? defaultFolderIIcon
853 : **dirIconEntryCache.object(filePath);
854 if (iIcon) {
855 QPixmapCache::find(dirIconPixmapCacheKey(iIcon, iconSize, requestedImageListSize),
856 &pixmap);
857 if (pixmap.isNull()) // Let's keep both caches in sync
858 dirIconEntryCache.remove(filePath);
859 else
860 return pixmap;
861 }
862 }
863
864 SHFILEINFO info;
865 unsigned int flags = SHGFI_ICON | iconSize | SHGFI_SYSICONINDEX | SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX;
866 DWORD attributes = 0;
867 QString path = filePath;
868 if (cacheableDirIcon && useDefaultFolderIcon) {
869 flags |= SHGFI_USEFILEATTRIBUTES;
870 attributes |= FILE_ATTRIBUTE_DIRECTORY;
871 path = QStringLiteral("dummy");
872 } else if (!fileInfo().exists()) {
873 flags |= SHGFI_USEFILEATTRIBUTES;
874 attributes |= FILE_ATTRIBUTE_NORMAL;
875 }
876 const bool val = shGetFileInfoBackground(path, attributes, &info, flags);
877
878 // Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
879 if (val && info.hIcon) {
880 QString key;
881 if (cacheableDirIcon) {
882 if (useDefaultFolderIcon && defaultFolderIIcon < 0)
883 defaultFolderIIcon = info.iIcon;
884
885 //using the unique icon index provided by windows save us from duplicate keys
886 key = dirIconPixmapCacheKey(info.iIcon, iconSize, requestedImageListSize);
887 QPixmapCache::find(key, &pixmap);
888 if (!pixmap.isNull()) {
889 QMutexLocker locker(&mx);
890 dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
891 }
892 }
893
894 if (pixmap.isNull()) {
895 if (requestedImageListSize) {
896 pixmap = pixmapFromShellImageList(requestedImageListSize, info);
897 if (pixmap.isNull() && requestedImageListSize == sHIL_JUMBO)
898 pixmap = pixmapFromShellImageList(sHIL_EXTRALARGE, info);
899 }
900 if (pixmap.isNull())
901 pixmap = qt_pixmapFromWinHICON(info.hIcon);
902 if (!pixmap.isNull()) {
903 if (cacheableDirIcon) {
904 QMutexLocker locker(&mx);
905 QPixmapCache::insert(key, pixmap);
906 dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
907 }
908 } else {
909 qWarning("QWindowsTheme::fileIconPixmap() no icon found");
910 }
911 }
912 DestroyIcon(info.hIcon);
913 }
914
915 return pixmap;
916}
917
918QIcon QWindowsTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions) const
919{
920 return QIcon(new QWindowsFileIconEngine(fileInfo, iconOptions));
921}
922
923static inline bool doUseNativeMenus()
924{
925 const unsigned options = QWindowsIntegration::instance()->options();
926 if ((options & QWindowsIntegration::NoNativeMenus) != 0)
927 return false;
928 if ((options & QWindowsIntegration::AlwaysUseNativeMenus) != 0)
929 return true;
930 // "Auto" mode: For non-widget or Quick Controls 2 applications
931 if (!QCoreApplication::instance()->inherits("QApplication"))
932 return true;
933 const QWindowList &topLevels = QGuiApplication::topLevelWindows();
934 for (const QWindow *t : topLevels) {
935 if (t->inherits("QQuickApplicationWindow"))
936 return true;
937 }
938 return false;
939}
940
941bool QWindowsTheme::useNativeMenus()
942{
943 static const bool result = doUseNativeMenus();
944 return result;
945}
946
947QPlatformMenuItem *QWindowsTheme::createPlatformMenuItem() const
948{
949 qCDebug(lcQpaMenus) << __FUNCTION__;
950 return QWindowsTheme::useNativeMenus() ? new QWindowsMenuItem : nullptr;
951}
952
953QPlatformMenu *QWindowsTheme::createPlatformMenu() const
954{
955 qCDebug(lcQpaMenus) << __FUNCTION__;
956 // We create a popup menu here, since it will likely be used as context
957 // menu. Submenus should be created the factory functions of
958 // QPlatformMenu/Bar. Note though that Quick Controls 1 will use this
959 // function for submenus as well, but this has been found to work.
960 return QWindowsTheme::useNativeMenus() ? new QWindowsPopupMenu : nullptr;
961}
962
963QPlatformMenuBar *QWindowsTheme::createPlatformMenuBar() const
964{
965 qCDebug(lcQpaMenus) << __FUNCTION__;
966 return QWindowsTheme::useNativeMenus() ? new QWindowsMenuBar : nullptr;
967}
968
969void QWindowsTheme::showPlatformMenuBar()
970{
971 qCDebug(lcQpaMenus) << __FUNCTION__;
972}
973
974QT_END_NAMESPACE
975

Warning: That file was not part of the compilation database. It may have many parsing errors.