Windows: Introduce QFileDialog::DontUseCustomDirectoryIcons

Folders can have a custom icon, set by the user. Some system
folders also have one, for example c:\windows\fonts.

This option allows you to disable this behavior, you'll get the
folder directory icon.

As a side-effect, you'll get a very big performance improvement
on removable/network media: 2 seconds vs 60 seconds on a SDCard
with 10000 folders.

Change-Id: Id55ea628186e0a6523585ec7a4ff622d6f5da505
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
This commit is contained in:
Sérgio Martins 2013-04-29 12:58:23 +01:00 committed by The Qt Project
parent 0e9ea64edc
commit 46685f755b
13 changed files with 126 additions and 25 deletions

View File

@ -217,13 +217,14 @@ public:
enum FileDialogOption enum FileDialogOption
{ {
ShowDirsOnly = 0x00000001, ShowDirsOnly = 0x00000001,
DontResolveSymlinks = 0x00000002, DontResolveSymlinks = 0x00000002,
DontConfirmOverwrite = 0x00000004, DontConfirmOverwrite = 0x00000004,
DontUseSheet = 0x00000008, DontUseSheet = 0x00000008,
DontUseNativeDialog = 0x00000010, DontUseNativeDialog = 0x00000010,
ReadOnly = 0x00000020, ReadOnly = 0x00000020,
HideNameFilterDetails = 0x00000040 HideNameFilterDetails = 0x00000040,
DontUseCustomDirectoryIcons = 0x00000080
}; };
Q_DECLARE_FLAGS(FileDialogOptions, FileDialogOption) Q_DECLARE_FLAGS(FileDialogOptions, FileDialogOption)

View File

@ -208,10 +208,12 @@ QPixmap QPlatformTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) co
return QPixmap(); return QPixmap();
} }
QPixmap QPlatformTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size) const QPixmap QPlatformTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size,
QPlatformTheme::IconOptions iconOptions) const
{ {
Q_UNUSED(fileInfo); Q_UNUSED(fileInfo);
Q_UNUSED(size); Q_UNUSED(size);
Q_UNUSED(iconOptions);
// TODO Should return QCommonStyle pixmaps? // TODO Should return QCommonStyle pixmaps?
return QPixmap(); return QPixmap();
} }

View File

@ -253,6 +253,11 @@ public:
AnimateToolBoxUiEffect = 0x40 AnimateToolBoxUiEffect = 0x40
}; };
enum IconOption {
DontUseCustomDirectoryIcons = 0x01
};
Q_DECLARE_FLAGS(IconOptions, IconOption)
explicit QPlatformTheme(); explicit QPlatformTheme();
virtual ~QPlatformTheme(); virtual ~QPlatformTheme();
@ -274,7 +279,8 @@ public:
virtual QVariant themeHint(ThemeHint hint) const; virtual QVariant themeHint(ThemeHint hint) const;
virtual QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const; virtual QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const;
virtual QPixmap fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size) const; virtual QPixmap fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size,
QPlatformTheme::IconOptions iconOptions = 0) const;
virtual QIconEngine *createIconEngine(const QString &iconName) const; virtual QIconEngine *createIconEngine(const QString &iconName) const;

View File

@ -68,7 +68,9 @@ public:
const QPalette *palette(Palette type = SystemPalette) const; const QPalette *palette(Palette type = SystemPalette) const;
const QFont *font(Font type = SystemFont) const; const QFont *font(Font type = SystemFont) const;
QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const; QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const;
QPixmap fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size) const; QPixmap fileIconPixmap(const QFileInfo &fileInfo,
const QSizeF &size,
QPlatformTheme::IconOptions options = 0) const;
QVariant themeHint(ThemeHint hint) const; QVariant themeHint(ThemeHint hint) const;

View File

