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
{
ShowDirsOnly = 0x00000001,
DontResolveSymlinks = 0x00000002,
DontConfirmOverwrite = 0x00000004,
DontUseSheet = 0x00000008,
DontUseNativeDialog = 0x00000010,
ReadOnly = 0x00000020,
HideNameFilterDetails = 0x00000040
ShowDirsOnly = 0x00000001,
DontResolveSymlinks = 0x00000002,
DontConfirmOverwrite = 0x00000004,
DontUseSheet = 0x00000008,
DontUseNativeDialog = 0x00000010,
ReadOnly = 0x00000020,
HideNameFilterDetails = 0x00000040,
DontUseCustomDirectoryIcons = 0x00000080
};
Q_DECLARE_FLAGS(FileDialogOptions, FileDialogOption)

View File

@ -208,10 +208,12 @@ QPixmap QPlatformTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) co
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(size);
Q_UNUSED(iconOptions);
// TODO Should return QCommonStyle pixmaps?
return QPixmap();
}

View File

@ -253,6 +253,11 @@ public:
AnimateToolBoxUiEffect = 0x40
};
enum IconOption {
DontUseCustomDirectoryIcons = 0x01
};
Q_DECLARE_FLAGS(IconOptions, IconOption)
explicit QPlatformTheme();
virtual ~QPlatformTheme();
@ -274,7 +279,8 @@ public:
virtual QVariant themeHint(ThemeHint hint) 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;

View File

@ -68,7 +68,9 @@ public:
const QPalette *palette(Palette type = SystemPalette) const;
const QFont *font(Font type = SystemFont) 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;

View File

@ -249,8 +249,10 @@ QPixmap QCocoaTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
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;
NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:QCFString::toNSString(fileInfo.canonicalFilePath())];

View File

@ -595,7 +595,8 @@ public:
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
* 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 QMutex mx;
static int defaultFolderIIcon = 0;
const bool useDefaultFolderIcon = iconOptions & QPlatformTheme::DontUseCustomDirectoryIcons;
QPixmap pixmap;
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();
if (cacheableDirIcon) {
QMutexLocker locker(&mx);
int iIcon = **dirIconEntryCache.object(filePath);
int iIcon = (useDefaultFolderIcon && defaultFolderIIcon) ? defaultFolderIIcon
: **dirIconEntryCache.object(filePath);
if (iIcon) {
QPixmapCache::find(dirIconPixmapCacheKey(iIcon, iconSize), pixmap);
if (pixmap.isNull()) // Let's keep both caches in sync
@ -629,13 +633,24 @@ QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &s
#else
iconSize|SHGFI_SYSICONINDEX;
#endif // Q_OS_WINCE
unsigned long val = SHGetFileInfo((const wchar_t *)filePath.utf16(), 0,
&info, sizeof(SHFILEINFO), flags);
unsigned long val = 0;
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
if (val && info.hIcon) {
QString key;
if (cacheableDirIcon) {
if (useDefaultFolderIcon && !defaultFolderIIcon)
defaultFolderIIcon = info.iIcon;
//using the unique icon index provided by windows save us from duplicate keys
key = dirIconPixmapCacheKey(info.iIcon, iconSize);
QPixmapCache::find(key, pixmap);

View File

@ -68,7 +68,8 @@ public:
{ return m_fonts[type]; }
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);

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
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)
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

View File

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

View File

@ -83,6 +83,16 @@ QT_BEGIN_NAMESPACE
\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
{
Q_DECLARE_PUBLIC(QFileIconProvider)
@ -94,6 +104,7 @@ public:
QFileIconProvider *q_ptr;
const QString homePath;
QFileIconProvider::Options options;
private:
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.
*/
@ -228,6 +264,7 @@ static bool isCacheable(const QFileInfo &fi)
#ifdef Q_OS_WIN
// On windows it's faster to just look at the file extensions. QTBUG-13182
const QString fileExtension = fi.suffix();
// Will return false for .exe, .lnk and .ico extensions
return fileExtension.compare(QLatin1String("exe"), Qt::CaseInsensitive) &&
fileExtension.compare(QLatin1String("lnk"), Qt::CaseInsensitive) &&
fileExtension.compare(QLatin1String("ico"), Qt::CaseInsensitive);
@ -247,7 +284,6 @@ QIcon QFileIconProviderPrivate::getIcon(const QFileInfo &fi) const
if (sizes.isEmpty())
return retIcon;
const QString fileExtension = fi.suffix().toUpper();
const QString keyBase = QLatin1String("qt_.") + fi.suffix().toUpper();
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) {
QPixmap pixmap = theme->fileIconPixmap(fi, QSizeF(size, size));
QPixmap pixmap = theme->fileIconPixmap(fi, QSizeF(size, size), iconOptions);
if (!pixmap.isNull()) {
retIcon.addPixmap(pixmap);
if (cacheable)

View File

@ -59,10 +59,19 @@ public:
QFileIconProvider();
virtual ~QFileIconProvider();
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(const QFileInfo &info) const;
virtual QString type(const QFileInfo &info) const;
void setOptions(Options options);
Options options() const;
private:
Q_DECLARE_PRIVATE(QFileIconProvider)
QScopedPointer<QFileIconProviderPrivate> d_ptr;
@ -71,6 +80,8 @@ private:
#endif // QT_NO_FILEICONPROVIDER
Q_DECLARE_OPERATORS_FOR_FLAGS(QFileIconProvider::Options)
QT_END_NAMESPACE
#endif // QFILEICONPROVIDER_H

View File

@ -154,6 +154,7 @@ FileDialogPanel::FileDialogPanel(QWidget *parent)
, m_nameFilterDetailsVisible(new QCheckBox(tr("Name filter details visible")))
, m_resolveSymLinks(new QCheckBox(tr("Resolve symlinks")))
, 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_fileMode(createCombo(this, fileModeComboData, sizeof(fileModeComboData)/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_resolveSymLinks);
optionsLayout->addRow(m_readOnly);
optionsLayout->addRow(m_customDirIcons);
// Files
QGroupBox *filesGroupBox = new QGroupBox(tr("Files / Filters"));
@ -322,6 +324,8 @@ QFileDialog::Options FileDialogPanel::options() const
result |= QFileDialog::DontConfirmOverwrite;
if (!m_native->isChecked())
result |= QFileDialog::DontUseNativeDialog;
if (!m_customDirIcons->isChecked())
result |= QFileDialog::DontUseCustomDirectoryIcons;
return result;
}
@ -454,6 +458,7 @@ void FileDialogPanel::restoreDefaults()
m_resolveSymLinks->setChecked(d.resolveSymlinks());
m_readOnly->setChecked(d.isReadOnly());
m_native->setChecked(true);
m_customDirIcons->setChecked(d.testOption(QFileDialog::DontUseCustomDirectoryIcons));
m_directory->setText(QDir::homePath());
m_defaultSuffix->setText(QLatin1String("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_resolveSymLinks;
QCheckBox *m_native;
QCheckBox *m_customDirIcons;
QComboBox *m_acceptMode;
QComboBox *m_fileMode;
QComboBox *m_viewMode;