summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/filetree.cpp16
-rw-r--r--src/filetree.h3
-rw-r--r--src/filetreeitem.cpp165
-rw-r--r--src/filetreeitem.h5
-rw-r--r--src/filetreemodel.cpp59
-rw-r--r--src/filetreemodel.h9
6 files changed, 186 insertions, 71 deletions
diff --git a/src/filetree.cpp b/src/filetree.cpp
index 2d92c954..1dadfaad 100644
--- a/src/filetree.cpp
+++ b/src/filetree.cpp
@@ -802,11 +802,11 @@ void FileTree::addCommonMenus(QMenu& menu)
.addTo(menu);
MenuItem(tr("Ex&pand All"))
- .callback([&]{ m_tree->expandAll(); })
+ .callback([&]{ expandAll(); })
.addTo(menu);
MenuItem(tr("&Collapse All"))
- .callback([&]{ m_tree->collapseAll(); })
+ .callback([&]{ collapseAll(); })
.addTo(menu);
}
@@ -820,3 +820,15 @@ QModelIndex FileTree::proxiedIndex(const QModelIndex& index)
return index;
}
}
+
+void FileTree::collapseAll()
+{
+ m_tree->collapseAll();
+}
+
+void FileTree::expandAll()
+{
+ m_model->aboutToExpandAll();
+ m_tree->expandAll();
+ m_model->expandedAll();
+}
diff --git a/src/filetree.h b/src/filetree.h
index 2669e53b..8d71abb3 100644
--- a/src/filetree.h
+++ b/src/filetree.h
@@ -25,6 +25,9 @@ public:
bool fullyLoaded() const;
void ensureFullyLoaded();
+ void expandAll();
+ void collapseAll();
+
void open(FileTreeItem* item=nullptr);
void openHooked(FileTreeItem* item=nullptr);
void preview(FileTreeItem* item=nullptr);
diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp
index 788b4129..49bc65ac 100644
--- a/src/filetreeitem.cpp
+++ b/src/filetreeitem.cpp
@@ -14,9 +14,7 @@ constexpr bool AlwaysSortDirectoriesFirst = true;
const QString& directoryFileType()
{
- static QString name;
-
- if (name.isEmpty()) {
+ static const QString name = [] {
const DWORD flags = SHGFI_TYPENAME;
SHFILEINFOW sfi = {};
@@ -30,15 +28,88 @@ const QString& directoryFileType()
"SHGetFileInfoW failed for folder file type, {}",
formatSystemMessage(e));
- name = "File folder";
+ return QString("File folder");
} else {
- name = QString::fromWCharArray(sfi.szTypeName);
+ return QString::fromWCharArray(sfi.szTypeName);
}
- }
+ }();
+
+ return name;
+}
+
+const QString& cachedFileTypeNoExtension()
+{
+ static const QString name = [] {
+ const DWORD flags = SHGFI_TYPENAME;
+ SHFILEINFOW sfi = {};
+
+ // dummy filename with no extension
+ const auto r = SHGetFileInfoW(L"file", 0, &sfi, sizeof(sfi), flags);
+
+ if (!r) {
+ const auto e = GetLastError();
+
+ log::error(
+ "SHGetFileInfoW failed for file without extension, {}",
+ formatSystemMessage(e));
+
+ return QString("File");
+ } else {
+ return QString::fromWCharArray(sfi.szTypeName);
+ }
+ }();
return name;
}
+const QString& cachedFileType(const std::wstring& file, bool isOnFilesystem)
+{
+ static std::map<std::wstring, QString, std::less<>> map;
+ static std::mutex mutex;
+
+ const auto dot = file.find_last_of(L'.');
+ if (dot == std::wstring::npos) {
+ return cachedFileTypeNoExtension();
+ }
+
+ std::scoped_lock lock(mutex);
+ const auto sv = std::wstring_view(file.c_str() + dot, file.size() - dot);
+
+ auto itor = map.find(sv);
+ if (itor != map.end()) {
+ return itor->second;
+ }
+
+
+ DWORD flags = SHGFI_TYPENAME;
+
+ if (!isOnFilesystem) {
+ // files from archives are not on the filesystem; this flag forces
+ // SHGetFileInfoW() to only work with the filename
+ flags |= SHGFI_USEFILEATTRIBUTES;
+ }
+
+ SHFILEINFOW sfi = {};
+ const auto r = SHGetFileInfoW(file.c_str(), 0, &sfi, sizeof(sfi), flags);
+
+ QString s;
+
+ if (!r) {
+ const auto e = GetLastError();
+
+ log::error(
+ "SHGetFileInfoW failed for '{}', {}",
+ file, formatSystemMessage(e));
+
+ s = cachedFileTypeNoExtension();
+ } else {
+ s = QString::fromWCharArray(sfi.szTypeName);
+ }
+
+ return map.emplace(sv, s).first->second;
+}
+
+
FileTreeItem::FileTreeItem(
FileTreeModel* model, FileTreeItem* parent,
@@ -176,50 +247,59 @@ public:
}
};
-void FileTreeItem::sort()
+void FileTreeItem::queueSort()
{
if (!m_children.empty()) {
- m_model->sortItem(*this, true);
+ m_model->queueSortItem(this);
+ }
+}
+
+void FileTreeItem::makeSortingStale()
+{
+ m_sortingStale = true;
+
+ for (auto& c : m_children) {
+ c->makeSortingStale();
}
}
void FileTreeItem::sort(int column, Qt::SortOrder order, bool force)
{
- if (!force && !m_expanded) {
+ if (!m_expanded) {
m_sortingStale = true;
return;
}
- if (m_sortingStale) {
+ if (m_sortingStale || force) {
//log::debug("sorting is stale for {}, sorting now", debugName());
m_sortingStale = false;
- }
- std::sort(m_children.begin(), m_children.end(), [&](auto&& a, auto&& b) {
- int r = 0;
+ std::sort(m_children.begin(), m_children.end(), [&](auto&& a, auto&& b) {
+ int r = 0;
- if (a->isDirectory() && !b->isDirectory()) {
- if constexpr (AlwaysSortDirectoriesFirst) {
- return true;
+ if (a->isDirectory() && !b->isDirectory()) {
+ if constexpr (AlwaysSortDirectoriesFirst) {
+ return true;
+ } else {
+ r = -1;
+ }
+ } else if (!a->isDirectory() && b->isDirectory()) {
+ if constexpr (AlwaysSortDirectoriesFirst) {
+ return false;
+ } else {
+ r = 1;
+ }
} else {
- r = -1;
+ r = FileTreeItem::Sorter::compare(column, a.get(), b.get());
}
- } else if (!a->isDirectory() && b->isDirectory()) {
- if constexpr (AlwaysSortDirectoriesFirst) {
- return false;
+
+ if (order == Qt::AscendingOrder) {
+ return (r < 0);
} else {
- r = 1;
+ return (r > 0);
}
- } else {
- r = FileTreeItem::Sorter::compare(column, a.get(), b.get());
- }
-
- if (order == Qt::AscendingOrder) {
- return (r < 0);
- } else {
- return (r > 0);
- }
- });
+ });
+ }
for (auto& child : m_children) {
child->sort(column, order, force);
@@ -321,28 +401,11 @@ void FileTreeItem::getFileType() const
return;
}
- DWORD flags = SHGFI_TYPENAME;
-
- if (isFromArchive()) {
- // files from archives are not on the filesystem; this flag forces
- // SHGetFileInfoW() to only work with the filename
- flags |= SHGFI_USEFILEATTRIBUTES;
- }
-
- SHFILEINFOW sfi = {};
- const auto r = SHGetFileInfoW(
- m_wsRealPath.c_str(), 0, &sfi, sizeof(sfi), flags);
-
- if (!r) {
- const auto e = GetLastError();
-
- log::error(
- "SHGetFileInfoW failed for '{}', {}",
- m_realPath, formatSystemMessage(e));
-
+ const auto& t = cachedFileType(m_wsRealPath, !isFromArchive());
+ if (t.isEmpty()) {
m_fileType.fail();
} else {
- m_fileType.set(QString::fromWCharArray(sfi.szTypeName));
+ m_fileType.set(t);
}
}
diff --git a/src/filetreeitem.h b/src/filetreeitem.h
index 2092782e..750e4719 100644
--- a/src/filetreeitem.h
+++ b/src/filetreeitem.h
@@ -93,6 +93,7 @@ public:
}
void sort(int column, Qt::SortOrder order, bool force);
+ void makeSortingStale();
FileTreeItem* parent()
{
@@ -223,7 +224,7 @@ public:
m_expanded = b;
if (m_expanded && m_sortingStale) {
- sort();
+ queueSort();
}
}
@@ -314,7 +315,7 @@ private:
std::wstring dataRelativeParentPath, bool isDirectory, std::wstring file);
void getFileType() const;
- void sort();
+ void queueSort();
};
#endif // MODORGANIZER_FILETREEITEM_INCLUDED
diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp
index 11901c43..8790f981 100644
--- a/src/filetreemodel.cpp
+++ b/src/filetreemodel.cpp
@@ -196,13 +196,13 @@ void* makeInternalPointer(FileTreeItem* item)
FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) :
QAbstractItemModel(parent), m_core(core), m_enabled(true),
m_root(FileTreeItem::createDirectory(this, nullptr, L"", L"")),
- m_flags(NoFlags), m_fullyLoaded(false)
+ m_flags(NoFlags), m_fullyLoaded(false), m_sortingEnabled(true)
{
m_root->setExpanded(true);
+ m_sortTimer.setSingleShot(true);
connect(&m_removeTimer, &QTimer::timeout, [&]{ removeItems(); });
connect(&m_sortTimer, &QTimer::timeout, [&]{ sortItems(); });
-
connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); });
}
@@ -212,6 +212,7 @@ void FileTreeModel::refresh()
m_fullyLoaded = false;
update(*m_root, *m_core.directoryStructure(), L"", false);
+ sortItem(*m_root, false);
}
void FileTreeModel::clear()
@@ -253,6 +254,17 @@ void FileTreeModel::setEnabled(bool b)
m_enabled = b;
}
+void FileTreeModel::aboutToExpandAll()
+{
+ m_sortingEnabled = false;
+}
+
+void FileTreeModel::expandedAll()
+{
+ m_sortingEnabled = true;
+ sortItem(*m_root, false);
+}
+
const FileTreeModel::SortInfo& FileTreeModel::sortInfo() const
{
return m_sort;
@@ -360,6 +372,10 @@ void FileTreeModel::doFetchMore(const QModelIndex& parent, bool forFetch)
const auto parentPath = item->dataRelativeParentPath();
update(*item, *parentEntry, parentPath.toStdWString(), forFetch);
+
+ if (!forFetch) {
+ sortItem(*item, false);
+ }
}
QVariant FileTreeModel::data(const QModelIndex& index, int role) const
@@ -485,7 +501,7 @@ void FileTreeModel::sort(int column, Qt::SortOrder order)
m_sort.column = column;
m_sort.order = order;
- sortItem(*m_root, false);
+ sortItem(*m_root, true);
}
FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const
@@ -558,11 +574,15 @@ void FileTreeModel::update(
}
if (added) {
+ parentItem.makeSortingStale();
+
// see comment at the top of this file
- if (forFetching)
- queueSortItem(&parentItem);
- else
- sortItem(parentItem, true);
+ if (forFetching) {
+ // don't pass a specific item, this will start a timer and re-sort the
+ // whole tree, which is faster than potentially queuing every single
+ // node if the whole tree is expanded
+ queueSortItem(nullptr);
+ }
}
}
@@ -873,21 +893,32 @@ void FileTreeModel::removeItems()
void FileTreeModel::queueSortItem(FileTreeItem* item)
{
- m_sortItems.push_back(item);
+ if (!m_sortingEnabled) {
+ return;
+ }
+
+ if (item) {
+ m_sortItems.push_back(item);
+ }
+
m_sortTimer.start(1);
}
void FileTreeModel::sortItems()
{
// see comment at the top of this file
- trace(log::debug("sort item timer: sorting {} items", m_sortItems.size()));
- auto copy = std::move(m_sortItems);
- m_sortItems.clear();
- m_sortTimer.stop();
+ if (m_sortItems.empty()) {
+ sortItem(*m_root, false);
+ } else {
+ log::debug("sort item timer: sorting {} items", m_sortItems.size());
- for (auto&& f : copy) {
- sortItem(*f, true);
+ auto items = std::move(m_sortItems);
+ m_sortItems.clear();
+
+ for (auto* item : items) {
+ sortItem(*item, false);
+ }
}
}
diff --git a/src/filetreemodel.h b/src/filetreemodel.h
index 334a0577..4894e4be 100644
--- a/src/filetreemodel.h
+++ b/src/filetreemodel.h
@@ -61,6 +61,10 @@ public:
bool enabled() const;
void setEnabled(bool b);
+ void aboutToExpandAll();
+ void expandedAll();
+
+
const SortInfo& sortInfo() const;
QModelIndex index(int row, int col, const QModelIndex& parent={}) const override;
@@ -77,6 +81,7 @@ public:
FileTreeItem* itemFromIndex(const QModelIndex& index) const;
void sortItem(FileTreeItem& item, bool force);
+ void queueSortItem(FileTreeItem* item);
private:
class Range;
@@ -92,11 +97,12 @@ private:
mutable QTimer m_iconPendingTimer;
SortInfo m_sort;
bool m_fullyLoaded;
+ bool m_sortingEnabled;
// see top of filetreemodel.cpp
std::vector<FileTreeItem*> m_removeItems;
- QTimer m_removeTimer;
std::vector<FileTreeItem*> m_sortItems;
+ QTimer m_removeTimer;
QTimer m_sortTimer;
@@ -118,7 +124,6 @@ private:
void queueRemoveItem(FileTreeItem* item);
void removeItems();
- void queueSortItem(FileTreeItem* item);
void sortItems();