@ -249,8 +249,10 @@ QPixmap QCocoaTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
return QPlatformTheme::standardPixmap(sp, size); return QPlatformTheme::standardPixmap(sp, size);
} }
QPixmap QCocoaTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size) const QPixmap QCocoaTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size,
QPlatformTheme::IconOptions iconOptions) const
{ {
Q_UNUSED(iconOptions);
QCocoaAutoReleasePool pool; QCocoaAutoReleasePool pool;
NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:QCFString::toNSString(fileInfo.canonicalFilePath())]; NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:QCFString::toNSString(fileInfo.canonicalFilePath())];

View File

@ -595,7 +595,8 @@ public:
void operator delete (void *) {} void operator delete (void *) {}
}; };
QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size) const QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size,
QPlatformTheme::IconOptions iconOptions) const
{ {
/* We don't use the variable, but by storing it statically, we /* We don't use the variable, but by storing it statically, we
* ensure CoInitialize is only called once. */ * ensure CoInitialize is only called once. */
@ -604,6 +605,8 @@ QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &s
static QCache<QString, FakePointer<int> > dirIconEntryCache(1000); static QCache<QString, FakePointer<int> > dirIconEntryCache(1000);
static QMutex mx; static QMutex mx;
static int defaultFolderIIcon = 0;
const bool useDefaultFolderIcon = iconOptions & QPlatformTheme::DontUseCustomDirectoryIcons;
QPixmap pixmap; QPixmap pixmap;
const QString filePath = QDir::toNativeSeparators(fileInfo.filePath()); const QString filePath = QDir::toNativeSeparators(fileInfo.filePath());
@ -612,7 +615,8 @@ QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &s
bool cacheableDirIcon = fileInfo.isDir() && !fileInfo.isRoot(); bool cacheableDirIcon = fileInfo.isDir() && !fileInfo.isRoot();
if (cacheableDirIcon) { if (cacheableDirIcon) {
QMutexLocker locker(&mx); QMutexLocker locker(&mx);
int iIcon = **dirIconEntryCache.object(filePath); int iIcon = (useDefaultFolderIcon && defaultFolderIIcon) ? defaultFolderIIcon
: **dirIconEntryCache.object(filePath);
if (iIcon) { if (iIcon) {
QPixmapCache::find(dirIconPixmapCacheKey(iIcon, iconSize), pixmap); QPixmapCache::find(dirIconPixmapCacheKey(iIcon, iconSize), pixmap);
if (pixmap.isNull()) // Let's keep both caches in sync if (pixmap.isNull()) // Let's keep both caches in sync
@ -629,13 +633,24 @@ QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &s
#else #else
iconSize|SHGFI_SYSICONINDEX; iconSize|SHGFI_SYSICONINDEX;
#endif // Q_OS_WINCE #endif // Q_OS_WINCE
unsigned long val = SHGetFileInfo((const wchar_t *)filePath.utf16(), 0, unsigned long val = 0;
&info, sizeof(SHFILEINFO), flags); if (cacheableDirIcon && useDefaultFolderIcon) {
flags |= SHGFI_USEFILEATTRIBUTES;
val = SHGetFileInfo(L"dummy",
FILE_ATTRIBUTE_DIRECTORY,
&info, sizeof(SHFILEINFO), flags);
} else {
val = SHGetFileInfo(reinterpret_cast<const wchar_t *>(filePath.utf16()), 0,
&info, sizeof(SHFILEINFO), flags);
}
// Even if GetFileInfo returns a valid result, hIcon can be empty in some cases // Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
if (val && info.hIcon) { if (val && info.hIcon) {
QString key; QString key;
if (cacheableDirIcon) { if (cacheableDirIcon) {
if (useDefaultFolderIcon && !defaultFolderIIcon)
defaultFolderIIcon = info.iIcon;
//using the unique icon index provided by windows save us from duplicate keys //using the unique icon index provided by windows save us from duplicate keys
key = dirIconPixmapCacheKey(info.iIcon, iconSize); key = dirIconPixmapCacheKey(info.iIcon, iconSize);
QPixmapCache::find(key, pixmap); QPixmapCache::find(key, pixmap);

View File

@ -68,7 +68,8 @@ public:
{ return m_fonts[type]; } { return m_fonts[type]; }
virtual QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const; virtual QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const;
virtual QPixmap fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size) const; virtual QPixmap fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size,
QPlatformTheme::IconOptions iconOptions = 0) const;
void windowsThemeChanged(QWindow *window); void windowsThemeChanged(QWindow *window);

View File

@ -255,6 +255,11 @@ Q_WIDGETS_EXPORT _qt_filedialog_save_file_url_hook qt_filedialog_save_file_url_h
static functions will always be an application modal dialog. If static functions will always be an application modal dialog. If
you want to use sheets, use QFileDialog::open() instead. you want to use sheets, use QFileDialog::open() instead.
\value DontUseCustomDirectoryIcons Always use the default directory icon.
Some platforms allow the user to set a different icon. Custom icon lookup
cause a big performance impact over network or removable drives.
Setting this will enable the QFileIconProvider::DontUseCustomDirectoryIcons
option in the icon provider. This enum value was added in Qt 5.2.
*/ */
/*! /*!
@ -754,6 +759,15 @@ void QFileDialog::setOptions(Options options)
if (changed & ShowDirsOnly) if (changed & ShowDirsOnly)
setFilter((options & ShowDirsOnly) ? filter() & ~QDir::Files : filter() | QDir::Files); setFilter((options & ShowDirsOnly) ? filter() & ~QDir::Files : filter() | QDir::Files);
if (changed & DontUseCustomDirectoryIcons) {
QFileIconProvider::Options providerOptions = iconProvider()->options();
if (options & DontUseCustomDirectoryIcons)
providerOptions |= QFileIconProvider::DontUseCustomDirectoryIcons;
else
providerOptions &= ~QFileIconProvider::DontUseCustomDirectoryIcons;
iconProvider()->setOptions(providerOptions);
}
} }
QFileDialog::Options QFileDialog::options() const QFileDialog::Options QFileDialog::options() const

View File

@ -84,13 +84,14 @@ public:
enum Option enum Option
{ {
ShowDirsOnly = 0x00000001, ShowDirsOnly = 0x00000001,
DontResolveSymlinks = 0x00000002, DontResolveSymlinks = 0x00000002,
DontConfirmOverwrite = 0x00000004, DontConfirmOverwrite = 0x00000004,
DontUseSheet = 0x00000008, DontUseSheet = 0x00000008,
DontUseNativeDialog = 0x00000010, DontUseNativeDialog = 0x00000010,
ReadOnly = 0x00000020, ReadOnly = 0x00000020,
HideNameFilterDetails = 0x00000040 HideNameFilterDetails = 0x00000040,
DontUseCustomDirectoryIcons = 0x00000080
}; };
Q_DECLARE_FLAGS(Options, Option) Q_DECLARE_FLAGS(Options, Option)

View File

@ -83,6 +83,16 @@ QT_BEGIN_NAMESPACE
\value File \value File
*/ */
/*!
\enum QFileIconProvider::Option
\since 5.2
\value DontUseCustomDirectoryIcons Always use the default directory icon.
Some platforms allow the user to set a different icon. Custom icon lookup
cause a big performance impact over network or removable drives.
*/
class QFileIconProviderPrivate class QFileIconProviderPrivate
{ {
Q_DECLARE_PUBLIC(QFileIconProvider) Q_DECLARE_PUBLIC(QFileIconProvider)
@ -94,6 +104,7 @@ public:
QFileIconProvider *q_ptr; QFileIconProvider *q_ptr;
const QString homePath; const QString homePath;
QFileIconProvider::Options options;
private: private:
mutable QIcon file; mutable QIcon file;
@ -192,6 +203,31 @@ QFileIconProvider::~QFileIconProvider()
{ {
} }
/*!
\since 5.2
Sets \a options that affect the icon provider.
\sa options()
*/
void QFileIconProvider::setOptions(QFileIconProvider::Options options)
{
Q_D(QFileIconProvider);
d->options = options;
}
/*!
\since 5.2
Returns all the options that affect the icon provider.
By default, all options are disabled.
\sa setOptions()
*/
QFileIconProvider::Options QFileIconProvider::options() const
{
Q_D(const QFileIconProvider);
return d->options;
}
/*! /*!
Returns an icon set for the given \a type. Returns an icon set for the given \a type.
*/ */
@ -228,6 +264,7 @@ static bool isCacheable(const QFileInfo &fi)
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
// On windows it's faster to just look at the file extensions. QTBUG-13182 // On windows it's faster to just look at the file extensions. QTBUG-13182
const QString fileExtension = fi.suffix(); const QString fileExtension = fi.suffix();
// Will return false for .exe, .lnk and .ico extensions
return fileExtension.compare(QLatin1String("exe"), Qt::CaseInsensitive) && return fileExtension.compare(QLatin1String("exe"), Qt::CaseInsensitive) &&
fileExtension.compare(QLatin1String("lnk"), Qt::CaseInsensitive) && fileExtension.compare(QLatin1String("lnk"), Qt::CaseInsensitive) &&
fileExtension.compare(QLatin1String("ico"), Qt::CaseInsensitive); fileExtension.compare(QLatin1String("ico"), Qt::CaseInsensitive);
@ -247,7 +284,6 @@ QIcon QFileIconProviderPrivate::getIcon(const QFileInfo &fi) const
if (sizes.isEmpty()) if (sizes.isEmpty())
return retIcon; return retIcon;
const QString fileExtension = fi.suffix().toUpper();
const QString keyBase = QLatin1String("qt_.") + fi.suffix().toUpper(); const QString keyBase = QLatin1String("qt_.") + fi.suffix().toUpper();
bool cacheable = isCacheable(fi); bool cacheable = isCacheable(fi);
@ -269,8 +305,12 @@ QIcon QFileIconProviderPrivate::getIcon(const QFileInfo &fi) const
} }
} }
QPlatformTheme::IconOptions iconOptions;
if (options & QFileIconProvider::DontUseCustomDirectoryIcons)
iconOptions |= QPlatformTheme::DontUseCustomDirectoryIcons;
Q_FOREACH (int size, sizes) { Q_FOREACH (int size, sizes) {
QPixmap pixmap = theme->fileIconPixmap(fi, QSizeF(size, size)); QPixmap pixmap = theme->fileIconPixmap(fi, QSizeF(size, size), iconOptions);
if (!pixmap.isNull()) { if (!pixmap.isNull()) {
retIcon.addPixmap(pixmap); retIcon.addPixmap(pixmap);
if (cacheable) if (cacheable)

View File

@ -59,10 +59,19 @@ public:
QFileIconProvider(); QFileIconProvider();
virtual ~QFileIconProvider(); virtual ~QFileIconProvider();
enum IconType { Computer, Desktop, Trashcan, Network, Drive, Folder, File }; enum IconType { Computer, Desktop, Trashcan, Network, Drive, Folder, File };
enum Option {
DontUseCustomDirectoryIcons = 0x00000001
};
Q_DECLARE_FLAGS(Options, Option)
virtual QIcon icon(IconType type) const; virtual QIcon icon(IconType type) const;
virtual QIcon icon(const QFileInfo &info) const; virtual QIcon icon(const QFileInfo &info) const;
virtual QString type(const QFileInfo &info) const; virtual QString type(const QFileInfo &info) const;
void setOptions(Options options);
Options options() const;
private: private:
Q_DECLARE_PRIVATE(QFileIconProvider) Q_DECLARE_PRIVATE(QFileIconProvider)
QScopedPointer<QFileIconProviderPrivate> d_ptr; QScopedPointer<QFileIconProviderPrivate> d_ptr;
@ -71,6 +80,8 @@ private:
#endif // QT_NO_FILEICONPROVIDER #endif // QT_NO_FILEICONPROVIDER
Q_DECLARE_OPERATORS_FOR_FLAGS(QFileIconProvider::Options)
QT_END_NAMESPACE QT_END_NAMESPACE
#endif // QFILEICONPROVIDER_H #endif // QFILEICONPROVIDER_H

View File

@ -154,6 +154,7 @@ FileDialogPanel::FileDialogPanel(QWidget *parent)
, m_nameFilterDetailsVisible(new QCheckBox(tr("Name filter details visible"))) , m_nameFilterDetailsVisible(new QCheckBox(tr("Name filter details visible")))
, m_resolveSymLinks(new QCheckBox(tr("Resolve symlinks"))) , m_resolveSymLinks(new QCheckBox(tr("Resolve symlinks")))
, m_native(new QCheckBox(tr("Use native dialog"))) , m_native(new QCheckBox(tr("Use native dialog")))
, m_customDirIcons(new QCheckBox(tr("Don't use custom directory icons")))
, m_acceptMode(createCombo(this, acceptModeComboData, sizeof(acceptModeComboData)/sizeof(ComboData))) , m_acceptMode(createCombo(this, acceptModeComboData, sizeof(acceptModeComboData)/sizeof(ComboData)))
, m_fileMode(createCombo(this, fileModeComboData, sizeof(fileModeComboData)/sizeof(ComboData))) , m_fileMode(createCombo(this, fileModeComboData, sizeof(fileModeComboData)/sizeof(ComboData)))
, m_viewMode(createCombo(this, viewModeComboData, sizeof(viewModeComboData)/sizeof(ComboData))) , m_viewMode(createCombo(this, viewModeComboData, sizeof(viewModeComboData)/sizeof(ComboData)))
@ -178,6 +179,7 @@ FileDialogPanel::FileDialogPanel(QWidget *parent)
optionsLayout->addRow(m_nameFilterDetailsVisible); optionsLayout->addRow(m_nameFilterDetailsVisible);
optionsLayout->addRow(m_resolveSymLinks); optionsLayout->addRow(m_resolveSymLinks);
optionsLayout->addRow(m_readOnly); optionsLayout->addRow(m_readOnly);
optionsLayout->addRow(m_customDirIcons);
// Files // Files
QGroupBox *filesGroupBox = new QGroupBox(tr("Files / Filters")); QGroupBox *filesGroupBox = new QGroupBox(tr("Files / Filters"));
@ -322,6 +324,8 @@ QFileDialog::Options FileDialogPanel::options() const
result |= QFileDialog::DontConfirmOverwrite; result |= QFileDialog::DontConfirmOverwrite;
if (!m_native->isChecked()) if (!m_native->isChecked())
result |= QFileDialog::DontUseNativeDialog; result |= QFileDialog::DontUseNativeDialog;
if (!m_customDirIcons->isChecked())
result |= QFileDialog::DontUseCustomDirectoryIcons;
return result; return result;
} }
@ -454,6 +458,7 @@ void FileDialogPanel::restoreDefaults()
m_resolveSymLinks->setChecked(d.resolveSymlinks()); m_resolveSymLinks->setChecked(d.resolveSymlinks());
m_readOnly->setChecked(d.isReadOnly()); m_readOnly->setChecked(d.isReadOnly());
m_native->setChecked(true); m_native->setChecked(true);
m_customDirIcons->setChecked(d.testOption(QFileDialog::DontUseCustomDirectoryIcons));
m_directory->setText(QDir::homePath()); m_directory->setText(QDir::homePath());
m_defaultSuffix->setText(QLatin1String("txt")); m_defaultSuffix->setText(QLatin1String("txt"));
m_nameFilters->setPlainText(QLatin1String("Any files (*)\nImage files (*.png *.xpm *.jpg)\nText files (*.txt)")); m_nameFilters->setPlainText(QLatin1String("Any files (*)\nImage files (*.png *.xpm *.jpg)\nText files (*.txt)"));

View File

@ -92,6 +92,7 @@ private:
QCheckBox *m_nameFilterDetailsVisible; QCheckBox *m_nameFilterDetailsVisible;
QCheckBox *m_resolveSymLinks; QCheckBox *m_resolveSymLinks;
QCheckBox *m_native; QCheckBox *m_native;
QCheckBox *m_customDirIcons;
QComboBox *m_acceptMode; QComboBox *m_acceptMode;
QComboBox *m_fileMode; QComboBox *m_fileMode;
QComboBox *m_viewMode; QComboBox *m_viewMode;