diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2020-07-19 16:48:31 -0400 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2020-07-19 16:48:31 -0400 |
| commit | 6c4e237d4b43db5c3f9dc01f8c0d3313f3bd2605 (patch) | |
| tree | 7613427fe55d29abe21a11810ff355f84bdb31e7 /src/filetreeitem.cpp | |
| parent | d32250597abf3139268ec3480af9a1fadcf0d18e (diff) | |
fixed crash because items were sorted while being expanded
when expanding all or updating the tree, only sort once at the end
cache file types
Diffstat (limited to 'src/filetreeitem.cpp')
| -rw-r--r-- | src/filetreeitem.cpp | 165 |
1 files changed, 114 insertions, 51 deletions
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); } } |
