summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/filetree.cpp236
-rw-r--r--src/filetree.h8
2 files changed, 234 insertions, 10 deletions
diff --git a/src/filetree.cpp b/src/filetree.cpp
index d7a61898..0d9a1414 100644
--- a/src/filetree.cpp
+++ b/src/filetree.cpp
@@ -83,6 +83,11 @@ QString FileTreeItem::dataRelativeFilePath() const
return path += m_file;
}
+const QString& FileTreeItem::realPath() const
+{
+ return m_realPath;
+}
+
const QString& FileTreeItem::filename() const
{
return m_file;
@@ -154,12 +159,209 @@ QString FileTreeItem::debugName() const
}
+class FileTreeModel::IconFetcher
+{
+public:
+ IconFetcher()
+ : m_iconSize(GetSystemMetrics(SM_CXSMICON)), m_stop(false)
+ {
+ m_quickCache.file = getPixmapIcon(QFileIconProvider::File);
+ m_quickCache.directory = getPixmapIcon(QFileIconProvider::Folder);
+
+ m_thread = std::thread([&]{ threadFun(); });
+ }
+
+ ~IconFetcher()
+ {
+ stop();
+ m_thread.join();
+ }
+
+ void stop()
+ {
+ m_stop = true;
+ m_waiter.wakeUp();
+ }
+
+ QVariant icon(const QString& path) const
+ {
+ if (hasOwnIcon(path)) {
+ return fileIcon(path);
+ } else {
+ const auto dot = path.lastIndexOf(".");
+
+ if (dot == -1) {
+ // no extension
+ return m_quickCache.file;
+ }
+
+ return extensionIcon(path.midRef(dot));
+ }
+ }
+
+ QPixmap genericFileIcon() const
+ {
+ return m_quickCache.file;
+ }
+
+ QPixmap genericDirectoryIcon() const
+ {
+ return m_quickCache.directory;
+ }
+
+private:
+ struct QuickCache
+ {
+ QPixmap file;
+ QPixmap directory;
+ };
+
+ struct Cache
+ {
+ std::map<QString, QPixmap, std::less<>> map;
+ std::mutex mapMutex;
+
+ std::set<QString> queue;
+ std::mutex queueMutex;
+ };
+
+ struct Waiter
+ {
+ mutable std::mutex m_wakeUpMutex;
+ std::condition_variable m_wakeUp;
+ bool m_queueAvailable = false;
+
+ void wait()
+ {
+ std::unique_lock lock(m_wakeUpMutex);
+ m_wakeUp.wait(lock, [&]{ return m_queueAvailable; });
+ m_queueAvailable = false;
+ }
+
+ void wakeUp()
+ {
+ {
+ std::scoped_lock lock(m_wakeUpMutex);
+ m_queueAvailable = true;
+ }
+
+ m_wakeUp.notify_one();
+ }
+ };
+
+ const int m_iconSize;
+ QFileIconProvider m_provider;
+ std::thread m_thread;
+ std::atomic<bool> m_stop;
+
+ mutable QuickCache m_quickCache;
+ mutable Cache m_extensionCache;
+ mutable Cache m_fileCache;
+ mutable Waiter m_waiter;
+
+
+ bool hasOwnIcon(const QString& path) const
+ {
+ static const QString exe = ".exe";
+ static const QString lnk = ".lnk";
+ static const QString ico = ".ico";
+
+ return
+ path.endsWith(exe, Qt::CaseInsensitive) ||
+ path.endsWith(lnk, Qt::CaseInsensitive) ||
+ path.endsWith(ico, Qt::CaseInsensitive);
+ }
+
+ template <class T>
+ QPixmap getPixmapIcon(T&& t) const
+ {
+ return m_provider.icon(t).pixmap({m_iconSize, m_iconSize});
+ }
+
+ void threadFun()
+ {
+ while (!m_stop) {
+ m_waiter.wait();
+ if (m_stop) {
+ break;
+ }
+
+ checkCache(m_extensionCache);
+ checkCache(m_fileCache);
+ }
+ }
+
+ void checkCache(Cache& cache)
+ {
+ std::set<QString> queue;
+
+ {
+ std::scoped_lock lock(cache.queueMutex);
+ queue = std::move(cache.queue);
+ }
+
+ if (queue.empty()) {
+ return;
+ }
+
+ std::map<QString, QPixmap> map;
+ for (auto&& ext : queue) {
+ map.emplace(std::move(ext), getPixmapIcon(ext));
+ }
+
+ {
+ std::scoped_lock lock(cache.mapMutex);
+ for (auto&& p : map) {
+ cache.map.insert(std::move(p));
+ }
+ }
+ }
+
+ void queue(Cache& cache, QString path) const
+ {
+ {
+ std::scoped_lock lock(cache.queueMutex);
+ cache.queue.insert(std::move(path));
+ }
+
+ m_waiter.wakeUp();
+ }
+
+ QVariant fileIcon(const QString& path) const
+ {
+ {
+ std::scoped_lock lock(m_fileCache.mapMutex);
+ auto itor = m_fileCache.map.find(path);
+ if (itor != m_fileCache.map.end()) {
+ return itor->second;
+ }
+ }
+
+ queue(m_fileCache, path);
+ return {};
+ }
+
+ QVariant extensionIcon(const QStringRef& ext) const
+ {
+ {
+ std::scoped_lock lock(m_extensionCache.mapMutex);
+ auto itor = m_extensionCache.map.find(ext);
+ if (itor != m_extensionCache.map.end()) {
+ return itor->second;
+ }
+ }
+
+ queue(m_extensionCache, ext.toString());
+ return {};
+ }
+};
+
+
FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent)
: QAbstractItemModel(parent), m_core(core), m_flags(NoFlags)
{
- QFileIconProvider provider;
- m_fileIcon = provider.icon(QFileIconProvider::File);
- m_directoryIcon = provider.icon(QFileIconProvider::Folder);
+ m_iconFetcher.reset(new IconFetcher);
+ connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); });
}
void FileTreeModel::setFlags(Flags f)
@@ -496,12 +698,16 @@ QVariant FileTreeModel::data(const QModelIndex& index, int role) const
{
if (index.column() == 0) {
if (auto* item=itemFromIndex(index)) {
- const auto iconType = item->icon();
-
- if (iconType == QFileIconProvider::File) {
- return m_fileIcon;
- } else if (iconType == QFileIconProvider::Folder) {
- return m_directoryIcon;
+ if (item->isDirectory()) {
+ return m_iconFetcher->genericDirectoryIcon();
+ } else {
+ auto v = m_iconFetcher->icon(item->realPath());
+ if (v.isNull()) {
+ m_iconPending.push_back(index);
+ m_iconPendingTimer.start(std::chrono::milliseconds(1));
+ return m_iconFetcher->genericFileIcon();
+ }
+ return v;
}
}
}
@@ -513,6 +719,18 @@ QVariant FileTreeModel::data(const QModelIndex& index, int role) const
return {};
}
+void FileTreeModel::updatePendingIcons()
+{
+ log::debug("updating {} pending icons", m_iconPending.size());
+
+ for (auto&& index : m_iconPending) {
+ emit dataChanged(index, index, {Qt::DecorationRole});
+ }
+
+ m_iconPending.clear();
+ m_iconPendingTimer.stop();
+}
+
QVariant FileTreeModel::headerData(int i, Qt::Orientation ori, int role) const
{
if (role == Qt::DisplayRole) {
diff --git a/src/filetree.h b/src/filetree.h
index aabdf61b..75270d29 100644
--- a/src/filetree.h
+++ b/src/filetree.h
@@ -38,6 +38,7 @@ public:
const QString& filename() const;
const QString& mod() const;
+ const QString& realPath() const;
QString dataRelativeParentPath() const;
QString dataRelativeFilePath() const;
@@ -95,11 +96,15 @@ public:
Qt::ItemFlags flags(const QModelIndex& index) const override;
private:
+ class IconFetcher;
+
using DirectoryIterator = std::vector<MOShared::DirectoryEntry*>::const_iterator;
OrganizerCore& m_core;
mutable FileTreeItem m_root;
Flags m_flags;
- QIcon m_fileIcon, m_directoryIcon;
+ std::unique_ptr<IconFetcher> m_iconFetcher;
+ mutable std::vector<QModelIndex> m_iconPending;
+ mutable QTimer m_iconPendingTimer;
bool showConflicts() const;
bool showArchives() const;
@@ -120,6 +125,7 @@ private:
FileTreeItem* itemFromIndex(const QModelIndex& index) const;
void ensureLoaded(FileTreeItem* item) const;
+ void updatePendingIcons();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeModel::Flags);