summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/windows
diff options
context:
space:
mode:
authorGabriel de Dietrich <gabriel.dietrich-de@nokia.com>2012-10-10 13:58:19 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2012-10-16 15:29:15 +0200
commitc3b939818af214392505c58b93d5f8df46fa09d8 (patch)
treec52ffb1de9ad3cd9fda641b454a5979dfeb98426 /src/plugins/platforms/windows
parentc153f471d2b45ea0fbe9848b59403807c1cd37ab (diff)
QPA: Introducing QPlatformTheme standardPixmap(), fileIconPixmap()
The basic idea is that the platform theme is now responsible for providing the pixmaps for the given standard name, or any file or directory. Then, the QStyle implementation should query the platform theme for the pixmaps, and build the icons accordingly using ThemeHint::IconPixmapSizes. Same thing for QFileIconProvider. This also opens future support for getting platform dependent pixmaps in QtQuick components. Also includes the implementation for the Cocoa (QCocoaTheme) and Windows (QWindowsTheme) platform plugins. Task-number: QTBUG-27450 Change-Id: I4e8406585d970a9af481be10f6643cf0abbc38a3 Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@digia.com> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com> Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com> Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
Diffstat (limited to 'src/plugins/platforms/windows')
-rw-r--r--src/plugins/platforms/windows/qtwindows_additional.h15
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp5
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h3
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp230
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.h3
5 files changed, 255 insertions, 1 deletions
diff --git a/src/plugins/platforms/windows/qtwindows_additional.h b/src/plugins/platforms/windows/qtwindows_additional.h
index f5ea2cf6bf..3566367e41 100644
--- a/src/plugins/platforms/windows/qtwindows_additional.h
+++ b/src/plugins/platforms/windows/qtwindows_additional.h
@@ -83,6 +83,21 @@
#define CHILDID_SELF 0
#define WM_GETOBJECT 0x003D
+#ifndef SIID_SHIELD // Shell structures for icons.
+typedef struct _SHSTOCKICONINFO
+{
+ DWORD cbSize;
+ HICON hIcon;
+ int iSysImageIndex;
+ int iIcon;
+ WCHAR szPath[MAX_PATH];
+} SHSTOCKICONINFO;
+
+# define SIID_SHIELD 77
+# define SHGFI_ADDOVERLAYS 0x20
+# define SHGFI_OVERLAYINDEX 0x40
+#endif // SIID_SHIELD
+
#if !defined(__MINGW64_VERSION_MAJOR)
#define STATE_SYSTEM_HASPOPUP 0x40000000
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 98c17deba9..3d4871d7a2 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -210,7 +210,9 @@ bool QWindowsUser32DLL::initTouch()
\ingroup qt-lighthouse-win
*/
-QWindowsShell32DLL::QWindowsShell32DLL() : sHCreateItemFromParsingName(0)
+QWindowsShell32DLL::QWindowsShell32DLL()
+ : sHCreateItemFromParsingName(0)
+ , sHGetStockIconInfo(0)
{
}
@@ -218,6 +220,7 @@ void QWindowsShell32DLL::init()
{
QSystemLibrary library(QStringLiteral("shell32"));
sHCreateItemFromParsingName = (SHCreateItemFromParsingName)(library.resolve("SHCreateItemFromParsingName"));
+ sHGetStockIconInfo = (SHGetStockIconInfo)library.resolve("SHGetStockIconInfo");
}
QWindowsUser32DLL QWindowsContext::user32dll;
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index 450d6c8f4b..78cd104fbe 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -49,6 +49,7 @@
#include <QtCore/QSharedPointer>
struct IBindCtx;
+struct _SHSTOCKICONINFO;
QT_BEGIN_NAMESPACE
@@ -99,8 +100,10 @@ struct QWindowsShell32DLL
inline void init();
typedef HRESULT (WINAPI *SHCreateItemFromParsingName)(PCWSTR, IBindCtx *, const GUID&, void **);
+ typedef HRESULT (WINAPI *SHGetStockIconInfo)(int , int , _SHSTOCKICONINFO *);
SHCreateItemFromParsingName sHCreateItemFromParsingName;
+ SHGetStockIconInfo sHGetStockIconInfo;
};
#endif // Q_OS_WINCE
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index 7b3ffc633f..748ba09a90 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -55,9 +55,13 @@
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
#include <QtCore/QSysInfo>
+#include <QtCore/QCache>
#include <QtGui/QPalette>
#include <QtGui/QGuiApplication>
+#include <QtGui/QPainter>
+#include <QtGui/QPixmapCache>
#include <qpa/qwindowsysteminterface.h>
+#include <private/qsystemlibrary_p.h>
QT_BEGIN_NAMESPACE
@@ -348,6 +352,11 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const
return QVariant(int(WindowsKeyboardScheme));
case UiEffects:
return QVariant(uiEffects());
+ case IconPixmapSizes: {
+ QList<int> sizes;
+ sizes << 16 << 32;
+ return QVariant::fromValue(sizes);
+ }
default:
break;
}
@@ -433,4 +442,225 @@ void QWindowsTheme::windowsThemeChanged(QWindow * window)
QWindowSystemInterface::handleThemeChange(window);
}
+// Defined in qpixmap_win.cpp
+Q_GUI_EXPORT QPixmap qt_pixmapFromWinHICON(HICON icon);
+
+static QPixmap loadIconFromShell32(int resourceId, QSizeF size)
+{
+#ifdef Q_OS_WINCE
+ HMODULE hmod = LoadLibrary(L"ceshell");
+#else
+ HMODULE hmod = QSystemLibrary::load(L"shell32");
+#endif
+ if (hmod) {
+ HICON iconHandle = (HICON)LoadImage(hmod, MAKEINTRESOURCE(resourceId), IMAGE_ICON, size.width(), size.height(), 0);
+ if (iconHandle) {
+ QPixmap iconpixmap = qt_pixmapFromWinHICON(iconHandle);
+ DestroyIcon(iconHandle);
+ return iconpixmap;
+ }
+ }
+ return QPixmap();
+}
+
+QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
+{
+ int resourceId = -1;
+ LPCTSTR iconName = 0;
+ switch (sp) {
+ case DriveCDIcon:
+ case DriveDVDIcon:
+ resourceId = 12;
+ break;
+ case DriveNetIcon:
+ resourceId = 10;
+ break;
+ case DriveHDIcon:
+ resourceId = 9;
+ break;
+ case DriveFDIcon:
+ resourceId = 7;
+ break;
+ case FileIcon:
+ case FileLinkIcon:
+ resourceId = 1;
+ break;
+ case DirIcon:
+ case DirLinkIcon:
+ case DirClosedIcon:
+ resourceId = 4;
+ break;
+ case DesktopIcon:
+ resourceId = 35;
+ break;
+ case ComputerIcon:
+ resourceId = 16;
+ break;
+ case DirOpenIcon:
+ case DirLinkOpenIcon:
+ resourceId = 5;
+ break;
+ case FileDialogNewFolder:
+ resourceId = 319;
+ break;
+ case DirHomeIcon:
+ resourceId = 235;
+ break;
+ case TrashIcon:
+ resourceId = 191;
+ break;
+ case MessageBoxInformation:
+ iconName = IDI_INFORMATION;
+ break;
+ case MessageBoxWarning:
+ iconName = IDI_WARNING;
+ break;
+ case MessageBoxCritical:
+ iconName = IDI_ERROR;
+ break;
+ case MessageBoxQuestion:
+ iconName = IDI_QUESTION;
+ break;
+ case VistaShield:
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA
+ && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based)) {
+ if (!QWindowsContext::shell32dll.sHGetStockIconInfo)
+ return QPixmap();
+ QPixmap pixmap;
+ SHSTOCKICONINFO iconInfo;
+ memset(&iconInfo, 0, sizeof(iconInfo));
+ iconInfo.cbSize = sizeof(iconInfo);
+ const int iconSize = size.width() > 16 ? SHGFI_LARGEICON : SHGFI_SMALLICON;
+ if (QWindowsContext::shell32dll.sHGetStockIconInfo(SIID_SHIELD, SHGFI_ICON | iconSize, &iconInfo) == S_OK) {
+ pixmap = qt_pixmapFromWinHICON(iconInfo.hIcon);
+ DestroyIcon(iconInfo.hIcon);
+ return pixmap;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (resourceId != -1) {
+ QPixmap pixmap = loadIconFromShell32(resourceId, size);
+ if (!pixmap.isNull()) {
+ if (sp == FileLinkIcon || sp == DirLinkIcon || sp == DirLinkOpenIcon) {
+ QPainter painter(&pixmap);
+ QPixmap link = loadIconFromShell32(30, size);
+ painter.drawPixmap(0, 0, size.width(), size.height(), link);
+ }
+ return pixmap;
+ }
+ }
+
+ if (iconName) {
+ HICON iconHandle = LoadIcon(NULL, iconName);
+ QPixmap pixmap = qt_pixmapFromWinHICON(iconHandle);
+ DestroyIcon(iconHandle);
+ if (!pixmap.isNull())
+ return pixmap;
+ }
+
+ return QPlatformTheme::standardPixmap(sp, size);
+}
+
+static QString dirIconPixmapCacheKey(int iIcon, int iconSize)
+{
+ QString key = QLatin1String("qt_dir_") + QString::number(iIcon);
+ if (iconSize == SHGFI_LARGEICON)
+ key += QLatin1Char('l');
+ return key;
+}
+
+template <typename T>
+class FakePointer
+{
+public:
+
+ Q_STATIC_ASSERT_X(sizeof(T) <= sizeof(void *), "FakePointers can only go that far.");
+
+ static FakePointer *create(T thing)
+ {
+ return reinterpret_cast<FakePointer *>(thing);
+ }
+
+ T operator * () const
+ {
+ return reinterpret_cast<T>(this);
+ }
+
+ void operator delete (void *) {}
+};
+
+QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size) const
+{
+ /* We don't use the variable, but by storing it statically, we
+ * ensure CoInitialize is only called once. */
+ static HRESULT comInit = CoInitialize(NULL);
+ Q_UNUSED(comInit);
+
+ static QCache<QString, FakePointer<int> > dirIconEntryCache(1000);
+ static QMutex mx;
+
+ QPixmap pixmap;
+ const QString filePath = QDir::toNativeSeparators(fileInfo.filePath());
+ int iconSize = size.width() > 16 ? SHGFI_LARGEICON : SHGFI_SMALLICON;
+
+ bool cacheableDirIcon = fileInfo.isDir() && !fileInfo.isRoot();
+ if (cacheableDirIcon) {
+ QMutexLocker locker(&mx);
+ int iIcon = **dirIconEntryCache.object(filePath);
+ if (iIcon) {
+ QPixmapCache::find(dirIconPixmapCacheKey(iIcon, iconSize), pixmap);
+ if (pixmap.isNull()) // Let's keep both caches in sync
+ dirIconEntryCache.remove(filePath);
+ else
+ return pixmap;
+ }
+ }
+
+ SHFILEINFO info;
+ unsigned int flags =
+#ifndef Q_OS_WINCE
+ SHGFI_ICON|iconSize|SHGFI_SYSICONINDEX|SHGFI_ADDOVERLAYS|SHGFI_OVERLAYINDEX;
+#else
+ iconSize|SHGFI_SYSICONINDEX;
+#endif // Q_OS_WINCE
+ unsigned long val = SHGetFileInfo((const wchar_t *)filePath.utf16(), 0,
+ &info, sizeof(SHFILEINFO), flags);
+
+ // Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
+ if (val && info.hIcon) {
+ QString key;
+ if (cacheableDirIcon) {
+ //using the unique icon index provided by windows save us from duplicate keys
+ key = dirIconPixmapCacheKey(info.iIcon, iconSize);
+ QPixmapCache::find(key, pixmap);
+ if (!pixmap.isNull()) {
+ QMutexLocker locker(&mx);
+ dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
+ }
+ }
+
+ if (pixmap.isNull()) {
+ pixmap = qt_pixmapFromWinHICON(info.hIcon);
+ if (!pixmap.isNull()) {
+ if (cacheableDirIcon) {
+ QMutexLocker locker(&mx);
+ QPixmapCache::insert(key, pixmap);
+ dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
+ }
+ } else {
+ qWarning("QWindowsTheme::fileIconPixmap() no icon found");
+ }
+ }
+ DestroyIcon(info.hIcon);
+ }
+
+ if (!pixmap.isNull())
+ return pixmap;
+ return QPlatformTheme::fileIconPixmap(fileInfo, size);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h
index 7e885b8f3e..89ef527e76 100644
--- a/src/plugins/platforms/windows/qwindowstheme.h
+++ b/src/plugins/platforms/windows/qwindowstheme.h
@@ -67,6 +67,9 @@ public:
virtual const QFont *font(Font type = SystemFont) const
{ return m_fonts[type]; }
+ virtual QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const;
+ virtual QPixmap fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size) const;
+
void windowsThemeChanged(QWindow *window);
static const char *name;