diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 606a8de68..b8e9cd6d7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,9 @@ find_package(LibtorrentRasterbar REQUIRED) list(APPEND QBT_QT_COMPONENTS Core Network Xml) if (GUI) list (APPEND QBT_QT_COMPONENTS Concurrent Gui Widgets) + if (WIN32) + list (APPEND QBT_QT_COMPONENTS WinExtras) + endif(WIN32) endif (GUI) if (DBUS) list (APPEND QBT_QT_COMPONENTS DBus) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 66b84381f..4d3bb152d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -150,3 +150,6 @@ target_link_libraries(qbt_gui qbt_lineedit qbt_powermanagement qbt_rss qbt_prope ${QBT_GUI_OPTIONAL_LINK_LIBRARIES} qbt_base QtSingleApplication::QtSingleApplication ) +if(WIN32) + target_link_libraries(qbt_gui Qt5::WinExtras) +endif(WIN32) diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index bae6d58be..df668be61 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -29,8 +29,19 @@ */ #include +#include +#include #include +#ifdef Q_OS_WIN +#include +#include +#include +#else +#include +#include +#endif + #include "guiiconprovider.h" #include "base/utils/misc.h" #include "base/utils/fs.h" @@ -47,21 +58,97 @@ namespace return cached; } - QIcon getFileIcon() + class UnifiedFileIconProvider: public QFileIconProvider { - static QIcon cached = GuiIconProvider::instance()->getIcon("text-plain"); - return cached; + public: + using QFileIconProvider::icon; + QIcon icon(const QFileInfo &info) const override + { + Q_UNUSED(info); + static QIcon cached = GuiIconProvider::instance()->getIcon("text-plain"); + return cached; + } + }; +#ifdef Q_OS_WIN + // See QTBUG-25319 for explanation why this is required + class WinShellFileIconProvider: public UnifiedFileIconProvider + { + public: + using QFileIconProvider::icon; + QIcon icon(const QFileInfo &info) const override + { + SHFILEINFO sfi = { 0 }; + HRESULT hr = ::SHGetFileInfoW(info.absoluteFilePath().toStdWString().c_str(), + FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_USEFILEATTRIBUTES); + if (FAILED(hr)) + return UnifiedFileIconProvider::icon(info); + + QPixmap iconPixmap = QtWin::fromHICON(sfi.hIcon); + ::DestroyIcon(sfi.hIcon); + return QIcon(iconPixmap); + } + }; +#else + /** + * @brief Tests whether QFileIconProvider actually works + * + * Some QPA plugins do not implement QPlatformTheme::fileIcon(), and + * QFileIconProvider::icon() returns empty icons as the result. Here we ask it for + * two icons for probably absent files and when both icons are null, we assume that + * the current QPA plugin does not implement QPlatformTheme::fileIcon(). + */ + bool doesQFileIconProviderWork() + { + QFileIconProvider provider; + const char PSEUDO_UNIQUE_FILE_NAME[] = "/tmp/qBittorrent-test-QFileIconProvider-845eb448-7ad5-4cdb-b764-b3f322a266a9"; + QIcon testIcon1 = provider.icon(QFileInfo( + QLatin1String(PSEUDO_UNIQUE_FILE_NAME) + QLatin1String(".pdf"))); + QIcon testIcon2 = provider.icon(QFileInfo( + QLatin1String(PSEUDO_UNIQUE_FILE_NAME) + QLatin1String(".png"))); + + return (!testIcon1.isNull() || !testIcon2.isNull()); } + + class MimeFileIconProvider: public UnifiedFileIconProvider + { + using QFileIconProvider::icon; + QIcon icon(const QFileInfo &info) const override + { + const QMimeType mimeType = m_db.mimeTypeForFile(info, QMimeDatabase::MatchExtension); + QIcon res = QIcon::fromTheme(mimeType.iconName()); + if (!res.isNull()) { + return res; + } + + res = QIcon::fromTheme(mimeType.genericIconName()); + if (!res.isNull()) { + return res; + } + + return UnifiedFileIconProvider::icon(info); + } + + private: + QMimeDatabase m_db; + }; +#endif } TorrentContentModel::TorrentContentModel(QObject *parent) : QAbstractItemModel(parent) , m_rootItem(new TorrentContentModelFolder(QList({ tr("Name"), tr("Size"), tr("Progress"), tr("Download Priority"), tr("Remaining"), tr("Availability") }))) { +#ifdef Q_OS_WIN + m_fileIconProvider = new WinShellFileIconProvider(); +#else + static bool doesBuiltInProviderWork = doesQFileIconProviderWork(); + m_fileIconProvider = doesBuiltInProviderWork ? new QFileIconProvider() : new MimeFileIconProvider(); +#endif } TorrentContentModel::~TorrentContentModel() { + delete m_fileIconProvider; delete m_rootItem; } @@ -202,7 +289,7 @@ QVariant TorrentContentModel::data(const QModelIndex& index, int role) const if (item->itemType() == TorrentContentModelItem::FolderType) return getDirectoryIcon(); else - return getFileIcon(); + return m_fileIconProvider->icon(QFileInfo(item->name())); } if ((index.column() == TorrentContentModelItem::COL_NAME) && (role == Qt::CheckStateRole)) { diff --git a/src/gui/torrentcontentmodel.h b/src/gui/torrentcontentmodel.h index 8fa9ac565..cd6ef3966 100644 --- a/src/gui/torrentcontentmodel.h +++ b/src/gui/torrentcontentmodel.h @@ -39,6 +39,7 @@ #include "base/bittorrent/torrentinfo.h" #include "torrentcontentmodelitem.h" +class QFileIconProvider; class TorrentContentModelFile; class TorrentContentModel: public QAbstractItemModel @@ -54,16 +55,16 @@ public: void updateFilesAvailability(const QVector &fa); QVector getFilePriorities() const; bool allFiltered() const; - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; - virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; TorrentContentModelItem::ItemType itemType(const QModelIndex& index) const; int getFileIndex(const QModelIndex& index); - virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - virtual Qt::ItemFlags flags(const QModelIndex& index) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; - virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; - virtual QModelIndex parent(const QModelIndex& index) const; - virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& index) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; void clear(); void setupModelData(const BitTorrent::TorrentInfo &info); @@ -77,6 +78,7 @@ public slots: private: TorrentContentModelFolder *m_rootItem; QVector m_filesIndex; + QFileIconProvider *m_fileIconProvider; }; #endif // TORRENTCONTENTMODEL_H diff --git a/src/src.pro b/src/src.pro index 9a296d258..71914081a 100644 --- a/src/src.pro +++ b/src/src.pro @@ -28,6 +28,9 @@ nogui { DEFINES += QBT_STATIC_QT QTPLUGIN += qico } + win32 { + QT += winextras + } TARGET = qbittorrent } nowebui: DEFINES += DISABLE_WEBUI