From a7051df5da4139661169f227861b712453e7b8b0 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 19:01:13 -0500 Subject: split item and model --- src/filetreeitem.cpp | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 src/filetreeitem.cpp (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp new file mode 100644 index 00000000..38eb5ec2 --- /dev/null +++ b/src/filetreeitem.cpp @@ -0,0 +1,222 @@ +#include "filetreeitem.h" +#include "modinfo.h" +#include + +using namespace MOBase; + +FileTreeItem::FileTreeItem() + : m_flags(NoFlags), m_loaded(false) +{ +} + +FileTreeItem::FileTreeItem( + FileTreeItem* parent, int originID, + std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, + std::wstring file, std::wstring mod) : + m_parent(parent), m_originID(originID), + m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), + m_realPath(QString::fromStdWString(realPath)), + m_flags(flags), + m_file(QString::fromStdWString(file)), + m_mod(QString::fromStdWString(mod)), + m_loaded(false), + m_expanded(false) +{ +} + +void FileTreeItem::add(std::unique_ptr child) +{ + m_children.push_back(std::move(child)); +} + +void FileTreeItem::insert(std::unique_ptr child, std::size_t at) +{ + if (at > m_children.size()) { + log::error( + "{}: can't insert child {} at {}, out of range", + debugName(), child->debugName(), at); + + return; + } + + m_children.insert(m_children.begin() + at, std::move(child)); +} + +void FileTreeItem::remove(std::size_t i) +{ + if (i >= m_children.size()) { + log::error("{}: can't remove child at {}", debugName(), i); + return; + } + + m_children.erase(m_children.begin() + i); +} + +const std::vector>& FileTreeItem::children() const +{ + return m_children; +} + +FileTreeItem* FileTreeItem::parent() +{ + return m_parent; +} + +int FileTreeItem::originID() const +{ + return m_originID; +} + +const QString& FileTreeItem::virtualParentPath() const +{ + return m_virtualParentPath; +} + +QString FileTreeItem::virtualPath() const +{ + QString s = "Data\\"; + + if (!m_virtualParentPath.isEmpty()) { + s += m_virtualParentPath + "\\"; + } + + s += m_file; + + return s; +} + +QString FileTreeItem::dataRelativeParentPath() const +{ + return m_virtualParentPath; +} + +QString FileTreeItem::dataRelativeFilePath() const +{ + auto path = dataRelativeParentPath(); + if (!path.isEmpty()) { + path += "\\"; + } + + return path += m_file; +} + +const QString& FileTreeItem::realPath() const +{ + return m_realPath; +} + +const QString& FileTreeItem::filename() const +{ + return m_file; +} + +const QString& FileTreeItem::mod() const +{ + return m_mod; +} + +QFont FileTreeItem::font() const +{ + QFont f; + + if (isFromArchive()) { + f.setItalic(true); + } else if (isHidden()) { + f.setStrikeOut(true); + } + + return f; +} + +QFileIconProvider::IconType FileTreeItem::icon() const +{ + if (m_flags & Directory) { + return QFileIconProvider::Folder; + } else { + return QFileIconProvider::File; + } +} + +bool FileTreeItem::isDirectory() const +{ + return (m_flags & Directory); +} + +bool FileTreeItem::isFromArchive() const +{ + return (m_flags & FromArchive); +} + +bool FileTreeItem::isConflicted() const +{ + return (m_flags & Conflicted); +} + +bool FileTreeItem::isHidden() const +{ + return m_file.endsWith(ModInfo::s_HiddenExt); +} + +bool FileTreeItem::hasChildren() const +{ + if (!isDirectory()) { + return false; + } + + if (isLoaded() && m_children.empty()) { + return false; + } + + return true; +} + +void FileTreeItem::setLoaded(bool b) +{ + m_loaded = b; +} + +bool FileTreeItem::isLoaded() const +{ + return m_loaded; +} + +void FileTreeItem::unload() +{ + if (!m_loaded) { + return; + } + + m_loaded = false; + m_children.clear(); +} + +void FileTreeItem::setExpanded(bool b) +{ + m_expanded = b; +} + +bool FileTreeItem::isStrictlyExpanded() const +{ + return m_expanded; +} + +bool FileTreeItem::areChildrenVisible() const +{ + if (m_expanded) { + if (m_parent) { + return m_parent->areChildrenVisible(); + } else { + return true; + } + } + + return false; +} + +QString FileTreeItem::debugName() const +{ + return QString("%1(ld=%2,cs=%3)") + .arg(virtualPath()) + .arg(m_loaded) + .arg(m_children.size()); +} -- cgit v1.3.1 From 72394faa750ac05871f62583c7c922879a20bc7b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 20:18:14 -0500 Subject: some optimizations to avoid case conversions and memory allocations --- src/filetreeitem.cpp | 13 +++++++++++++ src/filetreeitem.h | 3 +++ src/filetreemodel.cpp | 24 ++++++++---------------- src/shared/directoryentry.cpp | 6 ++++-- src/shared/directoryentry.h | 2 +- 5 files changed, 29 insertions(+), 19 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 38eb5ec2..c2f3a1fc 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -1,8 +1,10 @@ #include "filetreeitem.h" #include "modinfo.h" +#include "util.h" #include using namespace MOBase; +using namespace MOShared; FileTreeItem::FileTreeItem() : m_flags(NoFlags), m_loaded(false) @@ -17,6 +19,7 @@ FileTreeItem::FileTreeItem( m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), m_realPath(QString::fromStdWString(realPath)), m_flags(flags), + m_wsFile(file), m_wsLcFile(ToLower(file)), m_file(QString::fromStdWString(file)), m_mod(QString::fromStdWString(mod)), m_loaded(false), @@ -110,6 +113,16 @@ const QString& FileTreeItem::filename() const return m_file; } +const std::wstring& FileTreeItem::filenameWs() const +{ + return m_wsFile; +} + +const std::wstring& FileTreeItem::filenameWsLowerCase() const +{ + return m_wsLcFile; +} + const QString& FileTreeItem::mod() const { return m_mod; diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 516319ac..423038ba 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -37,6 +37,8 @@ public: const QString& virtualParentPath() const; QString virtualPath() const; const QString& filename() const; + const std::wstring& filenameWs() const; + const std::wstring& filenameWsLowerCase() const; const QString& mod() const; QFont font() const; @@ -68,6 +70,7 @@ private: QString m_virtualParentPath; QString m_realPath; Flags m_flags; + std::wstring m_wsFile, m_wsLcFile; QString m_file; QString m_mod; bool m_loaded; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 024ec6ee..b5ae9dc8 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -1,6 +1,7 @@ #include "filetreemodel.h" #include "organizercore.h" #include +#include using namespace MOBase; using namespace MOShared; @@ -247,18 +248,16 @@ void FileTreeModel::updateDirectories( int row = 0; std::vector remove; - std::set seen; + std::unordered_set seen; for (auto&& item : parentItem.children()) { if (!item->isDirectory()) { break; } - const auto name = item->filename().toStdWString(); - - if (auto d=parentEntry.findSubDirectory(name)) { + if (auto d=parentEntry.findSubDirectory(item->filenameWsLowerCase())) { // directory still exists - seen.insert(name); + seen.insert(item->filenameWs()); if (item->areChildrenVisible()) { trace([&]{ log::debug( @@ -419,7 +418,7 @@ void FileTreeModel::updateFiles( parentItem.debugName(), (path.empty() ? L"\\" : path)); }); - std::set seen; + std::unordered_set seen; std::vector remove; for (auto&& item : parentItem.children()) { @@ -427,13 +426,11 @@ void FileTreeModel::updateFiles( continue; } - const auto name = item->filename().toStdWString(); - - if (auto f=parentEntry.findFile(name)) { + if (auto f=parentEntry.findFile(item->filenameWsLowerCase(), true)) { if (shouldShowFile(*f)) { // file still exists trace([&]{ log::debug("{} still exists", item->debugName()); }); - seen.insert(name); + seen.insert(item->filenameWs()); continue; } } @@ -569,12 +566,7 @@ FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const return nullptr; } - auto* item = static_cast(data); - if (!item->debugName().isEmpty()) { - return item; - } - - return nullptr; + return static_cast(data); } QModelIndex FileTreeModel::indexFromItem( diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 146662ad..c6b29fbb 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -875,9 +875,11 @@ DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &pa } -const FileEntry::Ptr DirectoryEntry::findFile(const std::wstring &name) const +const FileEntry::Ptr DirectoryEntry::findFile( + const std::wstring &name, bool alreadyLowerCase) const { - auto iter = m_Files.find(ToLower(name)); + auto iter = m_Files.find(alreadyLowerCase ? name : ToLower(name)); + if (iter != m_Files.end()) { return m_FileRegister->getFile(iter->second); } else { diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 52265583..d33b495a 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -278,7 +278,7 @@ public: * @param name name of the file * @return fileentry object for the file or nullptr if no file matches */ - const FileEntry::Ptr findFile(const std::wstring &name) const; + const FileEntry::Ptr findFile(const std::wstring &name, bool alreadyLowerCase=false) const; bool hasFile(const std::wstring& name) const; bool containsArchive(std::wstring archiveName); -- cgit v1.3.1 From 0a908c49625fe0e54bc45e29fe8c4908d20b0dbe Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 21:13:31 -0500 Subject: added a map for directories in DirectoryEntry more optimizations for filetree --- src/filetreeitem.cpp | 103 ----------------------------------- src/filetreeitem.h | 124 +++++++++++++++++++++++++++++++++++------- src/filetreemodel.cpp | 25 ++++++--- src/shared/directoryentry.cpp | 56 +++++++++++++++---- src/shared/directoryentry.h | 19 ++++++- 5 files changed, 186 insertions(+), 141 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index c2f3a1fc..0469dfcc 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -27,11 +27,6 @@ FileTreeItem::FileTreeItem( { } -void FileTreeItem::add(std::unique_ptr child) -{ - m_children.push_back(std::move(child)); -} - void FileTreeItem::insert(std::unique_ptr child, std::size_t at) { if (at > m_children.size()) { @@ -55,26 +50,6 @@ void FileTreeItem::remove(std::size_t i) m_children.erase(m_children.begin() + i); } -const std::vector>& FileTreeItem::children() const -{ - return m_children; -} - -FileTreeItem* FileTreeItem::parent() -{ - return m_parent; -} - -int FileTreeItem::originID() const -{ - return m_originID; -} - -const QString& FileTreeItem::virtualParentPath() const -{ - return m_virtualParentPath; -} - QString FileTreeItem::virtualPath() const { QString s = "Data\\"; @@ -88,11 +63,6 @@ QString FileTreeItem::virtualPath() const return s; } -QString FileTreeItem::dataRelativeParentPath() const -{ - return m_virtualParentPath; -} - QString FileTreeItem::dataRelativeFilePath() const { auto path = dataRelativeParentPath(); @@ -103,31 +73,6 @@ QString FileTreeItem::dataRelativeFilePath() const return path += m_file; } -const QString& FileTreeItem::realPath() const -{ - return m_realPath; -} - -const QString& FileTreeItem::filename() const -{ - return m_file; -} - -const std::wstring& FileTreeItem::filenameWs() const -{ - return m_wsFile; -} - -const std::wstring& FileTreeItem::filenameWsLowerCase() const -{ - return m_wsLcFile; -} - -const QString& FileTreeItem::mod() const -{ - return m_mod; -} - QFont FileTreeItem::font() const { QFont f; @@ -150,49 +95,11 @@ QFileIconProvider::IconType FileTreeItem::icon() const } } -bool FileTreeItem::isDirectory() const -{ - return (m_flags & Directory); -} - -bool FileTreeItem::isFromArchive() const -{ - return (m_flags & FromArchive); -} - -bool FileTreeItem::isConflicted() const -{ - return (m_flags & Conflicted); -} - bool FileTreeItem::isHidden() const { return m_file.endsWith(ModInfo::s_HiddenExt); } -bool FileTreeItem::hasChildren() const -{ - if (!isDirectory()) { - return false; - } - - if (isLoaded() && m_children.empty()) { - return false; - } - - return true; -} - -void FileTreeItem::setLoaded(bool b) -{ - m_loaded = b; -} - -bool FileTreeItem::isLoaded() const -{ - return m_loaded; -} - void FileTreeItem::unload() { if (!m_loaded) { @@ -203,16 +110,6 @@ void FileTreeItem::unload() m_children.clear(); } -void FileTreeItem::setExpanded(bool b) -{ - m_expanded = b; -} - -bool FileTreeItem::isStrictlyExpanded() const -{ - return m_expanded; -} - bool FileTreeItem::areChildrenVisible() const { if (m_expanded) { diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 423038ba..1e820f3f 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -27,39 +27,125 @@ public: FileTreeItem(FileTreeItem&&) = default; FileTreeItem& operator=(FileTreeItem&&) = default; - void add(std::unique_ptr child); + void add(std::unique_ptr child) + { + m_children.push_back(std::move(child)); + } + void insert(std::unique_ptr child, std::size_t at); void remove(std::size_t i); - const std::vector>& children() const; - FileTreeItem* parent(); - int originID() const; - const QString& virtualParentPath() const; + const std::vector>& children() const + { + return m_children; + } + + + FileTreeItem* parent() + { + return m_parent; + } + + int originID() const + { + return m_originID; + } + + const QString& virtualParentPath() const + { + return m_virtualParentPath; + } + QString virtualPath() const; - const QString& filename() const; - const std::wstring& filenameWs() const; - const std::wstring& filenameWsLowerCase() const; - const QString& mod() const; + const QString& filename() const + { + return m_file; + } + + const std::wstring& filenameWs() const + { + return m_wsFile; + } + + const std::wstring& filenameWsLowerCase() const + { + return m_wsLcFile; + } + + const QString& mod() const + { + return m_mod; + } + QFont font() const; - const QString& realPath() const; - QString dataRelativeParentPath() const; + const QString& realPath() const + { + return m_realPath; + } + + const QString& dataRelativeParentPath() const + { + return m_virtualParentPath; + } + QString dataRelativeFilePath() const; QFileIconProvider::IconType icon() const; - bool isDirectory() const; - bool isFromArchive() const; - bool isConflicted() const; + bool isDirectory() const + { + return (m_flags & Directory); + } + + bool isFromArchive() const + { + return (m_flags & FromArchive); + } + + bool isConflicted() const + { + return (m_flags & Conflicted); + } + bool isHidden() const; - bool hasChildren() const; - void setLoaded(bool b); - bool isLoaded() const; + bool hasChildren() const + { + if (!isDirectory()) { + return false; + } + + if (isLoaded() && m_children.empty()) { + return false; + } + + return true; + } + + + void setLoaded(bool b) + { + m_loaded = b; + } + + bool isLoaded() const + { + return m_loaded; + } + void unload(); - void setExpanded(bool b); - bool isStrictlyExpanded() const; + void setExpanded(bool b) + { + m_expanded = b; + } + + bool isStrictlyExpanded() const + { + return m_expanded; + } + bool areChildrenVisible() const; QString debugName() const; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index b5ae9dc8..9332b354 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -255,7 +255,7 @@ void FileTreeModel::updateDirectories( break; } - if (auto d=parentEntry.findSubDirectory(item->filenameWsLowerCase())) { + if (auto d=parentEntry.findSubDirectory(item->filenameWsLowerCase(), true)) { // directory still exists seen.insert(item->filenameWs()); @@ -418,7 +418,7 @@ void FileTreeModel::updateFiles( parentItem.debugName(), (path.empty() ? L"\\" : path)); }); - std::unordered_set seen; + std::unordered_set seen; std::vector remove; for (auto&& item : parentItem.children()) { @@ -430,7 +430,7 @@ void FileTreeModel::updateFiles( if (shouldShowFile(*f)) { // file still exists trace([&]{ log::debug("{} still exists", item->debugName()); }); - seen.insert(item->filenameWs()); + seen.emplace(f->getIndex()); continue; } } @@ -483,9 +483,14 @@ void FileTreeModel::updateFiles( std::size_t insertPos = firstFile; - for (auto&& file : parentEntry.getFiles()) { - if (shouldShowFile(*file)) { - if (!seen.contains(file->getName())) { + parentEntry.forEachFileIndex([&](auto&& fileIndex) { + if (!seen.contains(fileIndex)) { + const auto& file = parentEntry.getFileByIndex(fileIndex); + if (!file) { + return true; + } + + if (shouldShowFile(*file)) { trace([&]{ log::debug( "{}: new file {}", parentItem.debugName(), QString::fromStdWString(file->getName())); @@ -532,11 +537,15 @@ void FileTreeModel::updateFiles( beginInsertRows(parentIndex, first, last); parentItem.insert(std::move(child), insertPos); endInsertRows(); + } else { + ++insertPos; } - + } else { ++insertPos; } - } + + return true; + }); } std::wstring FileTreeModel::makeModName(const FileEntry& file, int originID) const diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index c6b29fbb..97da1061 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -485,7 +485,9 @@ void DirectoryEntry::clear() for (DirectoryEntry *entry : m_SubDirectories) { delete entry; } + m_SubDirectories.clear(); + m_SubDirectoriesMap.clear(); } @@ -665,7 +667,9 @@ void DirectoryEntry::removeDirRecursive() entry->removeDirRecursive(); delete entry; } + m_SubDirectories.clear(); + m_SubDirectoriesMap.clear(); } void DirectoryEntry::removeDir(const std::wstring &path) @@ -676,6 +680,20 @@ void DirectoryEntry::removeDir(const std::wstring &path) DirectoryEntry *entry = *iter; if (CaseInsensitiveEqual(entry->getName(), path)) { entry->removeDirRecursive(); + + bool found = false; + for (auto iter2=m_SubDirectoriesMap.begin(); iter2!=m_SubDirectoriesMap.end(); ++iter2) { + if (iter2->second == entry) { + m_SubDirectoriesMap.erase(iter2); + found = true; + break; + } + } + + if (!found) { + log::error("entry {} not in sub directories map", entry->getName()); + } + m_SubDirectories.erase(iter); delete entry; break; @@ -858,14 +876,22 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const } -DirectoryEntry *DirectoryEntry::findSubDirectory(const std::wstring &name) const +DirectoryEntry *DirectoryEntry::findSubDirectory( + const std::wstring &name, bool alreadyLowerCase) const { - for (DirectoryEntry *entry : m_SubDirectories) { - if (CaseInsensitiveEqual(entry->getName(), name)) { - return entry; - } + SubDirectoriesMap::const_iterator itor; + + if (alreadyLowerCase) { + itor = m_SubDirectoriesMap.find(name); + } else { + itor = m_SubDirectoriesMap.find(ToLower(name)); + } + + if (itor == m_SubDirectoriesMap.end()) { + return nullptr; } - return nullptr; + + return itor->second; } @@ -878,7 +904,13 @@ DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &pa const FileEntry::Ptr DirectoryEntry::findFile( const std::wstring &name, bool alreadyLowerCase) const { - auto iter = m_Files.find(alreadyLowerCase ? name : ToLower(name)); + std::map::const_iterator iter; + + if (alreadyLowerCase) { + iter = m_Files.find(name); + } else { + iter = m_Files.find(ToLower(name)); + } if (iter != m_Files.end()) { return m_FileRegister->getFile(iter->second); @@ -900,9 +932,13 @@ DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool c } } if (create) { - std::vector::iterator iter = m_SubDirectories.insert(m_SubDirectories.end(), - new DirectoryEntry(name, this, originID, m_FileRegister, m_OriginConnection)); - return *iter; + auto* entry = new DirectoryEntry( + name, this, originID, m_FileRegister, m_OriginConnection); + + m_SubDirectories.push_back(entry); + m_SubDirectoriesMap.emplace(ToLower(name), entry); + + return entry; } else { return nullptr; } diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index d33b495a..79bc5cf2 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -271,7 +271,22 @@ public: } } - DirectoryEntry *findSubDirectory(const std::wstring &name) const; + template + void forEachFileIndex(F&& f) const + { + for (auto&& p : m_Files) { + if (!f(p.second)) { + break; + } + } + } + + FileEntry::Ptr getFileByIndex(FileEntry::Index index) const + { + return m_FileRegister->getFile(index); + } + + DirectoryEntry *findSubDirectory(const std::wstring &name, bool alreadyLowerCase=false) const; DirectoryEntry *findSubDirectoryRecursive(const std::wstring &path); /** retrieve a file in this directory by name. @@ -352,6 +367,7 @@ private: void removeDirRecursive(); private: + using SubDirectoriesMap = std::unordered_map; boost::shared_ptr m_FileRegister; boost::shared_ptr m_OriginConnection; @@ -359,6 +375,7 @@ private: std::wstring m_Name; std::map m_Files; std::vector m_SubDirectories; + SubDirectoriesMap m_SubDirectoriesMap; DirectoryEntry *m_Parent; std::set m_Origins; -- cgit v1.3.1 From 2be531470d54fa56307e392ad5bdfbc02048a744 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 23:00:18 -0500 Subject: renamed ToLower() to avoid confusion with in-place vs copy pre-hashed file lookup in DirectoryEntry --- src/filetree.cpp | 5 ++ src/filetree.h | 1 + src/filetreeitem.cpp | 26 ++++----- src/filetreeitem.h | 31 ++++++++--- src/filetreemodel.cpp | 40 +++++++------- src/filetreemodel.h | 13 ++++- src/organizercore.cpp | 10 ---- src/organizercore.h | 12 +++- src/shared/directoryentry.cpp | 125 +++++++++++++++++++++++++++++++++++++----- src/shared/directoryentry.h | 88 ++++++++++++++++++----------- src/shared/util.cpp | 12 ++-- src/shared/util.h | 8 +-- 12 files changed, 258 insertions(+), 113 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetree.cpp b/src/filetree.cpp index 8fc9e908..71a49200 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -144,6 +144,11 @@ void FileTree::refresh() m_model->refresh(); } +void FileTree::clear() +{ + m_model->clear(); +} + FileTreeItem* FileTree::singleSelection() { const auto sel = m_tree->selectionModel()->selectedRows(); diff --git a/src/filetree.h b/src/filetree.h index 77d5012c..1a17354f 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -20,6 +20,7 @@ public: FileTreeModel* model(); void refresh(); + void clear(); void open(); void openHooked(); diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 0469dfcc..39cdd9c4 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -6,24 +6,22 @@ using namespace MOBase; using namespace MOShared; -FileTreeItem::FileTreeItem() - : m_flags(NoFlags), m_loaded(false) -{ -} - FileTreeItem::FileTreeItem( FileTreeItem* parent, int originID, std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, std::wstring file, std::wstring mod) : - m_parent(parent), m_originID(originID), - m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), - m_realPath(QString::fromStdWString(realPath)), - m_flags(flags), - m_wsFile(file), m_wsLcFile(ToLower(file)), - m_file(QString::fromStdWString(file)), - m_mod(QString::fromStdWString(mod)), - m_loaded(false), - m_expanded(false) + m_parent(parent), + m_originID(originID), + m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), + m_realPath(QString::fromStdWString(realPath)), + m_flags(flags), + m_wsFile(file), + m_wsLcFile(ToLowerCopy(file)), + m_key(m_wsLcFile), + m_file(QString::fromStdWString(file)), + m_mod(QString::fromStdWString(mod)), + m_loaded(false), + m_expanded(false) { } diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 1e820f3f..fb9eccce 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -1,6 +1,7 @@ #ifndef MODORGANIZER_FILETREEITEM_INCLUDED #define MODORGANIZER_FILETREEITEM_INCLUDED +#include "directoryentry.h" #include class FileTreeItem @@ -16,7 +17,6 @@ public: Q_DECLARE_FLAGS(Flags, Flag); - FileTreeItem(); FileTreeItem( FileTreeItem* parent, int originID, std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, @@ -35,6 +35,12 @@ public: void insert(std::unique_ptr child, std::size_t at); void remove(std::size_t i); + void clear() + { + m_children.clear(); + m_loaded = false; + } + const std::vector>& children() const { return m_children; @@ -57,6 +63,7 @@ public: } QString virtualPath() const; + const QString& filename() const { return m_file; @@ -72,6 +79,11 @@ public: return m_wsLcFile; } + const MOShared::DirectoryEntry::FileKey& key() const + { + return m_key; + } + const QString& mod() const { return m_mod; @@ -152,13 +164,16 @@ public: private: FileTreeItem* m_parent; - int m_originID; - QString m_virtualParentPath; - QString m_realPath; - Flags m_flags; - std::wstring m_wsFile, m_wsLcFile; - QString m_file; - QString m_mod; + + const int m_originID; + const QString m_virtualParentPath; + const QString m_realPath; + const Flags m_flags; + const std::wstring m_wsFile, m_wsLcFile; + const MOShared::DirectoryEntry::FileKey m_key; + const QString m_file; + const QString m_mod; + bool m_loaded; bool m_expanded; std::vector> m_children; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 9332b354..ed62b8ae 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -17,9 +17,13 @@ void trace(F&&) } -FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) - : QAbstractItemModel(parent), m_core(core), m_flags(NoFlags) +FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : + QAbstractItemModel(parent), m_core(core), + m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""), + m_flags(NoFlags) { + m_root.setExpanded(true); + connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); }); connect( @@ -33,21 +37,6 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) }); } -void FileTreeModel::setFlags(Flags f) -{ - m_flags = f; -} - -bool FileTreeModel::showConflicts() const -{ - return (m_flags & Conflicts); -} - -bool FileTreeModel::showArchives() const -{ - return (m_flags & Archives) && m_core.getArchiveParsing(); -} - void FileTreeModel::refresh() { if (m_root.hasChildren()) { @@ -56,13 +45,24 @@ void FileTreeModel::refresh() } else { TimeThis tt("FileTreeModel::fill()"); beginResetModel(); - m_root = {nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""}; - m_root.setExpanded(true); + m_root.clear(); fill(m_root, *m_core.directoryStructure(), L""); endResetModel(); } } +void FileTreeModel::clear() +{ + beginResetModel(); + m_root.clear(); + endResetModel(); +} + +bool FileTreeModel::showArchives() const +{ + return (m_flags & Archives) && m_core.getArchiveParsing(); +} + void FileTreeModel::ensureLoaded(FileTreeItem* item) const { if (!item) { @@ -426,7 +426,7 @@ void FileTreeModel::updateFiles( continue; } - if (auto f=parentEntry.findFile(item->filenameWsLowerCase(), true)) { + if (auto f=parentEntry.findFile(item->key())) { if (shouldShowFile(*f)) { // file still exists trace([&]{ log::debug("{} still exists", item->debugName()); }); diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 0cfe19c7..8a1738b3 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -23,8 +23,13 @@ public: FileTreeModel(OrganizerCore& core, QObject* parent=nullptr); - void setFlags(Flags f); + void setFlags(Flags f) + { + m_flags = f; + } + void refresh(); + void clear(); QModelIndex index(int row, int col, const QModelIndex& parent={}) const override; QModelIndex parent(const QModelIndex& index) const override; @@ -53,7 +58,11 @@ private: mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; - bool showConflicts() const; + bool showConflicts() const + { + return (m_flags & Conflicts); + } + bool showArchives() const; void fill( diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 336be37d..c4f9e081 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -500,16 +500,6 @@ std::wstring OrganizerCore::crashDumpsPath() { ).toStdWString(); } -bool OrganizerCore::getArchiveParsing() const -{ - return m_ArchiveParsing; -} - -void OrganizerCore::setArchiveParsing(const bool archiveParsing) -{ - m_ArchiveParsing = archiveParsing; -} - void OrganizerCore::setCurrentProfile(const QString &profileName) { if ((m_CurrentProfile != nullptr) diff --git a/src/organizercore.h b/src/organizercore.h index 6c9edb9f..4ee6ddc5 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -218,8 +218,16 @@ public: bool onFinishedRun(const std::function &func); void refreshModList(bool saveChanges = true); QStringList modsSortedByProfilePriority() const; - bool getArchiveParsing() const; - void setArchiveParsing(bool archiveParsing); + + bool getArchiveParsing() const + { + return m_ArchiveParsing; + } + + void setArchiveParsing(bool archiveParsing) + { + m_ArchiveParsing = archiveParsing; + } public: // IPluginDiagnose interface diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 97da1061..639d6cac 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -482,6 +482,8 @@ const std::wstring &DirectoryEntry::getName() const void DirectoryEntry::clear() { m_Files.clear(); + m_FilesLookup.clear(); + for (DirectoryEntry *entry : m_SubDirectories) { delete entry; } @@ -663,6 +665,8 @@ void DirectoryEntry::removeDirRecursive() m_FileRegister->removeFile(m_Files.begin()->second); } + m_FilesLookup.clear(); + for (DirectoryEntry *entry : m_SubDirectories) { entry->removeDirRecursive(); delete entry; @@ -709,6 +713,56 @@ void DirectoryEntry::removeDir(const std::wstring &path) } } +bool DirectoryEntry::remove(const std::wstring &fileName, int *origin) +{ + const auto lcFileName = ToLowerCopy(fileName); + + auto iter = m_Files.find(lcFileName); + bool b = false; + + if (iter != m_Files.end()) { + if (origin != nullptr) { + FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); + if (entry.get() != nullptr) { + bool ignore; + *origin = entry->getOrigin(ignore); + } + } + + b = m_FileRegister->removeFile(iter->second); + } + + if (m_Files.size() != m_FilesLookup.size()) { + DebugBreak(); + } + + return b; +} + +void DirectoryEntry::insert( + const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, + const std::wstring &archive, int order) +{ + std::wstring fileNameLower = ToLowerCopy(fileName); + auto iter = m_Files.find(fileNameLower); + FileEntry::Ptr file; + + if (iter != m_Files.end()) { + file = m_FileRegister->getFile(iter->second); + } else { + file = m_FileRegister->createFile(fileName, this); + m_Files.emplace(fileNameLower, file->getIndex()); + m_FilesLookup.emplace(fileNameLower, file->getIndex()); + } + + if (m_Files.size() != m_FilesLookup.size()) { + DebugBreak(); + } + + file->addOrigin(origin.getID(), fileTime, archive, order); + origin.addFile(file->getIndex()); +} + bool DirectoryEntry::hasContentsFromOrigin(int originID) const { return m_Origins.find(originID) != m_Origins.end(); @@ -726,13 +780,34 @@ void DirectoryEntry::insertFile(const std::wstring &filePath, FilesOrigin &origi } } - void DirectoryEntry::removeFile(FileEntry::Index index) { + if (!m_FilesLookup.empty()) { + auto iter = std::find_if( + m_FilesLookup.begin(), m_FilesLookup.end(), + [&index](auto&& pair) { return (pair.second == index); } + ); + + if (iter != m_FilesLookup.end()) { + m_FilesLookup.erase(iter); + } else { + log::error( + "file \"{}\" not in directory for lookup \"{}\"", + m_FileRegister->getFile(index)->getName(), this->getName()); + } + } else { + log::error( + "file \"{}\" not in directory \"{}\" for lookup, directory empty", + m_FileRegister->getFile(index)->getName(), this->getName()); + } + if (!m_Files.empty()) { - auto iter = std::find_if(m_Files.begin(), m_Files.end(), - [&index](const std::pair &iter) -> bool { - return iter.second == index; } ); + auto iter = std::find_if( + m_Files.begin(), m_Files.end(), + [&index](const std::pair &iter) -> bool { + return iter.second == index; } + ); + if (iter != m_Files.end()) { m_Files.erase(iter); } else { @@ -745,14 +820,25 @@ void DirectoryEntry::removeFile(FileEntry::Index index) QObject::tr("file \"{}\" not in directory \"{}\", directory empty").toStdString(), m_FileRegister->getFile(index)->getName(), this->getName()); } -} + if (m_Files.size() != m_FilesLookup.size()) { + DebugBreak(); + } +} void DirectoryEntry::removeFiles(const std::set &indices) { for (auto iter = m_Files.begin(); iter != m_Files.end();) { if (indices.find(iter->second) != indices.end()) { - m_Files.erase(iter++); + iter = m_Files.erase(iter); + } else { + ++iter; + } + } + + for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) { + if (indices.find(iter->second) != indices.end()) { + iter = m_FilesLookup.erase(iter); } else { ++iter; } @@ -851,7 +937,7 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const if (len == std::string::npos) { // no more path components - auto iter = m_Files.find(ToLower(path)); + auto iter = m_Files.find(ToLowerCopy(path)); if (iter != m_Files.end()) { return m_FileRegister->getFile(iter->second); } else if (directory != nullptr) { @@ -884,7 +970,7 @@ DirectoryEntry *DirectoryEntry::findSubDirectory( if (alreadyLowerCase) { itor = m_SubDirectoriesMap.find(name); } else { - itor = m_SubDirectoriesMap.find(ToLower(name)); + itor = m_SubDirectoriesMap.find(ToLowerCopy(name)); } if (itor == m_SubDirectoriesMap.end()) { @@ -904,15 +990,26 @@ DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &pa const FileEntry::Ptr DirectoryEntry::findFile( const std::wstring &name, bool alreadyLowerCase) const { - std::map::const_iterator iter; + FilesLookup::const_iterator iter; if (alreadyLowerCase) { - iter = m_Files.find(name); + iter = m_FilesLookup.find(FileKey(name)); } else { - iter = m_Files.find(ToLower(name)); + iter = m_FilesLookup.find(FileKey(ToLowerCopy(name))); } - if (iter != m_Files.end()) { + if (iter != m_FilesLookup.end()) { + return m_FileRegister->getFile(iter->second); + } else { + return FileEntry::Ptr(); + } +} + +const FileEntry::Ptr DirectoryEntry::findFile(const FileKey& key) const +{ + auto iter = m_FilesLookup.find(key); + + if (iter != m_FilesLookup.end()) { return m_FileRegister->getFile(iter->second); } else { return FileEntry::Ptr(); @@ -921,7 +1018,7 @@ const FileEntry::Ptr DirectoryEntry::findFile( bool DirectoryEntry::hasFile(const std::wstring& name) const { - return m_Files.contains(ToLower(name)); + return m_Files.contains(ToLowerCopy(name)); } DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool create, int originID) @@ -936,7 +1033,7 @@ DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool c name, this, originID, m_FileRegister, m_OriginConnection); m_SubDirectories.push_back(entry); - m_SubDirectoriesMap.emplace(ToLower(name), entry); + m_SubDirectoriesMap.emplace(ToLowerCopy(name), entry); return entry; } else { diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 79bc5cf2..fc68cae7 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -35,6 +35,20 @@ along with Mod Organizer. If not, see . #endif #include "util.h" +namespace MOShared { struct DirectoryEntryFileKey; } + +namespace std +{ + template <> + struct hash + { + using argument_type = MOShared::DirectoryEntryFileKey; + using result_type = std::size_t; + + inline result_type operator()(const argument_type& key) const; + }; +} + namespace MOShared { @@ -203,9 +217,32 @@ private: }; +struct DirectoryEntryFileKey +{ + DirectoryEntryFileKey(std::wstring v) + : value(std::move(v)), hash(getHash(value)) + { + } + + bool operator==(const DirectoryEntryFileKey& o) const + { + return (value == o.value); + } + + static std::size_t getHash(const std::wstring& value) + { + return std::hash()(value); + } + + const std::wstring value; + const std::size_t hash; +}; + + class DirectoryEntry { public: + using FileKey = DirectoryEntryFileKey; DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID); @@ -294,6 +331,7 @@ public: * @return fileentry object for the file or nullptr if no file matches */ const FileEntry::Ptr findFile(const std::wstring &name, bool alreadyLowerCase=false) const; + const FileEntry::Ptr findFile(const FileKey& key) const; bool hasFile(const std::wstring& name) const; bool containsArchive(std::wstring archiveName); @@ -315,21 +353,7 @@ public: */ void removeDir(const std::wstring &path); - bool remove(const std::wstring &fileName, int *origin) { - auto iter = m_Files.find(ToLower(fileName)); - if (iter != m_Files.end()) { - if (origin != nullptr) { - FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); - if (entry.get() != nullptr) { - bool ignore; - *origin = entry->getOrigin(ignore); - } - } - return m_FileRegister->removeFile(iter->second); - } else { - return false; - } - } + bool remove(const std::wstring &fileName, int *origin); bool hasContentsFromOrigin(int originID) const; @@ -342,20 +366,9 @@ private: DirectoryEntry(const DirectoryEntry &reference); DirectoryEntry &operator=(const DirectoryEntry &reference); - void insert(const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) { - std::wstring fileNameLower = ToLower(fileName); - auto iter = m_Files.find(fileNameLower); - FileEntry::Ptr file; - if (iter != m_Files.end()) { - file = m_FileRegister->getFile(iter->second); - } else { - file = m_FileRegister->createFile(fileName, this); - // TODO this has been observed to cause a crash, no clue why - m_Files[fileNameLower] = file->getIndex(); - } - file->addOrigin(origin.getID(), fileTime, archive, order); - origin.addFile(file->getIndex()); - } + void insert( + const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, + const std::wstring &archive, int order); void addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset); void addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName, int order); @@ -367,13 +380,16 @@ private: void removeDirRecursive(); private: + using FilesMap = std::map; + using FilesLookup = std::unordered_map; using SubDirectoriesMap = std::unordered_map; boost::shared_ptr m_FileRegister; boost::shared_ptr m_OriginConnection; std::wstring m_Name; - std::map m_Files; + FilesMap m_Files; + FilesLookup m_FilesLookup; std::vector m_SubDirectories; SubDirectoriesMap m_SubDirectoriesMap; @@ -386,7 +402,17 @@ private: }; - } // namespace MOShared + +namespace std +{ + hash::result_type + hash::operator()( + const argument_type& key) const + { + return key.hash; + } +} + #endif // DIRECTORYENTRY_H diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 4ac95465..009aad70 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -102,32 +102,28 @@ static auto locToLower = [] (char in) -> char { return std::tolower(in, loc); }; -std::string &ToLower(std::string &text) +std::string& ToLowerInPlace(std::string& text) { - //std::transform(text.begin(), text.end(), text.begin(), locToLower); CharLowerBuffA(const_cast(text.c_str()), static_cast(text.size())); return text; } -std::string ToLower(const std::string &text) +std::string ToLowerCopy(const std::string& text) { std::string result(text); - //std::transform(result.begin(), result.end(), result.begin(), locToLower); CharLowerBuffA(const_cast(result.c_str()), static_cast(result.size())); return result; } -std::wstring &ToLower(std::wstring &text) +std::wstring& ToLowerInPlace(std::wstring& text) { - //std::transform(text.begin(), text.end(), text.begin(), locToLowerW); CharLowerBuffW(const_cast(text.c_str()), static_cast(text.size())); return text; } -std::wstring ToLower(const std::wstring &text) +std::wstring ToLowerCopy(const std::wstring& text) { std::wstring result(text); - //std::transform(result.begin(), result.end(), result.begin(), locToLowerW); CharLowerBuffW(const_cast(result.c_str()), static_cast(result.size())); return result; } diff --git a/src/shared/util.h b/src/shared/util.h index 788d2444..79cadf71 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -36,11 +36,11 @@ bool FileExists(const std::wstring &searchPath, const std::wstring &filename); std::string ToString(const std::wstring &source, bool utf8); std::wstring ToWString(const std::string &source, bool utf8); -std::string &ToLower(std::string &text); -std::string ToLower(const std::string &text); +std::string& ToLowerInPlace(std::string& text); +std::string ToLowerCopy(const std::string& text); -std::wstring &ToLower(std::wstring &text); -std::wstring ToLower(const std::wstring &text); +std::wstring& ToLowerInPlace(std::wstring& text); +std::wstring ToLowerCopy(const std::wstring& text); bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs); -- cgit v1.3.1 From 08188830a3cf67924980b4183e2ca7a4b5e12c3d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 14 Jan 2020 16:52:33 -0500 Subject: removed LEAK_TRACE stuff refactored DirectoryEntry for lookup maps filetreemodel: made ensureLoaded() a no-op while refreshing, faster processing for removing rows --- src/filetreeitem.cpp | 13 +++ src/filetreeitem.h | 1 + src/filetreemodel.cpp | 66 ++++++++++--- src/filetreemodel.h | 1 + src/shared/directoryentry.cpp | 219 ++++++++++++++++++------------------------ src/shared/directoryentry.h | 20 ++-- 6 files changed, 174 insertions(+), 146 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 39cdd9c4..33a70798 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -48,6 +48,19 @@ void FileTreeItem::remove(std::size_t i) m_children.erase(m_children.begin() + i); } +void FileTreeItem::remove(std::size_t from, std::size_t n) +{ + if ((from + n) > m_children.size()) { + log::error("{}: can't remove children from {} n={}", debugName(), from, n); + return; + } + + auto begin = m_children.begin() + from; + auto end = begin + n; + + m_children.erase(begin, end); +} + QString FileTreeItem::virtualPath() const { QString s = "Data\\"; diff --git a/src/filetreeitem.h b/src/filetreeitem.h index fb9eccce..70757ca2 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -34,6 +34,7 @@ public: void insert(std::unique_ptr child, std::size_t at); void remove(std::size_t i); + void remove(std::size_t from, std::size_t n); void clear() { diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index ed62b8ae..91dd4b90 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -20,7 +20,7 @@ void trace(F&&) FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""), - m_flags(NoFlags) + m_flags(NoFlags), m_isRefreshing(false) { m_root.setExpanded(true); @@ -39,6 +39,9 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : void FileTreeModel::refresh() { + m_isRefreshing = true; + Guard g([&]{ m_isRefreshing = false; }); + if (m_root.hasChildren()) { TimeThis tt("FileTreeModel::update()"); update(m_root, *m_core.directoryStructure(), L""); @@ -65,6 +68,10 @@ bool FileTreeModel::showArchives() const void FileTreeModel::ensureLoaded(FileTreeItem* item) const { + if (m_isRefreshing) { + return; + } + if (!item) { log::error("ensureLoaded(): item is null"); return; @@ -247,7 +254,7 @@ void FileTreeModel::updateDirectories( }); int row = 0; - std::vector remove; + std::list remove; std::unordered_set seen; for (auto&& item : parentItem.children()) { @@ -329,26 +336,59 @@ void FileTreeModel::updateDirectories( parentItem.debugName()); }); - for (auto* toRemove : remove) { - const auto& cs = parentItem.children(); - for (std::size_t i=0; i(i), 0); + if (!parentIndex.isValid()) { + parentIndex = parent(indexFromItem( + toRemove, static_cast(i), 0)); + } - const auto parentIndex = parent(itemIndex); - const int first = static_cast(i); - const int last = static_cast(i); + if (first == -1) { + first = i; + last = i; + } else if (i == (last + 1)) { + last = i; + } else { + beginRemoveRows(parentIndex, first, last); - beginRemoveRows(parentIndex, first, last); - parentItem.remove(i); - endRemoveRows(); + parentItem.remove( + static_cast(first), + static_cast(last - first + 1)); + + endRemoveRows(); + + first = i; + last = i; + } + remove.erase(itor); break; } } } + + if (first != -1) { + beginRemoveRows(parentIndex, first, last); + + parentItem.remove( + static_cast(first), + static_cast(last - first + 1)); + + endRemoveRows(); + } } diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 8a1738b3..c363266a 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -57,6 +57,7 @@ private: mutable IconFetcher m_iconFetcher; mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; + bool m_isRefreshing; bool showConflicts() const { diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index c44be9a1..819075ae 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -19,7 +19,6 @@ along with Mod Organizer. If not, see . #include "directoryentry.h" #include "windows_error.h" -#include "leaktrace.h" #include "error_report.h" #include #include @@ -32,7 +31,6 @@ along with Mod Organizer. If not, see . #include #include #include -#include namespace MOShared { @@ -80,12 +78,6 @@ public: OriginConnection() : m_NextID(0) { - LEAK_TRACE; - } - - ~OriginConnection() - { - LEAK_UNTRACE; } FilesOrigin& createOrigin( @@ -166,19 +158,12 @@ FileEntry::FileEntry() : m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr), m_LastAccessed(time(nullptr)) { - LEAK_TRACE; } FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) : m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent), m_LastAccessed(time(nullptr)) { - LEAK_TRACE; -} - -FileEntry::~FileEntry() -{ - LEAK_UNTRACE; } void FileEntry::addOrigin( @@ -399,7 +384,6 @@ bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) FilesOrigin::FilesOrigin() : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0) { - LEAK_TRACE; } FilesOrigin::FilesOrigin(const FilesOrigin &reference) @@ -411,7 +395,6 @@ FilesOrigin::FilesOrigin(const FilesOrigin &reference) , m_FileRegister(reference.m_FileRegister) , m_OriginConnection(reference.m_OriginConnection) { - LEAK_TRACE; } FilesOrigin::FilesOrigin( @@ -422,12 +405,6 @@ FilesOrigin::FilesOrigin( m_Priority(priority), m_FileRegister(fileRegister), m_OriginConnection(originConnection) { - LEAK_TRACE; -} - -FilesOrigin::~FilesOrigin() -{ - LEAK_UNTRACE; } void FilesOrigin::setPriority(int priority) @@ -504,13 +481,6 @@ bool FilesOrigin::containsArchive(std::wstring archiveName) FileRegister::FileRegister(boost::shared_ptr originConnection) : m_OriginConnection(originConnection) { - LEAK_TRACE; -} - -FileRegister::~FileRegister() -{ - LEAK_UNTRACE; - m_Files.clear(); } bool FileRegister::indexValid(FileEntry::Index index) const @@ -650,7 +620,6 @@ DirectoryEntry::DirectoryEntry( { m_FileRegister.reset(new FileRegister(m_OriginConnection)); m_Origins.insert(originID); - LEAK_TRACE; } DirectoryEntry::DirectoryEntry( @@ -660,13 +629,11 @@ DirectoryEntry::DirectoryEntry( m_FileRegister(fileRegister), m_OriginConnection(originConnection), m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(false) { - LEAK_TRACE; m_Origins.insert(originID); } DirectoryEntry::~DirectoryEntry() { - LEAK_UNTRACE; clear(); } @@ -680,7 +647,7 @@ void DirectoryEntry::clear() } m_SubDirectories.clear(); - m_SubDirectoriesMap.clear(); + m_SubDirectoriesLookup.clear(); } void DirectoryEntry::addFromOrigin( @@ -735,7 +702,7 @@ void DirectoryEntry::addFromBSA( stream << QObject::tr("invalid bsa file: ").toStdString() << ToString(fileName, false) - << " errorcode " << res << " - " << ::GetLastError(); + << " error code " << res << " - " << ::GetLastError(); throw std::runtime_error(stream.str()); } @@ -806,15 +773,15 @@ std::vector DirectoryEntry::getFiles() const DirectoryEntry *DirectoryEntry::findSubDirectory( const std::wstring &name, bool alreadyLowerCase) const { - SubDirectoriesMap::const_iterator itor; + SubDirectoriesLookup::const_iterator itor; if (alreadyLowerCase) { - itor = m_SubDirectoriesMap.find(name); + itor = m_SubDirectoriesLookup.find(name); } else { - itor = m_SubDirectoriesMap.find(ToLowerCopy(name)); + itor = m_SubDirectoriesLookup.find(ToLowerCopy(name)); } - if (itor == m_SubDirectoriesMap.end()) { + if (itor == m_SubDirectoriesLookup.end()) { return nullptr; } @@ -936,48 +903,7 @@ void DirectoryEntry::insertFile( void DirectoryEntry::removeFile(FileEntry::Index index) { - if (!m_FilesLookup.empty()) { - auto iter = std::find_if( - m_FilesLookup.begin(), m_FilesLookup.end(), - [&index](auto&& pair) { return (pair.second == index); } - ); - - if (iter != m_FilesLookup.end()) { - m_FilesLookup.erase(iter); - } else { - log::error( - QObject::tr("file \"{}\" not in directory for lookup \"{}\"").toStdString(), - m_FileRegister->getFile(index)->getName(), this->getName()); - } - } else { - log::error( - QObject::tr("file \"{}\" not in directory \"{}\" for lookup, directory empty").toStdString(), - m_FileRegister->getFile(index)->getName(), this->getName()); - } - - if (!m_Files.empty()) { - auto iter = std::find_if( - m_Files.begin(), m_Files.end(), - [&index](const std::pair &iter) -> bool { - return iter.second == index; } - ); - - if (iter != m_Files.end()) { - m_Files.erase(iter); - } else { - log::error( - QObject::tr("file \"{}\" not in directory \"{}\"").toStdString(), - m_FileRegister->getFile(index)->getName(), this->getName()); - } - } else { - log::error( - QObject::tr("file \"{}\" not in directory \"{}\", directory empty").toStdString(), - m_FileRegister->getFile(index)->getName(), this->getName()); - } - - if (m_Files.size() != m_FilesLookup.size()) { - DebugBreak(); - } + removeFileFromList(index); } bool DirectoryEntry::removeFile(const std::wstring &filePath, int *origin) @@ -1009,21 +935,7 @@ void DirectoryEntry::removeDir(const std::wstring &path) if (CaseInsensitiveEqual(entry->getName(), path)) { entry->removeDirRecursive(); - - bool found = false; - for (auto iter2=m_SubDirectoriesMap.begin(); iter2!=m_SubDirectoriesMap.end(); ++iter2) { - if (iter2->second == entry) { - m_SubDirectoriesMap.erase(iter2); - found = true; - break; - } - } - - if (!found) { - log::error("entry {} not in sub directories map", entry->getName()); - } - - m_SubDirectories.erase(iter); + removeDirectoryFromList(iter); delete entry; break; } @@ -1058,10 +970,6 @@ bool DirectoryEntry::remove(const std::wstring &fileName, int *origin) b = m_FileRegister->removeFile(iter->second); } - if (m_Files.size() != m_FilesLookup.size()) { - DebugBreak(); - } - return b; } @@ -1085,41 +993,21 @@ FilesOrigin &DirectoryEntry::createOrigin( void DirectoryEntry::removeFiles(const std::set &indices) { - for (auto iter = m_Files.begin(); iter != m_Files.end();) { - if (indices.find(iter->second) != indices.end()) { - iter = m_Files.erase(iter); - } else { - ++iter; - } - } - - for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) { - if (indices.find(iter->second) != indices.end()) { - iter = m_FilesLookup.erase(iter); - } else { - ++iter; - } - } + removeFilesFromList(indices); } void DirectoryEntry::insert( const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) { - std::wstring fileNameLower = ToLowerCopy(fileName); - auto iter = m_Files.find(fileNameLower); + auto iter = m_Files.find(ToLowerCopy(fileName)); FileEntry::Ptr file; if (iter != m_Files.end()) { file = m_FileRegister->getFile(iter->second); } else { file = m_FileRegister->createFile(fileName, this); - m_Files.emplace(fileNameLower, file->getIndex()); - m_FilesLookup.emplace(fileNameLower, file->getIndex()); - } - - if (m_Files.size() != m_FilesLookup.size()) { - DebugBreak(); + addFileToList(*file); } file->addOrigin(origin.getID(), fileTime, archive, order); @@ -1198,8 +1086,7 @@ DirectoryEntry *DirectoryEntry::getSubDirectory( auto* entry = new DirectoryEntry( name, this, originID, m_FileRegister, m_OriginConnection); - m_SubDirectories.push_back(entry); - m_SubDirectoriesMap.emplace(ToLowerCopy(name), entry); + addDirectoryToList(entry); return entry; } else { @@ -1244,7 +1131,87 @@ void DirectoryEntry::removeDirRecursive() } m_SubDirectories.clear(); - m_SubDirectoriesMap.clear(); + m_SubDirectoriesLookup.clear(); +} + +void DirectoryEntry::addDirectoryToList(DirectoryEntry* e) +{ + m_SubDirectories.push_back(e); + m_SubDirectoriesLookup.emplace(ToLowerCopy(e->getName()), e); +} + +void DirectoryEntry::removeDirectoryFromList(SubDirectories::iterator itor) +{ + const auto* entry = *itor; + + { + auto itor2 = std::find_if( + m_SubDirectoriesLookup.begin(), m_SubDirectoriesLookup.end(), + [&](auto&& d) { return (d.second == entry); }); + + if (itor2 == m_SubDirectoriesLookup.end()) { + log::error("entry {} not in sub directories map", entry->getName()); + } else { + m_SubDirectoriesLookup.erase(itor2); + } + } + + m_SubDirectories.erase(itor); +} + +void DirectoryEntry::removeFileFromList(FileEntry::Index index) +{ + auto removeFrom = [&](auto& list) { + auto iter = std::find_if( + list.begin(), list.end(), + [&index](auto&& pair) { return (pair.second == index); } + ); + + if (iter == list.end()) { + auto f = m_FileRegister->getFile(index); + + if (f) { + log::error( + "can't remove file '{}', not in directory entry '{}'", + f->getName(), getName()); + } else { + log::error( + "can't remove file with index {}, not in directory entry '{}' and " + "not in register", + index, getName()); + } + } else { + list.erase(iter); + } + }; + + removeFrom(m_FilesLookup); + removeFrom(m_Files); +} + +void DirectoryEntry::removeFilesFromList(const std::set& indices) +{ + for (auto iter = m_Files.begin(); iter != m_Files.end();) { + if (indices.find(iter->second) != indices.end()) { + iter = m_Files.erase(iter); + } else { + ++iter; + } + } + + for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) { + if (indices.find(iter->second) != indices.end()) { + iter = m_FilesLookup.erase(iter); + } else { + ++iter; + } + } +} + +void DirectoryEntry::addFileToList(const FileEntry& f) +{ + m_Files.emplace(ToLowerCopy(f.getName()), f.getIndex()); + m_FilesLookup.emplace(ToLowerCopy(f.getName()), f.getIndex()); } } // namespace MOShared diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 98366493..91c2a140 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -67,7 +67,6 @@ public: FileEntry(); FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent); - ~FileEntry(); Index getIndex() const { @@ -160,7 +159,6 @@ class FilesOrigin public: FilesOrigin(); FilesOrigin(const FilesOrigin &reference); - ~FilesOrigin(); // sets priority for this origin, but it will overwrite the existing mapping // for this priority, the previous origin will no longer be referenced @@ -226,7 +224,6 @@ class FileRegister { public: FileRegister(boost::shared_ptr originConnection); - ~FileRegister(); bool indexValid(FileEntry::Index index) const; @@ -351,7 +348,8 @@ public: std::vector::const_iterator &begin, std::vector::const_iterator &end) const { - begin = m_SubDirectories.begin(); end = m_SubDirectories.end(); + begin = m_SubDirectories.begin(); + end = m_SubDirectories.end(); } template @@ -442,7 +440,8 @@ public: private: using FilesMap = std::map; using FilesLookup = std::unordered_map; - using SubDirectoriesMap = std::unordered_map; + using SubDirectories = std::vector; + using SubDirectoriesLookup = std::unordered_map; boost::shared_ptr m_FileRegister; boost::shared_ptr m_OriginConnection; @@ -450,8 +449,8 @@ private: std::wstring m_Name; FilesMap m_Files; FilesLookup m_FilesLookup; - std::vector m_SubDirectories; - SubDirectoriesMap m_SubDirectoriesMap; + SubDirectories m_SubDirectories; + SubDirectoriesLookup m_SubDirectoriesLookup; DirectoryEntry *m_Parent; std::set m_Origins; @@ -479,6 +478,13 @@ private: const std::wstring &path, bool create, int originID = -1); void removeDirRecursive(); + + void addDirectoryToList(DirectoryEntry* e); + void removeDirectoryFromList(SubDirectories::iterator itor); + + void addFileToList(const FileEntry& f); + void removeFileFromList(FileEntry::Index index); + void removeFilesFromList(const std::set& indices); }; } // namespace MOShared -- cgit v1.3.1 From 9c9de680c6d53755d3bf99a0c3af2e2d440f86f3 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 20 Jan 2020 17:58:09 -0500 Subject: faster parent() and indexFromItem() with index guess implemented files --- src/filetreeitem.cpp | 4 +- src/filetreeitem.h | 27 ++++++ src/filetreemodel.cpp | 237 ++++++++++++++++++++++++++++++++++++++++++++------ src/filetreemodel.h | 24 +++-- 4 files changed, 251 insertions(+), 41 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 33a70798..3b18cf54 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -6,11 +6,12 @@ using namespace MOBase; using namespace MOShared; + FileTreeItem::FileTreeItem( FileTreeItem* parent, int originID, std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, std::wstring file, std::wstring mod) : - m_parent(parent), + m_parent(parent), m_indexGuess(NoIndexGuess), m_originID(originID), m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), m_realPath(QString::fromStdWString(realPath)), @@ -35,6 +36,7 @@ void FileTreeItem::insert(std::unique_ptr child, std::size_t at) return; } + child->m_indexGuess = at; m_children.insert(m_children.begin() + at, std::move(child)); } diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 2819afbd..e060f63a 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -29,6 +29,7 @@ public: void add(std::unique_ptr child) { + child->m_indexGuess = m_children.size(); m_children.push_back(std::move(child)); } @@ -37,6 +38,11 @@ public: template void insert(Itor begin, Itor end, std::size_t at) { + std::size_t nextRowGuess = m_children.size(); + for (auto itor=begin; itor!=end; ++itor) { + (*itor)->m_indexGuess = nextRowGuess++; + } + m_children.insert(m_children.begin() + at, begin, end); } @@ -54,6 +60,23 @@ public: return m_children; } + int childIndex(const FileTreeItem& item) const + { + if (item.m_indexGuess < m_children.size()) { + if (m_children[item.m_indexGuess].get() == &item) { + return static_cast(item.m_indexGuess); + } + } + + for (std::size_t i=0; i(i); + } + } + + return -1; + } FileTreeItem* parent() { @@ -171,7 +194,11 @@ public: QString debugName() const; private: + static constexpr std::size_t NoIndexGuess = + std::numeric_limits::max(); + FileTreeItem* m_parent; + mutable std::size_t m_indexGuess; const int m_originID; const QString m_virtualParentPath; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 3be58f05..f5877dbb 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -78,16 +78,13 @@ QModelIndex FileTreeModel::parent(const QModelIndex& index) const return {}; } - if (auto* item=itemFromIndex(index)) { - if (auto* parent=item->parent()) { - return indexFromItem(*parent); - } else { - return {}; - } + auto* parentItem = static_cast(index.internalPointer()); + if (!parentItem) { + log::error("FileTreeModel::parent(): no internal pointer"); + return {}; } - log::error("FileTreeModel::parent(): no internal pointer"); - return {}; + return indexFromItem(*parentItem); } int FileTreeModel::rowCount(const QModelIndex& parent) const @@ -221,19 +218,16 @@ QModelIndex FileTreeModel::indexFromItem(FileTreeItem& item) const return {}; } - const auto& cs = parent->children(); + const int index = parent->childIndex(item); + if (index == -1) { + log::error( + "FileTreeMode::indexFromItem(): item {} not found in parent", + item.debugName()); - for (std::size_t i=0; i(i), 0, parent); - } + return {}; } - log::error( - "FileTreeMode::indexFromItem(): item {} not found in parent", - item.debugName()); - - return {}; + return createIndex(index, 0, parent); } void FileTreeModel::update( @@ -252,6 +246,7 @@ void FileTreeModel::update( } updateDirectories(parentItem, path, parentEntry, FillFlag::None); + updateFiles(parentItem, path, parentEntry); parentItem.setLoaded(true); } @@ -266,13 +261,13 @@ void FileTreeModel::updateDirectories( // use this to figure out if a directory is new or not std::unordered_set seen; - removeDisappearingDirectories(parentItem, parentEntry, seen); + removeDisappearingDirectories(parentItem, parentEntry, parentPath, seen); addNewDirectories(parentItem, parentEntry, parentPath, seen); } void FileTreeModel::removeDisappearingDirectories( FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, - std::unordered_set& seen) + const std::wstring& parentPath, std::unordered_set& seen) { auto& children = parentItem.children(); auto itor = children.begin(); @@ -295,25 +290,29 @@ void FileTreeModel::removeDisappearingDirectories( auto d = parentEntry.findSubDirectory(item->filenameWsLowerCase(), true); if (d) { - trace([&]{ log::debug("{} still there", item->filename()); }); + trace([&]{ log::debug("dir {} still there", item->filename()); }); // directory is still there - seen.insert(item->filenameWs()); + seen.emplace(item->filenameWs()); // if there were directories before this row that need to be removed, // do it now if (removeStart != -1) { removeRange(parentItem, removeStart, row - 1); - removeStart = -1; - // adjust current row to account for those that were just removed row -= (row - removeStart); itor = children.begin() + row; + + removeStart = -1; + } + + if (item->areChildrenVisible()) { + update(*item, *d, parentPath); } } else { // directory is gone from the parent entry - trace([&]{ log::debug("{} is gone", item->filename()); }); + trace([&]{ log::debug("dir {} is gone", item->filename()); }); if (removeStart == -1) { // start a new contiguous sequence @@ -356,7 +355,7 @@ void FileTreeModel::addNewDirectories( } } else { // this is a new directory - trace([&]{ log::debug("new {}", QString::fromStdWString(d->getName())); }); + trace([&]{ log::debug("new dir {}", QString::fromStdWString(d->getName())); }); auto item = std::make_unique( &parentItem, 0, parentPath, L"", FileTreeItem::Directory, @@ -412,3 +411,189 @@ void FileTreeModel::addRange( endInsertRows(); } + + +void FileTreeModel::updateFiles( + FileTreeItem& parentItem, const std::wstring& parentPath, + const MOShared::DirectoryEntry& parentEntry) +{ + // removeDisappearingFiles() will add files that are in the tree and still on + // the filesystem to this set; addNewFiless() will use this to figure out if + // a file is new or not + std::unordered_set seen; + + int firstFileRow = 0; + + removeDisappearingFiles(parentItem, parentEntry, firstFileRow, seen); + addNewFiles(parentItem, parentEntry, parentPath, firstFileRow, seen); +} + +void FileTreeModel::removeDisappearingFiles( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + int& firstFileRow, std::unordered_set& seen) +{ + auto& children = parentItem.children(); + auto itor = children.begin(); + + // keeps track of the contiguous directories that need to be removed to + // avoid calling beginRemoveRows(), etc. for each item + int removeStart = -1; + firstFileRow = 0; + + // directories are always first, so find the first file item + while (itor != children.end()) { + const auto& item = *itor; + + if (!item->isDirectory()) { + break; + } + + ++firstFileRow; + ++itor; + } + + if (itor == children.end()) { + // no file items + return; + } + + int row = firstFileRow; + + // for each item in this tree item + while (itor != children.end()) { + const auto& item = *itor; + + auto f = parentEntry.findFile(item->key()); + + if (f) { + trace([&]{ log::debug("file {} still there", item->filename()); }); + + // file is still there + seen.emplace(f->getIndex()); + + // if there were files before this row that need to be removed, + // do it now + if (removeStart != -1) { + removeRange(parentItem, removeStart, row - 1); + + // adjust current row to account for those that were just removed + row -= (row - removeStart); + itor = children.begin() + row; + + removeStart = -1; + } + } else { + // file is gone from the parent entry + trace([&]{ log::debug("file {} is gone", item->filename()); }); + + if (removeStart == -1) { + // start a new contiguous sequence + removeStart = row; + } + } + + ++row; + ++itor; + } + + // remove the last file range, if any + if (removeStart != -1) { + removeRange(parentItem, removeStart, row -1 ); + } +} + +void FileTreeModel::addNewFiles( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath, const int firstFileRow, + const std::unordered_set& seen) +{ + // keeps track of the contiguous files that need to be added to + // avoid calling beginAddRows(), etc. for each item + std::vector> toAdd; + int addStart = -1; + int row = firstFileRow; + + // for each directory on the filesystem + parentEntry.forEachFileIndex([&](auto&& fileIndex) { + if (seen.contains(fileIndex)) { + // already seen in the parent item + + // if there were directories before this row that need to be added, + // do it now + if (addStart != -1) { + addRange(parentItem, addStart, toAdd); + toAdd.clear(); + addStart = -1; + } + } else { + const auto file = parentEntry.getFileByIndex(fileIndex); + + if (!file) { + log::error( + "FileTreeModel::addNewFiles(): file index {} in path {} not found", + fileIndex, parentPath); + + return true; + } + + // this is a new file + trace([&]{ log::debug("new file {}", QString::fromStdWString(file->getName())); }); + + bool isArchive = false; + int originID = file->getOrigin(isArchive); + + FileTreeItem::Flags flags = FileTreeItem::NoFlags; + + if (isArchive) { + flags |= FileTreeItem::FromArchive; + } + + if (!file->getAlternatives().empty()) { + flags |= FileTreeItem::Conflicted; + } + + auto item = std::make_unique( + &parentItem, originID, parentPath, file->getFullPath(), flags, + file->getName(), makeModName(*file, originID)); + + item->setLoaded(true); + + toAdd.push_back(std::move(item)); + + if (addStart == -1) { + // start a new contiguous sequence + addStart = row; + } + } + + ++row; + + return true; + }); + + // add the last file range, if any + if (addStart != -1) { + addRange(parentItem, addStart, toAdd); + } +} + +std::wstring FileTreeModel::makeModName( + const MOShared::FileEntry& file, int originID) const +{ + static const std::wstring Unmanaged = UnmanagedModName().toStdWString(); + + const auto origin = m_core.directoryStructure()->getOriginByID(originID); + + if (origin.getID() == 0) { + return Unmanaged; + } + + std::wstring name = origin.getName(); + + const auto& archive = file.getArchive(); + if (!archive.first.empty()) { + name += L" (" + archive.first + L")"; + } + + return name; +} diff --git a/src/filetreemodel.h b/src/filetreemodel.h index af24c70d..70291b61 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -81,7 +81,7 @@ private: void removeDisappearingDirectories( FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, - std::unordered_set& seen); + const std::wstring& parentPath, std::unordered_set& seen); void addNewDirectories( FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, @@ -95,22 +95,18 @@ private: std::vector>& items); - void fill( - FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, - const std::wstring& parentPath); - - void fillDirectories( - FileTreeItem& parentItem, const std::wstring& path, - DirectoryIterator begin, DirectoryIterator end, FillFlags flags); - - void fillFiles( + void updateFiles( FileTreeItem& parentItem, const std::wstring& path, - const std::vector& files, FillFlags flags); + const MOShared::DirectoryEntry& parentEntry); + void removeDisappearingFiles( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + int& firstFileRow, std::unordered_set& seen); - void updateFiles( - FileTreeItem& parentItem, const std::wstring& path, - const MOShared::DirectoryEntry& parentEntry, FillFlags flags); + void addNewFiles( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath, int firstFileRow, + const std::unordered_set& seen); std::wstring makeModName(const MOShared::FileEntry& file, int originID) const; -- cgit v1.3.1 From a55a69e6ac56dfb6851b5538e4252e9d8dab402b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 22 Jan 2020 03:58:22 -0500 Subject: file size column --- src/filetreeitem.cpp | 10 ++++++++++ src/filetreeitem.h | 9 +++++++++ src/filetreemodel.cpp | 41 ++++++++++++++++++++++++++++++++--------- src/filetreemodel.h | 9 +++++++++ 4 files changed, 60 insertions(+), 9 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 3b18cf54..cfcf891a 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -99,6 +99,16 @@ QFont FileTreeItem::font() const return f; } +const FileTreeItem::Meta& FileTreeItem::meta() const +{ + if (!m_meta) { + QFile f(m_realPath); + m_meta = {static_cast(f.size())}; + } + + return *m_meta; +} + QFileIconProvider::IconType FileTreeItem::icon() const { if (m_flags & Directory) { diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 2677d590..445b1ea8 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -17,6 +17,12 @@ public: Conflicted = 0x04 }; + struct Meta + { + uint64_t size; + }; + + Q_DECLARE_FLAGS(Flags, Flag); FileTreeItem( @@ -124,6 +130,8 @@ public: QFont font() const; + const Meta& meta() const; + const QString& realPath() const { return m_realPath; @@ -210,6 +218,7 @@ private: const MOShared::DirectoryEntry::FileKey m_key; const QString m_file; const QString m_mod; + mutable std::optional m_meta; bool m_loaded; bool m_expanded; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 5b513ce2..351e0d2f 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -196,7 +196,7 @@ int FileTreeModel::rowCount(const QModelIndex& parent) const int FileTreeModel::columnCount(const QModelIndex&) const { - return 2; + return ColumnCount; } bool FileTreeModel::hasChildren(const QModelIndex& parent) const @@ -246,10 +246,31 @@ QVariant FileTreeModel::data(const QModelIndex& index, int role) const case Qt::DisplayRole: { if (auto* item=itemFromIndex(index)) { - if (index.column() == 0) { - return item->filename(); - } else if (index.column() == 1) { - return item->mod(); + switch (index.column()) + { + case Filename: + { + return item->filename(); + } + + case ModName: + { + return item->mod(); + } + + case FileSize: + { + if (item->isDirectory()) { + return {}; + } else { + return localizedByteSize(item->meta().size); + } + } + + default: + { + break; + } } } @@ -304,11 +325,13 @@ QVariant FileTreeModel::data(const QModelIndex& index, int role) const QVariant FileTreeModel::headerData(int i, Qt::Orientation ori, int role) const { + static const std::array names = { + tr("File"), tr("Mod"), tr("Size") + }; + if (role == Qt::DisplayRole) { - if (i == 0) { - return tr("File"); - } else if (i == 1) { - return tr("Mod"); + if (i > 0 && i < static_cast(names.size())) { + return names[static_cast(i)]; } } diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 305aba2a..7de6830d 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -20,6 +20,15 @@ public: Archives = 0x02 }; + enum Columns + { + Filename = 0, + ModName, + FileSize, + + ColumnCount + }; + Q_DECLARE_FLAGS(Flags, Flag); FileTreeModel(OrganizerCore& core, QObject* parent=nullptr); -- cgit v1.3.1 From 7c98635edc33d74ab24352e971bb6a127e7272e8 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 22 Jan 2020 04:29:44 -0500 Subject: filetype and last modified columns --- src/filetreeitem.cpp | 24 +++++++++++++++-- src/filetreeitem.h | 2 ++ src/filetreemodel.cpp | 71 +++++++++++++++++++++++++++++++-------------------- src/filetreemodel.h | 3 +++ 4 files changed, 70 insertions(+), 30 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index cfcf891a..e7510279 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -102,8 +102,28 @@ QFont FileTreeItem::font() const const FileTreeItem::Meta& FileTreeItem::meta() const { if (!m_meta) { - QFile f(m_realPath); - m_meta = {static_cast(f.size())}; + QFileInfo fi(m_realPath); + + SHFILEINFOW sfi = {}; + const auto r = SHGetFileInfoW( + m_realPath.toStdWString().c_str(), 0, &sfi, sizeof(sfi), + SHGFI_TYPENAME); + + if (!r) { + const auto e = GetLastError(); + + log::error( + "SHGetFileInfoW failed for '{}', {}", + m_realPath, e); + + sfi = {}; + } + + m_meta = { + static_cast(fi.size()), + fi.lastModified(), + QString::fromWCharArray(sfi.szTypeName) + }; } return *m_meta; diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 445b1ea8..01cd01a7 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -20,6 +20,8 @@ public: struct Meta { uint64_t size; + QDateTime lastModified; + QString type; }; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 351e0d2f..5424d14b 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -246,32 +246,7 @@ QVariant FileTreeModel::data(const QModelIndex& index, int role) const case Qt::DisplayRole: { if (auto* item=itemFromIndex(index)) { - switch (index.column()) - { - case Filename: - { - return item->filename(); - } - - case ModName: - { - return item->mod(); - } - - case FileSize: - { - if (item->isDirectory()) { - return {}; - } else { - return localizedByteSize(item->meta().size); - } - } - - default: - { - break; - } - } + return displayData(item, index.column()); } break; @@ -326,11 +301,11 @@ QVariant FileTreeModel::data(const QModelIndex& index, int role) const QVariant FileTreeModel::headerData(int i, Qt::Orientation ori, int role) const { static const std::array names = { - tr("File"), tr("Mod"), tr("Size") + tr("Name"), tr("Mod"), tr("Type"), tr("Size"), tr("Date modified") }; if (role == Qt::DisplayRole) { - if (i > 0 && i < static_cast(names.size())) { + if (i >= 0 && i < static_cast(names.size())) { return names[static_cast(i)]; } } @@ -671,6 +646,46 @@ std::unique_ptr FileTreeModel::createFileItem( return item; } +QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const +{ + switch (column) + { + case Filename: + { + return item->filename(); + } + + case ModName: + { + return item->mod(); + } + + case FileType: + { + return item->meta().type; + } + + case FileSize: + { + if (item->isDirectory()) { + return {}; + } else { + return localizedByteSize(item->meta().size); + } + } + + case LastModified: + { + return item->meta().lastModified.toString(Qt::SystemLocaleDate); + } + + default: + { + break; + } + } +} + std::wstring FileTreeModel::makeModName( const MOShared::FileEntry& file, int originID) const { diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 7de6830d..96e9e5a7 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -24,7 +24,9 @@ public: { Filename = 0, ModName, + FileType, FileSize, + LastModified, ColumnCount }; @@ -124,6 +126,7 @@ private: const MOShared::FileEntry& file); + QVariant displayData(const FileTreeItem* item, int column) const; std::wstring makeModName(const MOShared::FileEntry& file, int originID) const; void ensureLoaded(FileTreeItem* item) const; -- cgit v1.3.1 From c6c01f86c64a3a9fa666dddec860e52d388e5e97 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 22 Jan 2020 05:45:08 -0500 Subject: sort --- src/datatab.cpp | 4 +++ src/filetreeitem.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++ src/filetreeitem.h | 4 +++ src/filetreemodel.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++--------- src/filetreemodel.h | 20 +++++++++---- src/mainwindow.ui | 3 ++ 6 files changed, 159 insertions(+), 19 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/datatab.cpp b/src/datatab.cpp index 1b7baa82..af457b33 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -59,6 +59,10 @@ void DataTab::saveState(Settings& s) const void DataTab::restoreState(const Settings& s) { s.geometry().restoreState(ui.tree->header()); + + // prior to 2.3, the list was not sortable, and this remembered in the + // widget state, for whatever reason + ui.tree->setSortingEnabled(true); } void DataTab::activated() diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index e7510279..a506f0f0 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -1,6 +1,8 @@ #include "filetreeitem.h" +#include "filetreemodel.h" #include "modinfo.h" #include "util.h" +#include "modinfodialogfwd.h" #include using namespace MOBase; @@ -63,6 +65,71 @@ void FileTreeItem::remove(std::size_t from, std::size_t n) m_children.erase(begin, end); } + +template +int threeWayCompare(T&& a, T&& b) +{ + if (a < b) { + return -1; + } + + if (a > b) { + return 1; + } + + return 0; +} + +class FileTreeItem::Sorter +{ +public: + static int compare(int column, const FileTreeItem* a, const FileTreeItem* b) + { + switch (column) + { + case FileTreeModel::FileName: + return naturalCompare(a->m_file, b->m_file); + + case FileTreeModel::ModName: + return naturalCompare(a->m_mod, b->m_mod); + + case FileTreeModel::FileType: + return naturalCompare(a->meta().type, b->meta().type); + + case FileTreeModel::FileSize: + return threeWayCompare(a->meta().size, b->meta().size); + + case FileTreeModel::LastModified: + return threeWayCompare(a->meta().lastModified, b->meta().lastModified); + + default: + return 0; + } + } +}; + + +void FileTreeItem::sort(int column, Qt::SortOrder order) +{ + std::sort(m_children.begin(), m_children.end(), [&](auto&& a, auto&& b) { + int r = 0; + + if (a->isDirectory() && !b->isDirectory()) { + r = -1; + } else if (!a->isDirectory() && b->isDirectory()) { + r = 1; + } else { + r = FileTreeItem::Sorter::compare(column, a.get(), b.get()); + } + + if (order == Qt::AscendingOrder) { + return (r < 0); + } else { + return (r > 0); + } + }); +} + QString FileTreeItem::virtualPath() const { QString s = "Data\\"; diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 01cd01a7..fc27b0c0 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -6,6 +6,8 @@ class FileTreeItem { + class Sorter; + public: using Children = std::vector>; @@ -88,6 +90,8 @@ public: return -1; } + void sort(int column, Qt::SortOrder order); + FileTreeItem* parent() { return m_parent; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 5424d14b..aa053eeb 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -326,6 +326,39 @@ Qt::ItemFlags FileTreeModel::flags(const QModelIndex& index) const return f; } +void FileTreeModel::sort(int column, Qt::SortOrder order) +{ + emit layoutAboutToBeChanged(); + + m_sort.column = column; + m_sort.order = order; + + const auto oldList = persistentIndexList(); + std::vector> oldItems; + + const auto itemCount = oldList.size(); + oldItems.reserve(static_cast(itemCount)); + + for (int i=0; i(i)]; + newList.append(indexFromItem(*pair.first, pair.second)); + } + + changePersistentIndexList(oldList, newList); + + emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); +} + FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const { if (!index.isValid()) { @@ -349,7 +382,7 @@ FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const return parentItem->children()[index.row()].get(); } -QModelIndex FileTreeModel::indexFromItem(FileTreeItem& item) const +QModelIndex FileTreeModel::indexFromItem(FileTreeItem& item, int col) const { auto* parent = item.parent(); if (!parent) { @@ -365,7 +398,7 @@ QModelIndex FileTreeModel::indexFromItem(FileTreeItem& item) const return {}; } - return createIndex(index, 0, parent); + return createIndex(index, col, parent); } void FileTreeModel::update( @@ -383,13 +416,24 @@ void FileTreeModel::update( path += parentEntry.getName(); } - updateDirectories(parentItem, path, parentEntry, FillFlag::None); - updateFiles(parentItem, path, parentEntry); - parentItem.setLoaded(true); + + bool added = false; + + if (updateDirectories(parentItem, path, parentEntry, FillFlag::None)) { + added = true; + } + + if (updateFiles(parentItem, path, parentEntry)) { + added = true; + } + + if (added) { + parentItem.sort(m_sort.column, m_sort.order); + } } -void FileTreeModel::updateDirectories( +bool FileTreeModel::updateDirectories( FileTreeItem& parentItem, const std::wstring& parentPath, const MOShared::DirectoryEntry& parentEntry, FillFlags flags) { @@ -399,7 +443,7 @@ void FileTreeModel::updateDirectories( std::unordered_set seen; removeDisappearingDirectories(parentItem, parentEntry, parentPath, seen); - addNewDirectories(parentItem, parentEntry, parentPath, seen); + return addNewDirectories(parentItem, parentEntry, parentPath, seen); } void FileTreeModel::removeDisappearingDirectories( @@ -453,7 +497,7 @@ void FileTreeModel::removeDisappearingDirectories( range.remove(); } -void FileTreeModel::addNewDirectories( +bool FileTreeModel::addNewDirectories( FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, const std::wstring& parentPath, const std::unordered_set& seen) @@ -462,6 +506,7 @@ void FileTreeModel::addNewDirectories( // avoid calling beginAddRows(), etc. for each item Range range(this, parentItem); std::vector> toAdd; + bool added = false; // for each directory on the filesystem for (auto&& d : parentEntry.getSubDirectories()) { @@ -477,6 +522,8 @@ void FileTreeModel::addNewDirectories( trace([&]{ log::debug("new dir {}", QString::fromStdWString(d->getName())); }); toAdd.push_back(createDirectoryItem(parentItem, parentPath, *d)); + added = true; + range.includeCurrent(); } @@ -485,9 +532,11 @@ void FileTreeModel::addNewDirectories( // add the last directory range, if any range.add(std::move(toAdd)); + + return added; } -void FileTreeModel::updateFiles( +bool FileTreeModel::updateFiles( FileTreeItem& parentItem, const std::wstring& parentPath, const MOShared::DirectoryEntry& parentEntry) { @@ -499,7 +548,7 @@ void FileTreeModel::updateFiles( int firstFileRow = 0; removeDisappearingFiles(parentItem, parentEntry, firstFileRow, seen); - addNewFiles(parentItem, parentEntry, parentPath, firstFileRow, seen); + return addNewFiles(parentItem, parentEntry, parentPath, firstFileRow, seen); } void FileTreeModel::removeDisappearingFiles( @@ -557,7 +606,7 @@ void FileTreeModel::removeDisappearingFiles( } } -void FileTreeModel::addNewFiles( +bool FileTreeModel::addNewFiles( FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, const std::wstring& parentPath, const int firstFileRow, const std::unordered_set& seen) @@ -566,6 +615,7 @@ void FileTreeModel::addNewFiles( // avoid calling beginAddRows(), etc. for each item std::vector> toAdd; Range range(this, parentItem, firstFileRow); + bool added = false; // for each directory on the filesystem parentEntry.forEachFileIndex([&](auto&& fileIndex) { @@ -591,6 +641,8 @@ void FileTreeModel::addNewFiles( trace([&]{ log::debug("new file {}", QString::fromStdWString(file->getName())); }); toAdd.push_back(createFileItem(parentItem, parentPath, *file)); + added = true; + range.includeCurrent(); } @@ -601,6 +653,8 @@ void FileTreeModel::addNewFiles( // add the last file range, if any range.add(std::move(toAdd)); + + return added; } std::unique_ptr FileTreeModel::createDirectoryItem( @@ -650,7 +704,7 @@ QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const { switch (column) { - case Filename: + case FileName: { return item->filename(); } @@ -681,7 +735,7 @@ QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const default: { - break; + return {}; } } } diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 96e9e5a7..38f611f9 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -22,7 +22,7 @@ public: enum Columns { - Filename = 0, + FileName = 0, ModName, FileType, FileSize, @@ -53,6 +53,7 @@ public: QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const override; QVariant headerData(int i, Qt::Orientation ori, int role=Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; + void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override; FileTreeItem* itemFromIndex(const QModelIndex& index) const; @@ -63,6 +64,12 @@ private: PruneDirectories = 0x01 }; + struct Sort + { + int column = 0; + Qt::SortOrder order = Qt::AscendingOrder; + }; + class Range; Q_DECLARE_FLAGS(FillFlags, FillFlag); @@ -75,6 +82,7 @@ private: mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; bool m_isRefreshing; + Sort m_sort; bool showConflicts() const { @@ -89,7 +97,7 @@ private: const std::wstring& parentPath); - void updateDirectories( + bool updateDirectories( FileTreeItem& parentItem, const std::wstring& path, const MOShared::DirectoryEntry& parentEntry, FillFlags flags); @@ -97,13 +105,13 @@ private: FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, const std::wstring& parentPath, std::unordered_set& seen); - void addNewDirectories( + bool addNewDirectories( FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, const std::wstring& parentPath, const std::unordered_set& seen); - void updateFiles( + bool updateFiles( FileTreeItem& parentItem, const std::wstring& path, const MOShared::DirectoryEntry& parentEntry); @@ -111,7 +119,7 @@ private: FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, int& firstFileRow, std::unordered_set& seen); - void addNewFiles( + bool addNewFiles( FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, const std::wstring& parentPath, int firstFileRow, const std::unordered_set& seen); @@ -138,7 +146,7 @@ private: QString makeTooltip(const FileTreeItem& item) const; QVariant makeIcon(const FileTreeItem& item, const QModelIndex& index) const; - QModelIndex indexFromItem(FileTreeItem& item) const; + QModelIndex indexFromItem(FileTreeItem& item, int col=0) const; }; Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeModel::Flags); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index da9f949c..62425d8c 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1091,6 +1091,9 @@ p, li { white-space: pre-wrap; } true + + true + 400 -- cgit v1.3.1 From 34bc2191e6ebb1458438180bd75a5cd2cd7e4f7b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 1 Feb 2020 05:21:34 -0500 Subject: don't get meta for fields that are not used show compressed file size if available remember file sizes for files in archives --- src/datatab.cpp | 73 ++--------------------- src/datatab.h | 3 +- src/filetreeitem.cpp | 136 +++++++++++++++++++++++++++++++++++------- src/filetreeitem.h | 59 +++++++++++++++--- src/filetreemodel.cpp | 35 ++++++++--- src/filetreemodel.h | 1 - src/mainwindow.cpp | 2 +- src/shared/directoryentry.cpp | 21 +++++-- src/shared/directoryentry.h | 22 ++++++- 9 files changed, 235 insertions(+), 117 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/datatab.cpp b/src/datatab.cpp index af457b33..6b403f66 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -59,7 +59,7 @@ void DataTab::saveState(Settings& s) const void DataTab::restoreState(const Settings& s) { s.geometry().restoreState(ui.tree->header()); - + // prior to 2.3, the list was not sortable, and this remembered in the // widget state, for whatever reason ui.tree->setSortingEnabled(true); @@ -67,7 +67,7 @@ void DataTab::restoreState(const Settings& s) void DataTab::activated() { - //refreshDataTreeKeepExpandedNodes(); + updateTree(); } void DataTab::onRefresh() @@ -79,76 +79,11 @@ void DataTab::onRefresh() m_core.refreshDirectoryStructure(); } -void DataTab::refreshDataTree() +void DataTab::updateTree() { m_filetree->refresh(); } -void DataTab::refreshDataTreeKeepExpandedNodes() -{ - //m_model->refreshKeepExpandedNodes(); - m_filetree->refresh(); // temp - - /*QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); - QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); - QStringList expandedNodes; - QTreeWidgetItemIterator it1(ui.tree, QTreeWidgetItemIterator::NotHidden | QTreeWidgetItemIterator::HasChildren); - while (*it1) { - QTreeWidgetItem *current = (*it1); - if (current->isExpanded() && !(current->text(0)=="data")) { - expandedNodes.append(current->text(0)+"/"+current->parent()->text(0)); - } - ++it1; - } - - ui.tree->clear(); - QStringList columns("data"); - columns.append(""); - QTreeWidgetItem *subTree = new QTreeWidgetItem(columns); - subTree->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); - updateTo(subTree, L"", *m_core.directoryStructure(), ui.conflicts->isChecked(), &fileIcon, &folderIcon); - ui.tree->insertTopLevelItem(0, subTree); - subTree->setExpanded(true); - QTreeWidgetItemIterator it2(ui.tree, QTreeWidgetItemIterator::HasChildren); - while (*it2) { - QTreeWidgetItem *current = (*it2); - if (!(current->text(0)=="data") && expandedNodes.contains(current->text(0)+"/"+current->parent()->text(0))) { - current->setExpanded(true); - } - ++it2; - }*/ -} - -void DataTab::onItemExpanded(QTreeWidgetItem* item) -{ - /*if ((item->childCount() == 1) && (item->child(0)->data(0, Qt::UserRole).toString() == "__loaded_on_demand__")) { - // read the data we need from the sub-item, then dispose of it - QTreeWidgetItem *onDemandDataItem = item->child(0); - const QString path = onDemandDataItem->data(0, Qt::UserRole + 1).toString(); - std::wstring wspath = path.toStdWString(); - bool conflictsOnly = onDemandDataItem->data(0, Qt::UserRole + 2).toBool(); - - std::wstring virtualPath = (wspath + L"\\").substr(6) + item->text(0).toStdWString(); - DirectoryEntry *dir = m_core.directoryStructure()->findSubDirectoryRecursive(virtualPath); - if (dir != nullptr) { - QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); - QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); - updateTo(item, wspath, *dir, conflictsOnly, &fileIcon, &folderIcon); - } else { - log::warn("failed to update view of {}", path); - } - - m_removeLater.push_back(item); - - QTimer::singleShot(5, [this]{ - for (QTreeWidgetItem *item : m_removeLater) { - item->removeChild(item->child(0)); - } - m_removeLater.clear(); - }); - }*/ -} - void DataTab::onConflicts() { updateOptions(); @@ -172,5 +107,5 @@ void DataTab::updateOptions() } m_filetree->model()->setFlags(flags); - refreshDataTree(); + updateTree(); } diff --git a/src/datatab.h b/src/datatab.h index fd93514c..4545dc5a 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -25,8 +25,7 @@ public: void restoreState(const Settings& s); void activated(); - void refreshDataTreeKeepExpandedNodes(); - void refreshDataTree(); + void updateTree(); signals: void executablesChanged(); diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index a506f0f0..6d42f2dc 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -4,9 +4,38 @@ #include "util.h" #include "modinfodialogfwd.h" #include +#include using namespace MOBase; using namespace MOShared; +namespace fs = std::filesystem; + +const QString& directoryFileType() +{ + static QString name; + + if (name.isEmpty()) { + const DWORD flags = SHGFI_TYPENAME; + SHFILEINFOW sfi = {}; + + // "." for the current directory, which should always exist + const auto r = SHGetFileInfoW(L".", 0, &sfi, sizeof(sfi), flags); + + if (!r) { + const auto e = GetLastError(); + + log::error( + "SHGetFileInfoW failed for folder file type, {}", + formatSystemMessage(e)); + + name = "File folder"; + } else { + name = QString::fromWCharArray(sfi.szTypeName); + } + } + + return name; +} FileTreeItem::FileTreeItem( @@ -16,6 +45,7 @@ FileTreeItem::FileTreeItem( m_parent(parent), m_indexGuess(NoIndexGuess), m_originID(originID), m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), + m_wsRealPath(realPath), m_realPath(QString::fromStdWString(realPath)), m_flags(flags), m_wsFile(file), @@ -94,13 +124,19 @@ public: return naturalCompare(a->m_mod, b->m_mod); case FileTreeModel::FileType: - return naturalCompare(a->meta().type, b->meta().type); + return naturalCompare( + a->fileType().value_or(QString()), + b->fileType().value_or(QString())); case FileTreeModel::FileSize: - return threeWayCompare(a->meta().size, b->meta().size); + return threeWayCompare( + a->fileSize().value_or(0), + b->fileSize().value_or(0)); case FileTreeModel::LastModified: - return threeWayCompare(a->meta().lastModified, b->meta().lastModified); + return threeWayCompare( + a->lastModified().value_or(QDateTime()), + b->lastModified().value_or(QDateTime())); default: return 0; @@ -166,34 +202,88 @@ QFont FileTreeItem::font() const return f; } -const FileTreeItem::Meta& FileTreeItem::meta() const +std::optional FileTreeItem::fileSize() const { - if (!m_meta) { - QFileInfo fi(m_realPath); - - SHFILEINFOW sfi = {}; - const auto r = SHGetFileInfoW( - m_realPath.toStdWString().c_str(), 0, &sfi, sizeof(sfi), - SHGFI_TYPENAME); + if (m_fileSize.empty()) { + std::error_code ec; + const auto size = fs::file_size(fs::path(m_wsRealPath), ec); - if (!r) { - const auto e = GetLastError(); + if (ec) { + log::error("can't get file size for '{}', {}", m_realPath, ec.message()); + m_fileSize.fail(); + } else { + m_fileSize.set(size); + } + } - log::error( - "SHGetFileInfoW failed for '{}', {}", - m_realPath, e); + return m_fileSize.value; +} - sfi = {}; +std::optional FileTreeItem::lastModified() const +{ + if (m_lastModified.empty()) { + if (m_realPath.isEmpty()) { + // this is a virtual directory + m_lastModified.set({}); + } else if (isFromArchive()) { + // can't get last modified date for files in archives + m_lastModified.set({}); + } else { + // looks like a regular file on the filesystem + const QFileInfo fi(m_realPath); + const auto d = fi.lastModified(); + + if (!d.isValid()) { + log::error("can't get last modified date for '{}'", m_realPath); + m_lastModified.fail(); + } else { + m_lastModified.set(d); + } } + } + + return m_lastModified.value; +} - m_meta = { - static_cast(fi.size()), - fi.lastModified(), - QString::fromWCharArray(sfi.szTypeName) - }; +std::optional FileTreeItem::fileType() const +{ + if (m_fileType.empty()) { + getFileType(); } - return *m_meta; + return m_fileType.value; +} + +void FileTreeItem::getFileType() const +{ + if (isDirectory()) { + m_fileType.set(directoryFileType()); + 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)); + + m_fileType.fail(); + } else { + m_fileType.set(QString::fromWCharArray(sfi.szTypeName)); + } } QFileIconProvider::IconType FileTreeItem::icon() const diff --git a/src/filetreeitem.h b/src/filetreeitem.h index fc27b0c0..c5418d43 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -19,13 +19,6 @@ public: Conflicted = 0x04 }; - struct Meta - { - uint64_t size; - QDateTime lastModified; - QString type; - }; - Q_DECLARE_FLAGS(Flags, Flag); @@ -136,7 +129,24 @@ public: QFont font() const; - const Meta& meta() const; + std::optional fileSize() const; + std::optional lastModified() const; + std::optional fileType() const; + + std::optional compressedFileSize() const + { + return m_compressedFileSize; + } + + void setFileSize(uint64_t size) + { + m_fileSize.set(size); + } + + void setCompressedFileSize(uint64_t compressedSize) + { + m_compressedFileSize = compressedSize; + } const QString& realPath() const { @@ -210,6 +220,30 @@ public: QString debugName() const; private: + template + struct Cached + { + std::optional value; + bool failed = false; + + bool empty() const + { + return !failed && !value; + } + + void set(T t) + { + value = std::move(t); + failed = false; + } + + void fail() + { + value = {}; + failed = true; + } + }; + static constexpr std::size_t NoIndexGuess = std::numeric_limits::max(); @@ -218,17 +252,24 @@ private: const int m_originID; const QString m_virtualParentPath; + const std::wstring m_wsRealPath; const QString m_realPath; const Flags m_flags; const std::wstring m_wsFile, m_wsLcFile; const MOShared::DirectoryEntry::FileKey m_key; const QString m_file; const QString m_mod; - mutable std::optional m_meta; + + mutable Cached m_fileSize; + mutable Cached m_lastModified; + mutable Cached m_fileType; + mutable std::optional m_compressedFileSize; bool m_loaded; bool m_expanded; Children m_children; + + void getFileType() const; }; #endif // MODORGANIZER_FILETREEITEM_INCLUDED diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index aa053eeb..cb7e94fa 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -127,7 +127,7 @@ private: FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""), - m_flags(NoFlags), m_isRefreshing(false) + m_flags(NoFlags) { m_root.setExpanded(true); @@ -136,9 +136,6 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : void FileTreeModel::refresh() { - m_isRefreshing = true; - Guard g([&]{ m_isRefreshing = false; }); - TimeThis tt("FileTreeModel::refresh()"); update(m_root, *m_core.directoryStructure(), L""); } @@ -695,6 +692,14 @@ std::unique_ptr FileTreeModel::createFileItem( &parentItem, originID, parentPath, file.getFullPath(), flags, file.getName(), makeModName(file, originID)); + if (file.getFileSize() != FileEntry::NoFileSize) { + item->setFileSize(file.getFileSize()); + } + + if (file.getCompressedFileSize() != FileEntry::NoFileSize) { + item->setCompressedFileSize(file.getCompressedFileSize()); + } + item->setLoaded(true); return item; @@ -716,7 +721,7 @@ QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const case FileType: { - return item->meta().type; + return item->fileType().value_or(QString()); } case FileSize: @@ -724,13 +729,29 @@ QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const if (item->isDirectory()) { return {}; } else { - return localizedByteSize(item->meta().size); + QString fs; + + if (auto n=item->fileSize()) { + fs = localizedByteSize(*n); + } + + if (auto n=item->compressedFileSize()) { + return QString("%1 (%2)").arg(fs).arg(localizedByteSize(*n)); + } else { + return fs; + } } } case LastModified: { - return item->meta().lastModified.toString(Qt::SystemLocaleDate); + if (auto d=item->lastModified()) { + if (d->isValid()) { + return d->toString(Qt::SystemLocaleDate); + } + } + + return {}; } default: diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 38f611f9..0fbcf521 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -81,7 +81,6 @@ private: mutable IconFetcher m_iconFetcher; mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; - bool m_isRefreshing; Sort m_sort; bool showConflicts() const diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c71a877d..bf0dc61f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2364,7 +2364,7 @@ void MainWindow::directory_refreshed() //Some better check for the current tab is needed. if (ui->tabWidget->currentIndex() == 2) { - m_DataTab->refreshDataTreeKeepExpandedNodes(); + m_DataTab->updateTree(); } } diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 4181098c..92b6c2bf 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -167,13 +167,15 @@ private: FileEntry::FileEntry() : m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr), + m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize), m_LastAccessed(time(nullptr)) { } FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) : - m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), - m_Parent(parent), m_LastAccessed(time(nullptr)) + m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent), + m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize), + m_LastAccessed(time(nullptr)) { } @@ -1020,7 +1022,7 @@ void DirectoryEntry::removeFiles(const std::set &indices) removeFilesFromList(indices); } -void DirectoryEntry::insert( +FileEntry::Ptr DirectoryEntry::insert( const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) { @@ -1036,6 +1038,8 @@ void DirectoryEntry::insert( file->addOrigin(origin.getID(), fileTime, archive, order); origin.addFile(file->getIndex()); + + return file; } void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset) @@ -1085,7 +1089,16 @@ void DirectoryEntry::addFiles( // add files for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) { BSA::File::Ptr file = archiveFolder->getFile(fileIdx); - insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order); + + auto f = insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order); + + if (f) { + if (file->getUncompressedFileSize() > 0) { + f->setFileSize(file->getFileSize(), file->getUncompressedFileSize()); + } else { + f->setFileSize(file->getFileSize(), FileEntry::NoFileSize); + } + } } // recurse into subdirectories diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 6102c88f..7e45493d 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -61,6 +61,9 @@ class FileRegister; class FileEntry { public: + static constexpr uint64_t NoFileSize = + std::numeric_limits::max(); + typedef unsigned int Index; typedef boost::shared_ptr Ptr; @@ -150,6 +153,22 @@ public: return m_FileTime; } + void setFileSize(uint64_t size, uint64_t compressedSize) + { + m_FileSize = size; + m_CompressedFileSize = compressedSize; + } + + uint64_t getFileSize() const + { + return m_FileSize; + } + + uint64_t getCompressedFileSize() const + { + return m_CompressedFileSize; + } + private: Index m_Index; std::wstring m_Name; @@ -158,6 +177,7 @@ private: AlternativesVector m_Alternatives; DirectoryEntry *m_Parent; mutable FILETIME m_FileTime; + uint64_t m_FileSize, m_CompressedFileSize; time_t m_LastAccessed; @@ -480,7 +500,7 @@ private: DirectoryEntry(const DirectoryEntry &reference); - void insert( + FileEntry::Ptr insert( const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order); -- cgit v1.3.1 From 47d1826f55c66a039951d922dabab8b01324bebb Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 2 Feb 2020 17:07:51 -0500 Subject: hidden unique_ptr into FileTreeItem refactored pruning don't show shell menu for directories added collapse all in context menu --- src/filetree.cpp | 37 +++++++++++--- src/filetree.h | 2 +- src/filetreeitem.cpp | 12 ++++- src/filetreeitem.h | 15 ++++-- src/filetreemodel.cpp | 137 ++++++++++++++++++++++++++++++-------------------- src/filetreemodel.h | 8 +-- src/shared/util.cpp | 86 +++++++++++++++++++++++++------ src/shared/util.h | 1 + 8 files changed, 214 insertions(+), 84 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetree.cpp b/src/filetree.cpp index a826ed9a..41b10586 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -442,8 +442,11 @@ void FileTree::onContextMenu(const QPoint &pos) const auto m = QApplication::keyboardModifiers(); if (m & Qt::ShiftModifier) { - showShellMenu(pos); - return; + // if no shell menu was available, continue on and show the regular + // context menu + if (showShellMenu(pos)) { + return; + } } QMenu menu; @@ -481,13 +484,14 @@ QMainWindow* getMainWindow(QWidget* w) return nullptr; } -void FileTree::showShellMenu(QPoint pos) +bool FileTree::showShellMenu(QPoint pos) { auto* mw = getMainWindow(m_tree); // menus by origin std::map menus; int totalFiles = 0; + bool hasDirectory = false; for (auto&& index : m_tree->selectionModel()->selectedRows()) { auto* item = m_model->itemFromIndex(index); @@ -495,6 +499,16 @@ void FileTree::showShellMenu(QPoint pos) continue; } + if (item->isDirectory()) { + hasDirectory = true; + + log::warn( + "directories do not have shell menus; '{}' selected", + item->filename()); + + continue; + } + auto itor = menus.find(item->originID()); if (itor == menus.end()) { itor = menus.emplace(item->originID(), mw).first; @@ -543,8 +557,13 @@ void FileTree::showShellMenu(QPoint pos) } if (menus.empty()) { - log::warn("no menus to show"); - return; + // don't warn if a directory was selected, a warning has already been + // logged above + if (!hasDirectory) { + log::warn("no menus to show"); + } + + return false; } else if (menus.size() == 1) { auto& menu = menus.begin()->second; @@ -576,6 +595,8 @@ void FileTree::showShellMenu(QPoint pos) mc.exec(m_tree->viewport()->mapToGlobal(pos)); } + + return true; } void FileTree::addDirectoryMenus(QMenu&, FileTreeItem&) @@ -720,7 +741,11 @@ void FileTree::addCommonMenus(QMenu& menu) .hint(QObject::tr("Refreshes the list")) .addTo(menu); - MenuItem(QObject::tr("E&xpand All")) + MenuItem(QObject::tr("Ex&pand All")) .callback([&]{ m_tree->expandAll(); }) .addTo(menu); + + MenuItem(QObject::tr("&Collapse All")) + .callback([&]{ m_tree->collapseAll(); }) + .addTo(menu); } diff --git a/src/filetree.h b/src/filetree.h index 39c9d0c6..80704f7b 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -51,7 +51,7 @@ private: void onExpandedChanged(const QModelIndex& index, bool expanded); void onContextMenu(const QPoint &pos); - void showShellMenu(QPoint pos); + bool showShellMenu(QPoint pos); void addDirectoryMenus(QMenu& menu, FileTreeItem& item); void addFileMenus(QMenu& menu, const MOShared::FileEntry& file, int originID); diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 6d42f2dc..da4ce701 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -58,7 +58,17 @@ FileTreeItem::FileTreeItem( { } -void FileTreeItem::insert(std::unique_ptr child, std::size_t at) +FileTreeItem::Ptr FileTreeItem::create( + FileTreeItem* parent, int originID, + std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, + std::wstring file, std::wstring mod) +{ + return std::unique_ptr(new FileTreeItem( + parent, originID, std::move(dataRelativeParentPath), std::move(realPath), + flags, std::move(file), std::move(mod))); +} + +void FileTreeItem::insert(FileTreeItem::Ptr child, std::size_t at) { if (at > m_children.size()) { log::error( diff --git a/src/filetreeitem.h b/src/filetreeitem.h index c5418d43..8ef42289 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -9,7 +9,8 @@ class FileTreeItem class Sorter; public: - using Children = std::vector>; + using Ptr = std::unique_ptr; + using Children = std::vector; enum Flag { @@ -22,7 +23,7 @@ public: Q_DECLARE_FLAGS(Flags, Flag); - FileTreeItem( + static Ptr create( FileTreeItem* parent, int originID, std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, std::wstring file, std::wstring mod); @@ -32,13 +33,13 @@ public: FileTreeItem(FileTreeItem&&) = default; FileTreeItem& operator=(FileTreeItem&&) = default; - void add(std::unique_ptr child) + void add(Ptr child) { child->m_indexGuess = m_children.size(); m_children.push_back(std::move(child)); } - void insert(std::unique_ptr child, std::size_t at); + void insert(Ptr child, std::size_t at); template void insert(Itor begin, Itor end, std::size_t at) @@ -269,6 +270,12 @@ private: bool m_expanded; Children m_children; + + FileTreeItem( + FileTreeItem* parent, int originID, + std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, + std::wstring file, std::wstring mod); + void getFileType() const; }; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 91e6198f..3890ad2e 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -65,7 +65,7 @@ public: const auto parentIndex = m_model->indexFromItem(m_parentItem); // make sure the number of items is the same as the size of this range - Q_ASSERT(static_cast(toAdd.size()) == (last - m_first)); + Q_ASSERT(static_cast(toAdd.size()) == (last - m_first + 1)); trace(log::debug("Range::add() {} to {}", m_first, last)); @@ -124,12 +124,24 @@ private: }; +FileTreeItem* getItem(const QModelIndex& index) +{ + return static_cast(index.internalPointer()); +} + +void* makeInternalPointer(FileTreeItem* item) +{ + return item; +} + + FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), - m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""), + m_root(FileTreeItem::create( + nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L"")), m_flags(NoFlags) { - m_root.setExpanded(true); + m_root->setExpanded(true); connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); }); } @@ -137,19 +149,19 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : void FileTreeModel::refresh() { TimeThis tt("FileTreeModel::refresh()"); - update(m_root, *m_core.directoryStructure(), L""); + update(*m_root, *m_core.directoryStructure(), L""); } void FileTreeModel::clear() { beginResetModel(); - m_root.clear(); + m_root->clear(); endResetModel(); } bool FileTreeModel::showArchives() const { - return (m_flags & Archives) && m_core.getArchiveParsing(); + return (m_flags.testFlag(Archives) && m_core.getArchiveParsing()); } QModelIndex FileTreeModel::index( @@ -160,7 +172,7 @@ QModelIndex FileTreeModel::index( return {}; } - return createIndex(row, col, parentItem); + return createIndex(row, col, makeInternalPointer(parentItem)); } log::error("FileTreeModel::index(): parentIndex has no internal pointer"); @@ -173,7 +185,7 @@ QModelIndex FileTreeModel::parent(const QModelIndex& index) const return {}; } - auto* parentItem = static_cast(index.internalPointer()); + auto* parentItem = getItem(index); if (!parentItem) { log::error("FileTreeModel::parent(): no internal pointer"); return {}; @@ -341,7 +353,7 @@ void FileTreeModel::sort(int column, Qt::SortOrder order) oldItems.push_back({itemFromIndex(index), index.column()}); } - m_root.sort(column, order); + m_root->sort(column, order); QModelIndexList newList; newList.reserve(itemCount); @@ -359,10 +371,10 @@ void FileTreeModel::sort(int column, Qt::SortOrder order) FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const { if (!index.isValid()) { - return &m_root; + return m_root.get(); } - auto* parentItem = static_cast(index.internalPointer()); + auto* parentItem = getItem(index); if (!parentItem) { log::error("FileTreeModel::itemFromIndex(): no internal pointer"); return nullptr; @@ -395,7 +407,7 @@ QModelIndex FileTreeModel::indexFromItem(FileTreeItem& item, int col) const return {}; } - return createIndex(index, col, parent); + return createIndex(index, col, makeInternalPointer(parent)); } void FileTreeModel::update( @@ -477,35 +489,22 @@ void FileTreeModel::removeDisappearingDirectories( if (item->areChildrenVisible()) { // the item is currently expanded, update it update(*item, *d, parentPath); - } else if (item->isLoaded()) { - // the item is loaded (previously expanded but now collapsed), mark it - // as unloaded - item->setLoaded(false); } - if ((m_flags & PruneDirectories)) { - // this directory must be checked to see if it's empty so it can be - // pruned - bool prune = false; - - if (item->isLoaded() && item->children().empty()) { - // item is loaded and has no children; prune it - prune = true; - } else { - // item is not loaded, so children have to be checked manually - if (!hasFilesAnywhere(*d)) { - // item wouldn't have any children, prune it - prune = true; - } + if (shouldShowFolder(*d, item.get())) { + // folder should be left in the list + if (!item->areChildrenVisible() && item->isLoaded()) { + // the item is loaded (previously expanded but now collapsed), mark + // it as unloaded so it updates when next expanded + item->setLoaded(false); } + } else { + // item wouldn't have any children, prune it + trace(log::debug("dir {} is empty and pruned", item->filename())); - if (prune) { - trace(log::debug("dir {} is empty and pruned", item->filename())); - - range.includeCurrent(); - currentRemoved = true; - ++itor; - } + range.includeCurrent(); + currentRemoved = true; + ++itor; } if (!currentRemoved) { @@ -536,7 +535,7 @@ bool FileTreeModel::addNewDirectories( // keeps track of the contiguous directories that need to be added to // avoid calling beginAddRows(), etc. for each item Range range(this, parentItem); - std::vector> toAdd; + std::vector toAdd; bool added = false; // for each directory on the filesystem @@ -553,7 +552,7 @@ bool FileTreeModel::addNewDirectories( range.add(std::move(toAdd)); toAdd.clear(); } else { - if ((m_flags & PruneDirectories) && !hasFilesAnywhere(*d)) { + if (!shouldShowFolder(*d, nullptr)) { // this is a new directory, but it doesn't contain anything interesting trace(log::debug("new dir {}, empty and pruned", QString::fromStdWString(d->getName()))); @@ -656,7 +655,7 @@ bool FileTreeModel::addNewFiles( { // keeps track of the contiguous files that need to be added to // avoid calling beginAddRows(), etc. for each item - std::vector> toAdd; + std::vector toAdd; Range range(this, parentItem, firstFileRow); bool added = false; @@ -705,11 +704,11 @@ bool FileTreeModel::addNewFiles( return added; } -std::unique_ptr FileTreeModel::createDirectoryItem( +FileTreeItem::Ptr FileTreeModel::createDirectoryItem( FileTreeItem& parentItem, const std::wstring& parentPath, const DirectoryEntry& d) { - auto item = std::make_unique( + auto item = FileTreeItem::create( &parentItem, 0, parentPath, L"", FileTreeItem::Directory, d.getName(), L""); @@ -722,7 +721,7 @@ std::unique_ptr FileTreeModel::createDirectoryItem( return item; } -std::unique_ptr FileTreeModel::createFileItem( +FileTreeItem::Ptr FileTreeModel::createFileItem( FileTreeItem& parentItem, const std::wstring& parentPath, const FileEntry& file) { @@ -739,7 +738,7 @@ std::unique_ptr FileTreeModel::createFileItem( flags |= FileTreeItem::Conflicted; } - auto item = std::make_unique( + auto item = FileTreeItem::create( &parentItem, originID, parentPath, file.getFullPath(), flags, file.getName(), makeModName(file, originID)); @@ -759,22 +758,54 @@ std::unique_ptr FileTreeModel::createFileItem( bool FileTreeModel::shouldShowFile(const FileEntry& file) const { if (showConflictsOnly() && (file.getAlternatives().size() == 0)) { + // only conflicts should be shown, but this file is not conflicted return false; } - if (!showArchives()) { - bool isArchive = false; - file.getOrigin(isArchive); - return !isArchive; + if (!showArchives() && file.isFromArchive()) { + // files from archives shouldn't be shown, but this file is from an archive + return false; } return true; } -bool FileTreeModel::hasFilesAnywhere(const DirectoryEntry& dir) const +bool FileTreeModel::shouldShowFolder( + const DirectoryEntry& dir, const FileTreeItem* item) const { + bool shouldPrune = m_flags.testFlag(PruneDirectories); + + if (m_core.settings().archiveParsing()) { + if (!m_flags.testFlag(Archives)) { + // archive parsing is enabled but the tree shouldn't show archives; this + // is a bit of a special case for folders because they have to be hidden + // regardless of the PruneDirectories flag if they only exist in archives + // + // note that this test is inaccurate: if a loose folder exists but is + // empty, and the same folder exists in an archive but is _not_ empty, + // then it's considered to exist _only_ in an archive and will be pruned + // + // if directories are ever made first-class so they can retain their + // origins, this test can be made more accurate + shouldPrune = true; + } + } + + if (!shouldPrune) { + // always show folders regardless of their content + return true; + } + + if (item) { + if (item->isLoaded() && item->children().empty()) { + // item is loaded and has no children; prune it + return false; + } + } + bool foundFile = false; + // check all files in this directory, return early if a file should be shown dir.forEachFile([&](auto&& f) { if (shouldShowFile(f)) { foundFile = true; @@ -791,11 +822,9 @@ bool FileTreeModel::hasFilesAnywhere(const DirectoryEntry& dir) const return true; } - std::vector::const_iterator begin, end; - dir.getSubDirectories(begin, end); - - for (auto itor=begin; itor!=end; ++itor) { - if (hasFilesAnywhere(**itor)) { + // recurse into subdirectories + for (auto subdir : dir.getSubDirectories()) { + if (shouldShowFolder(*subdir, nullptr)) { return true; } } diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 23640ac5..03bae602 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -69,7 +69,7 @@ private: using DirectoryIterator = std::vector::const_iterator; OrganizerCore& m_core; - mutable FileTreeItem m_root; + mutable FileTreeItem::Ptr m_root; Flags m_flags; mutable IconFetcher m_iconFetcher; mutable std::vector m_iconPending; @@ -117,11 +117,11 @@ private: const std::unordered_set& seen); - std::unique_ptr createDirectoryItem( + FileTreeItem::Ptr createDirectoryItem( FileTreeItem& parentItem, const std::wstring& parentPath, const MOShared::DirectoryEntry& d); - std::unique_ptr createFileItem( + FileTreeItem::Ptr createFileItem( FileTreeItem& parentItem, const std::wstring& parentPath, const MOShared::FileEntry& file); @@ -134,7 +134,7 @@ private: void removePendingIcons(const QModelIndex& parent, int first, int last); bool shouldShowFile(const MOShared::FileEntry& file) const; - bool hasFilesAnywhere(const MOShared::DirectoryEntry& dir) const; + bool shouldShowFolder(const MOShared::DirectoryEntry& dir, const FileTreeItem* item) const; QString makeTooltip(const FileTreeItem& item) const; QVariant makeIcon(const FileTreeItem& item, const QModelIndex& index) const; diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 009aad70..aa4ad4b3 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -25,6 +25,8 @@ along with Mod Organizer. If not, see . #include #include +using namespace MOBase; + namespace MOShared { @@ -199,7 +201,7 @@ std::wstring GetFileVersionString(const std::wstring &fileName) } } -MOBase::VersionInfo createVersionInfo() +VersionInfo createVersionInfo() { VS_FIXEDFILEINFO version = GetFileVersion(QApplication::applicationFilePath().toStdWString()); @@ -222,25 +224,25 @@ MOBase::VersionInfo createVersionInfo() if (noLetters) { // Default to pre-alpha when release type is unspecified - return MOBase::VersionInfo(version.dwFileVersionMS >> 16, - version.dwFileVersionMS & 0xFFFF, - version.dwFileVersionLS >> 16, - version.dwFileVersionLS & 0xFFFF, - MOBase::VersionInfo::RELEASE_PREALPHA); + return VersionInfo(version.dwFileVersionMS >> 16, + version.dwFileVersionMS & 0xFFFF, + version.dwFileVersionLS >> 16, + version.dwFileVersionLS & 0xFFFF, + VersionInfo::RELEASE_PREALPHA); } else { // Trust the string to make sense - return MOBase::VersionInfo(versionString); + return VersionInfo(versionString); } } else { // Non-pre-release builds just need their version numbers reading - return MOBase::VersionInfo(version.dwFileVersionMS >> 16, - version.dwFileVersionMS & 0xFFFF, - version.dwFileVersionLS >> 16, - version.dwFileVersionLS & 0xFFFF); + return VersionInfo(version.dwFileVersionMS >> 16, + version.dwFileVersionMS & 0xFFFF, + version.dwFileVersionLS >> 16, + version.dwFileVersionLS & 0xFFFF); } } @@ -314,6 +316,62 @@ void SetThisThreadName(const QString& s) } } + +char shortcutChar(const QAction* a) +{ + const auto text = a->text(); + char shortcut = 0; + + for (int i=0; i= (text.size() - 1)) { + log::error("ampersand at the end"); + return 0; + } + + return text[i + 1].toLatin1(); + } + } + + log::error("action {} has no shortcut", text); + return 0; +} + +void checkDuplicateShortcuts(const QMenu& m) +{ + const auto actions = m.actions(); + + for (int i=0; iisSeparator()) { + continue; + } + + const char shortcut1 = shortcutChar(action1); + if (shortcut1 == 0) { + continue; + } + + for (int j=i+1; jisSeparator()) { + continue; + } + + const char shortcut2 = shortcutChar(action2); + + if (shortcut1 == shortcut2) { + log::error( + "duplicate shortcut {} for {} and {}", + shortcut1, action1->text(), action2->text()); + + break; + } + } + } +} + } // namespace MOShared @@ -330,9 +388,9 @@ TimeThis::~TimeThis() const auto d = duration_cast(end - m_start).count(); if (m_what.isEmpty()) { - MOBase::log::debug("{} ms", d); + log::debug("{} ms", d); } else { - MOBase::log::debug("{} {} ms", m_what, d); + log::debug("{} {} ms", m_what, d); } } @@ -358,7 +416,7 @@ bool ExitModOrganizer(ExitFlags e) } g_exiting = true; - MOBase::Guard g([&]{ g_exiting = false; }); + Guard g([&]{ g_exiting = false; }); if (!e.testFlag(Exit::Force)) { if (auto* mw=findMainWindow()) { diff --git a/src/shared/util.h b/src/shared/util.h index 79cadf71..05522c2d 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -48,6 +48,7 @@ MOBase::VersionInfo createVersionInfo(); QString getUsvfsVersionString(); void SetThisThreadName(const QString& s); +void checkDuplicateShortcuts(const QMenu& m); } // namespace MOShared -- cgit v1.3.1 From d14488d2f750a52f85519ca15d087f35332a784f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 4 Feb 2020 03:12:19 -0500 Subject: update tree items when origins change --- src/filetreeitem.cpp | 48 +++++++++++++++++++++++++------------ src/filetreeitem.h | 65 +++++++++++++++++++++++++++++++++++---------------- src/filetreemodel.cpp | 39 ++++++++++++++++++++----------- src/filetreemodel.h | 2 ++ 4 files changed, 105 insertions(+), 49 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index da4ce701..30805d8b 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -39,33 +39,51 @@ const QString& directoryFileType() FileTreeItem::FileTreeItem( - FileTreeItem* parent, int originID, - std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, - std::wstring file, std::wstring mod) : + FileTreeItem* parent, + std::wstring dataRelativeParentPath, bool isDirectory, std::wstring file) : m_parent(parent), m_indexGuess(NoIndexGuess), - m_originID(originID), m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), - m_wsRealPath(realPath), - m_realPath(QString::fromStdWString(realPath)), - m_flags(flags), m_wsFile(file), m_wsLcFile(ToLowerCopy(file)), m_key(m_wsLcFile), m_file(QString::fromStdWString(file)), - m_mod(QString::fromStdWString(mod)), + m_isDirectory(isDirectory), + m_originID(-1), + m_flags(NoFlags), m_loaded(false), m_expanded(false) { } -FileTreeItem::Ptr FileTreeItem::create( - FileTreeItem* parent, int originID, - std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, - std::wstring file, std::wstring mod) +FileTreeItem::Ptr FileTreeItem::createFile( + FileTreeItem* parent, std::wstring dataRelativeParentPath, std::wstring file) { return std::unique_ptr(new FileTreeItem( - parent, originID, std::move(dataRelativeParentPath), std::move(realPath), - flags, std::move(file), std::move(mod))); + parent, std::move(dataRelativeParentPath), false, std::move(file))); +} + +FileTreeItem::Ptr FileTreeItem::createDirectory( + FileTreeItem* parent, + std::wstring dataRelativeParentPath, std::wstring file) +{ + return std::unique_ptr(new FileTreeItem( + parent, std::move(dataRelativeParentPath), true, std::move(file))); +} + +void FileTreeItem::setOrigin( + int originID, const std::wstring& realPath, Flags flags, + const std::wstring& mod) +{ + m_originID = originID; + m_wsRealPath = realPath; + m_realPath = QString::fromStdWString(realPath); + m_flags = flags; + m_mod = QString::fromStdWString(mod); + + m_fileSize.reset(); + m_lastModified.reset(); + m_fileType.reset(); + m_compressedFileSize.reset(); } void FileTreeItem::insert(FileTreeItem::Ptr child, std::size_t at) @@ -298,7 +316,7 @@ void FileTreeItem::getFileType() const QFileIconProvider::IconType FileTreeItem::icon() const { - if (m_flags & Directory) { + if (m_isDirectory) { return QFileIconProvider::Folder; } else { return QFileIconProvider::File; diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 8ef42289..0d92b3f7 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -15,24 +15,30 @@ public: enum Flag { NoFlags = 0x00, - Directory = 0x01, - FromArchive = 0x02, - Conflicted = 0x04 + FromArchive = 0x01, + Conflicted = 0x02 }; Q_DECLARE_FLAGS(Flags, Flag); - static Ptr create( - FileTreeItem* parent, int originID, - std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, - std::wstring file, std::wstring mod); + static Ptr createFile( + FileTreeItem* parent, + std::wstring dataRelativeParentPath, std::wstring file); + + static Ptr createDirectory( + FileTreeItem* parent, + std::wstring dataRelativeParentPath, std::wstring file); FileTreeItem(const FileTreeItem&) = delete; FileTreeItem& operator=(const FileTreeItem&) = delete; FileTreeItem(FileTreeItem&&) = default; FileTreeItem& operator=(FileTreeItem&&) = default; + void setOrigin( + int originID, const std::wstring& realPath, + Flags flags, const std::wstring& mod); + void add(Ptr child) { child->m_indexGuess = m_children.size(); @@ -136,17 +142,17 @@ public: std::optional compressedFileSize() const { - return m_compressedFileSize; + return m_compressedFileSize.value; } void setFileSize(uint64_t size) { - m_fileSize.set(size); + m_fileSize.override(size); } void setCompressedFileSize(uint64_t compressedSize) { - m_compressedFileSize = compressedSize; + m_compressedFileSize.override(compressedSize); } const QString& realPath() const @@ -165,7 +171,7 @@ public: bool isDirectory() const { - return (m_flags & Directory); + return m_isDirectory; } bool isFromArchive() const @@ -226,6 +232,7 @@ private: { std::optional value; bool failed = false; + bool overridden = false; bool empty() const { @@ -236,12 +243,29 @@ private: { value = std::move(t); failed = false; + overridden = false; + } + + void override(T t) + { + value = std::move(t); + failed = false; + overridden = true; } void fail() { value = {}; failed = true; + overridden = false; + } + + void reset() + { + if (!overridden) { + value = {}; + failed = false; + } } }; @@ -251,20 +275,22 @@ private: FileTreeItem* m_parent; mutable std::size_t m_indexGuess; - const int m_originID; const QString m_virtualParentPath; - const std::wstring m_wsRealPath; - const QString m_realPath; - const Flags m_flags; const std::wstring m_wsFile, m_wsLcFile; const MOShared::DirectoryEntry::FileKey m_key; const QString m_file; - const QString m_mod; + const bool m_isDirectory; + + int m_originID; + QString m_realPath; + std::wstring m_wsRealPath; + Flags m_flags; + QString m_mod; mutable Cached m_fileSize; mutable Cached m_lastModified; mutable Cached m_fileType; - mutable std::optional m_compressedFileSize; + mutable Cached m_compressedFileSize; bool m_loaded; bool m_expanded; @@ -272,9 +298,8 @@ private: FileTreeItem( - FileTreeItem* parent, int originID, - std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, - std::wstring file, std::wstring mod); + FileTreeItem* parent, + std::wstring dataRelativeParentPath, bool isDirectory, std::wstring file); void getFileType() const; }; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 3890ad2e..aa87edbf 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -137,8 +137,7 @@ void* makeInternalPointer(FileTreeItem* item) FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), - m_root(FileTreeItem::create( - nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L"")), + m_root(FileTreeItem::createDirectory(nullptr, L"", L"")), m_flags(NoFlags) { m_root->setExpanded(true); @@ -623,6 +622,11 @@ void FileTreeModel::removeDisappearingFiles( // file is still there seen.emplace(f->getIndex()); + if (f->getOrigin() != item->originID()) { + // origin has changed + updateFileItem(*item, *f); + } + // if there were files before this row that need to be removed, // do it now itor = range.remove(); @@ -708,9 +712,8 @@ FileTreeItem::Ptr FileTreeModel::createDirectoryItem( FileTreeItem& parentItem, const std::wstring& parentPath, const DirectoryEntry& d) { - auto item = FileTreeItem::create( - &parentItem, 0, parentPath, L"", FileTreeItem::Directory, - d.getName(), L""); + auto item = FileTreeItem::createDirectory( + &parentItem, parentPath, d.getName()); if (d.isEmpty()) { // if this directory is empty, mark the item as loaded so the expand @@ -724,6 +727,19 @@ FileTreeItem::Ptr FileTreeModel::createDirectoryItem( FileTreeItem::Ptr FileTreeModel::createFileItem( FileTreeItem& parentItem, const std::wstring& parentPath, const FileEntry& file) +{ + auto item = FileTreeItem::createFile( + &parentItem, parentPath, file.getName()); + + updateFileItem(*item, file); + + item->setLoaded(true); + + return item; +} + +void FileTreeModel::updateFileItem( + FileTreeItem& item, const MOShared::FileEntry& file) { bool isArchive = false; int originID = file.getOrigin(isArchive); @@ -738,21 +754,16 @@ FileTreeItem::Ptr FileTreeModel::createFileItem( flags |= FileTreeItem::Conflicted; } - auto item = FileTreeItem::create( - &parentItem, originID, parentPath, file.getFullPath(), flags, - file.getName(), makeModName(file, originID)); + item.setOrigin( + originID, file.getFullPath(), flags, makeModName(file, originID)); if (file.getFileSize() != FileEntry::NoFileSize) { - item->setFileSize(file.getFileSize()); + item.setFileSize(file.getFileSize()); } if (file.getCompressedFileSize() != FileEntry::NoFileSize) { - item->setCompressedFileSize(file.getCompressedFileSize()); + item.setCompressedFileSize(file.getCompressedFileSize()); } - - item->setLoaded(true); - - return item; } bool FileTreeModel::shouldShowFile(const FileEntry& file) const diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 03bae602..ffd46fac 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -125,6 +125,8 @@ private: FileTreeItem& parentItem, const std::wstring& parentPath, const MOShared::FileEntry& file); + void updateFileItem(FileTreeItem& item, const MOShared::FileEntry& file); + QVariant displayData(const FileTreeItem* item, int column) const; std::wstring makeModName(const MOShared::FileEntry& file, int originID) const; -- cgit v1.3.1 From 6f107023498cb00f268f55232e5f16254df9e79b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 4 Feb 2020 03:36:34 -0500 Subject: case insensitive checks for mohidden extension (lost in rebase) --- src/filetree.cpp | 2 +- src/filetreeitem.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetree.cpp b/src/filetree.cpp index 5831e75e..bf3a9a7a 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -27,7 +27,7 @@ bool canOpenFile(const FileEntry& file) bool isHidden(const FileEntry& file) { - return (QString::fromStdWString(file.getName()).endsWith(ModInfo::s_HiddenExt)); + return (QString::fromStdWString(file.getName()).endsWith(ModInfo::s_HiddenExt, Qt::CaseInsensitive)); } bool canExploreFile(const FileEntry& file); diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 30805d8b..bb07568a 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -325,7 +325,7 @@ QFileIconProvider::IconType FileTreeItem::icon() const bool FileTreeItem::isHidden() const { - return m_file.endsWith(ModInfo::s_HiddenExt); + return m_file.endsWith(ModInfo::s_HiddenExt, Qt::CaseInsensitive); } void FileTreeItem::unload() -- cgit v1.3.1 From 34b022a2297b869c7be306e3f6bf8e124adaf1a7 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:21:41 -0500 Subject: fixed warning when comparing empty strings filter in data tab fixed some bad iterators when removing rows fixed empty directories not being marked as such when refreshing always sort directories first even when reversing the sort order fixed children not being sorted fixed errors about file sizes for directories remember state of checkboxes in data tab --- src/datatab.cpp | 28 ++++++++++++++++++-- src/datatab.h | 2 ++ src/filetree.cpp | 24 ++++++++++++++--- src/filetree.h | 3 +++ src/filetreeitem.cpp | 20 ++++++++++++--- src/filetreemodel.cpp | 71 ++++++++++++++++++++++++++++++++++++--------------- src/filetreemodel.h | 3 +++ src/mainwindow.cpp | 22 +++++++--------- src/mainwindow.h | 2 -- src/mainwindow.ui | 13 +++++++--- src/modinfodialog.cpp | 12 +++++++++ 11 files changed, 152 insertions(+), 48 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/datatab.cpp b/src/datatab.cpp index 3923ba4e..74fa35ce 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -21,10 +21,21 @@ DataTab::DataTab( QWidget* parent, Ui::MainWindow* mwui) : m_core(core), m_pluginContainer(pc), m_parent(parent), ui{ - mwui->btnRefreshData, mwui->dataTree, - mwui->conflictsCheckBox, mwui->showArchiveDataCheckBox} + mwui->dataTabRefresh, mwui->dataTree, + mwui->dataTabShowOnlyConflicts, mwui->dataTabShowFromArchives} { m_filetree.reset(new FileTree(core, m_pluginContainer, ui.tree)); + m_filter.setUseSourceSort(true); + m_filter.setEdit(mwui->dataTabFilter); + m_filter.setList(mwui->dataTree); + + if (auto* m=m_filter.proxyModel()) { + m->setDynamicSortFilter(false); + } + + connect( + &m_filter, &FilterWidget::aboutToChange, + [&]{ m_filetree->ensureFullyLoaded(); }); connect( ui.refresh, &QPushButton::clicked, @@ -54,6 +65,8 @@ DataTab::DataTab( void DataTab::saveState(Settings& s) const { s.geometry().saveState(ui.tree->header()); + s.widgets().saveChecked(ui.conflicts); + s.widgets().saveChecked(ui.archives); } void DataTab::restoreState(const Settings& s) @@ -63,6 +76,9 @@ void DataTab::restoreState(const Settings& s) // prior to 2.3, the list was not sortable, and this remembered in the // widget state, for whatever reason ui.tree->setSortingEnabled(true); + + s.widgets().restoreChecked(ui.conflicts); + s.widgets().restoreChecked(ui.archives); } void DataTab::activated() @@ -82,6 +98,14 @@ void DataTab::onRefresh() void DataTab::updateTree() { m_filetree->refresh(); + + if (!m_filter.empty()) { + m_filetree->ensureFullyLoaded(); + + if (auto* m=m_filter.proxyModel()) { + m->invalidate(); + } + } } void DataTab::onConflicts() diff --git a/src/datatab.h b/src/datatab.h index 4545dc5a..7e8eb2b9 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -1,5 +1,6 @@ #include "modinfodialogfwd.h" #include "modinfo.h" +#include #include #include #include @@ -47,6 +48,7 @@ private: DataTabUi ui; std::unique_ptr m_filetree; std::vector m_removeLater; + MOBase::FilterWidget m_filter; void onRefresh(); void onItemExpanded(QTreeWidgetItem* item); diff --git a/src/filetree.cpp b/src/filetree.cpp index bf3a9a7a..ae1fddfe 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -150,11 +150,16 @@ void FileTree::clear() m_model->clear(); } +void FileTree::ensureFullyLoaded() +{ + m_model->ensureFullyLoaded(); +} + FileTreeItem* FileTree::singleSelection() { const auto sel = m_tree->selectionModel()->selectedRows(); if (sel.size() == 1) { - return m_model->itemFromIndex(sel[0]); + return m_model->itemFromIndex(proxiedIndex(sel[0])); } return nullptr; @@ -357,7 +362,7 @@ void FileTree::dumpToFile() const QString file = QFileDialog::getSaveFileName(m_tree->window()); if (file.isEmpty()) { - log::debug("user cancelled"); + log::debug("user canceled"); return; } @@ -432,7 +437,7 @@ void FileTree::dumpToFile( void FileTree::onExpandedChanged(const QModelIndex& index, bool expanded) { - if (auto* item=m_model->itemFromIndex(index)) { + if (auto* item=m_model->itemFromIndex(proxiedIndex(index))) { item->setExpanded(expanded); } } @@ -494,7 +499,7 @@ bool FileTree::showShellMenu(QPoint pos) bool warnOnEmpty = true; for (auto&& index : m_tree->selectionModel()->selectedRows()) { - auto* item = m_model->itemFromIndex(index); + auto* item = m_model->itemFromIndex(proxiedIndex(index)); if (!item) { continue; } @@ -759,3 +764,14 @@ void FileTree::addCommonMenus(QMenu& menu) .callback([&]{ m_tree->collapseAll(); }) .addTo(menu); } + +QModelIndex FileTree::proxiedIndex(const QModelIndex& index) +{ + auto* model = m_tree->model(); + + if (auto* proxy=dynamic_cast(model)) { + return proxy->mapToSource(index); + } else { + return index; + } +} diff --git a/src/filetree.h b/src/filetree.h index 80704f7b..855a1751 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -21,6 +21,7 @@ public: FileTreeModel* model(); void refresh(); void clear(); + void ensureFullyLoaded(); void open(); void openHooked(); @@ -60,6 +61,8 @@ private: void toggleVisibility(bool b); + QModelIndex proxiedIndex(const QModelIndex& index); + void dumpToFile( QFile& out, const QString& parentPath, const MOShared::DirectoryEntry& entry) const; diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index bb07568a..3332fb7d 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -10,6 +10,8 @@ using namespace MOBase; using namespace MOShared; namespace fs = std::filesystem; +constexpr bool AlwaysSortDirectoriesFirst = true; + const QString& directoryFileType() { static QString name; @@ -179,9 +181,17 @@ void FileTreeItem::sort(int column, Qt::SortOrder order) int r = 0; if (a->isDirectory() && !b->isDirectory()) { - r = -1; + if constexpr (AlwaysSortDirectoriesFirst) { + return true; + } else { + r = -1; + } } else if (!a->isDirectory() && b->isDirectory()) { - r = 1; + if constexpr (AlwaysSortDirectoriesFirst) { + return false; + } else { + r = 1; + } } else { r = FileTreeItem::Sorter::compare(column, a.get(), b.get()); } @@ -192,6 +202,10 @@ void FileTreeItem::sort(int column, Qt::SortOrder order) return (r > 0); } }); + + for (auto& child : m_children) { + child->sort(column, order); + } } QString FileTreeItem::virtualPath() const @@ -232,7 +246,7 @@ QFont FileTreeItem::font() const std::optional FileTreeItem::fileSize() const { - if (m_fileSize.empty()) { + if (m_fileSize.empty() && !m_isDirectory) { std::error_code ec; const auto size = fs::file_size(fs::path(m_wsRealPath), ec); diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index aa87edbf..912e2007 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -87,31 +87,32 @@ public: // FileTreeItem::Children::const_iterator remove() { - if (m_first == -1) { - // nothing to remove - return m_parentItem.children().begin() + m_current + 1; - } + if (m_first >= 0) { + const auto last = m_current - 1; + const auto parentIndex = m_model->indexFromItem(m_parentItem); - const auto last = m_current - 1; - const auto parentIndex = m_model->indexFromItem(m_parentItem); + trace(log::debug("Range::remove() {} to {}", m_first, last)); - trace(log::debug("Range::remove() {} to {}", m_first, last)); + m_model->beginRemoveRows(parentIndex, m_first, last); - m_model->beginRemoveRows(parentIndex, m_first, last); + m_parentItem.remove( + static_cast(m_first), + static_cast(last - m_first + 1)); - m_parentItem.remove( - static_cast(m_first), - static_cast(last - m_first + 1)); + m_model->endRemoveRows(); - m_model->endRemoveRows(); + m_model->removePendingIcons(parentIndex, m_first, last); - m_model->removePendingIcons(parentIndex, m_first, last); + // adjust current row to account for those that were just removed + m_current -= (m_current - m_first); - // adjust current row to account for those that were just removed - m_current -= (m_current - m_first); + // reset + m_first = -1; + } - // reset - m_first = -1; + if (m_current >= m_parentItem.children().size()) { + return m_parentItem.children().end(); + } return m_parentItem.children().begin() + m_current + 1; } @@ -138,7 +139,7 @@ void* makeInternalPointer(FileTreeItem* item) FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_root(FileTreeItem::createDirectory(nullptr, L"", L"")), - m_flags(NoFlags) + m_flags(NoFlags), m_fullyLoaded(false) { m_root->setExpanded(true); @@ -148,16 +149,40 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : void FileTreeModel::refresh() { TimeThis tt("FileTreeModel::refresh()"); + + m_fullyLoaded = false; update(*m_root, *m_core.directoryStructure(), L""); } void FileTreeModel::clear() { + m_fullyLoaded = false; + beginResetModel(); m_root->clear(); endResetModel(); } +void FileTreeModel::recursiveFetchMore(const QModelIndex& m) +{ + if (canFetchMore(m)) { + fetchMore(m); + } + + for (int i=0; iareChildrenVisible() && item->isLoaded()) { - // the item is loaded (previously expanded but now collapsed), mark - // it as unloaded so it updates when next expanded - item->setLoaded(false); + if (!d->isEmpty()) { + // the item is loaded (previously expanded but now collapsed) and + // has children, mark it as unloaded so it updates when next + // expanded + item->setLoaded(false); + } } } else { // item wouldn't have any children, prune it @@ -694,6 +722,7 @@ bool FileTreeModel::addNewFiles( } else { // this is a new file, but it shouldn't be shown trace(log::debug("new file {}, not shown", QString::fromStdWString(file->getName()))); + return true; } } diff --git a/src/filetreemodel.h b/src/filetreemodel.h index ffd46fac..3e846a0f 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -43,6 +43,7 @@ public: void refresh(); void clear(); + void ensureFullyLoaded(); QModelIndex index(int row, int col, const QModelIndex& parent={}) const override; QModelIndex parent(const QModelIndex& index) const override; @@ -75,6 +76,7 @@ private: mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; Sort m_sort; + bool m_fullyLoaded; bool showConflictsOnly() const { @@ -141,6 +143,7 @@ private: QVariant makeIcon(const FileTreeItem& item, const QModelIndex& index) const; QModelIndex indexFromItem(FileTreeItem& item, int col=0) const; + void recursiveFetchMore(const QModelIndex& m); }; Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeModel::Flags); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index bf0dc61f..439775f8 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -455,15 +455,13 @@ MainWindow::MainWindow(Settings &settings if (m_OrganizerCore.getArchiveParsing()) { - ui->showArchiveDataCheckBox->setCheckState(Qt::Checked); - ui->showArchiveDataCheckBox->setEnabled(true); - m_showArchiveData = true; + ui->dataTabShowFromArchives->setCheckState(Qt::Checked); + ui->dataTabShowFromArchives->setEnabled(true); } else { - ui->showArchiveDataCheckBox->setCheckState(Qt::Unchecked); - ui->showArchiveDataCheckBox->setEnabled(false); - m_showArchiveData = false; + ui->dataTabShowFromArchives->setCheckState(Qt::Unchecked); + ui->dataTabShowFromArchives->setEnabled(false); } QApplication::instance()->installEventFilter(this); @@ -1193,7 +1191,7 @@ void MainWindow::downloadFilterChanged(const QString &filter) void MainWindow::expandModList(const QModelIndex &index) { QAbstractItemModel *model = ui->modList->model(); -#pragma message("why is this so complicated? mapping the index doesn't work, probably a bug in QtGroupingProxy?") + for (int i = 0; i < model->rowCount(); ++i) { QModelIndex targetIdx = model->index(i, 0); if (model->data(targetIdx).toString() == index.data().toString()) { @@ -4949,15 +4947,13 @@ void MainWindow::on_actionSettings_triggered() m_OrganizerCore.setArchiveParsing(state); if (!state) { - ui->showArchiveDataCheckBox->setCheckState(Qt::Unchecked); - ui->showArchiveDataCheckBox->setEnabled(false); - m_showArchiveData = false; + ui->dataTabShowFromArchives->setCheckState(Qt::Unchecked); + ui->dataTabShowFromArchives->setEnabled(false); } else { - ui->showArchiveDataCheckBox->setCheckState(Qt::Checked); - ui->showArchiveDataCheckBox->setEnabled(true); - m_showArchiveData = true; + ui->dataTabShowFromArchives->setCheckState(Qt::Checked); + ui->dataTabShowFromArchives->setEnabled(true); } m_OrganizerCore.refreshModList(); m_OrganizerCore.refreshDirectoryStructure(); diff --git a/src/mainwindow.h b/src/mainwindow.h index d50705e3..608bfa64 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -349,8 +349,6 @@ private: bool m_DidUpdateMasterList; - bool m_showArchiveData{ true }; - MOBase::DelayedFileWriter m_ArchiveListWriter; QAction* m_LinkToolbar; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index f0887f56..8bd22ff1 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1058,7 +1058,7 @@ p, li { white-space: pre-wrap; } - + refresh data-directory overview @@ -1088,7 +1088,7 @@ p, li { white-space: pre-wrap; } - + Filters the above list so that only conflicts are displayed. @@ -1101,7 +1101,7 @@ p, li { white-space: pre-wrap; } - + Filters the above list so that files from archives are not shown @@ -1147,6 +1147,13 @@ p, li { white-space: pre-wrap; } + + + + + + + diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index b0a4248e..fb093529 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -47,6 +47,18 @@ int naturalCompare(const QString& a, const QString& b) return c; }(); + + // todo: remove this once the fix is released + // see https://bugreports.qt.io/projects/QTBUG/issues/QTBUG-81673 + // and https://codereview.qt-project.org/c/qt/qtbase/+/287966/5/src/corelib/text/qcollator_win.cpp + if (!a.size()) { + return b.size() ? -1 : 0; + } + if (!b.size()) { + return +1; + } + + return c.compare(a, b); } -- cgit v1.3.1 From 9c2fc694051b8e500a3343322dd45cf8be1c1b7d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 7 Feb 2020 11:26:11 -0500 Subject: sort on demand --- src/filetreeitem.cpp | 30 +++++++++++++++++++++++------- src/filetreeitem.h | 15 ++++++++++++--- src/filetreemodel.cpp | 11 ++++++++--- src/filetreemodel.h | 17 ++++++++++------- 4 files changed, 53 insertions(+), 20 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 3332fb7d..8602ea6d 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -41,9 +41,9 @@ const QString& directoryFileType() FileTreeItem::FileTreeItem( - FileTreeItem* parent, + FileTreeModel* model, FileTreeItem* parent, std::wstring dataRelativeParentPath, bool isDirectory, std::wstring file) : - m_parent(parent), m_indexGuess(NoIndexGuess), + m_model(model), m_parent(parent), m_indexGuess(NoIndexGuess), m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), m_wsFile(file), m_wsLcFile(ToLowerCopy(file)), @@ -53,23 +53,25 @@ FileTreeItem::FileTreeItem( m_originID(-1), m_flags(NoFlags), m_loaded(false), - m_expanded(false) + m_expanded(false), + m_sortingStale(true) { } FileTreeItem::Ptr FileTreeItem::createFile( - FileTreeItem* parent, std::wstring dataRelativeParentPath, std::wstring file) + FileTreeModel* model, FileTreeItem* parent, + std::wstring dataRelativeParentPath, std::wstring file) { return std::unique_ptr(new FileTreeItem( - parent, std::move(dataRelativeParentPath), false, std::move(file))); + model, parent, std::move(dataRelativeParentPath), false, std::move(file))); } FileTreeItem::Ptr FileTreeItem::createDirectory( - FileTreeItem* parent, + FileTreeModel* model, FileTreeItem* parent, std::wstring dataRelativeParentPath, std::wstring file) { return std::unique_ptr(new FileTreeItem( - parent, std::move(dataRelativeParentPath), true, std::move(file))); + model, parent, std::move(dataRelativeParentPath), true, std::move(file))); } void FileTreeItem::setOrigin( @@ -174,9 +176,23 @@ public: } }; +void FileTreeItem::sort() +{ + sort(m_model->sortInfo().column, m_model->sortInfo().order); +} void FileTreeItem::sort(int column, Qt::SortOrder order) { + if (!m_expanded) { + m_sortingStale = true; + return; + } + + if (m_sortingStale) { + //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; diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 0d92b3f7..375a05c4 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -4,6 +4,8 @@ #include "directoryentry.h" #include +class FileTreeModel; + class FileTreeItem { class Sorter; @@ -23,11 +25,11 @@ public: Q_DECLARE_FLAGS(Flags, Flag); static Ptr createFile( - FileTreeItem* parent, + FileTreeModel* model, FileTreeItem* parent, std::wstring dataRelativeParentPath, std::wstring file); static Ptr createDirectory( - FileTreeItem* parent, + FileTreeModel* model, FileTreeItem* parent, std::wstring dataRelativeParentPath, std::wstring file); FileTreeItem(const FileTreeItem&) = delete; @@ -215,6 +217,10 @@ public: void setExpanded(bool b) { m_expanded = b; + + if (m_sortingStale) { + sort(); + } } bool isStrictlyExpanded() const @@ -272,6 +278,7 @@ private: static constexpr std::size_t NoIndexGuess = std::numeric_limits::max(); + FileTreeModel* m_model; FileTreeItem* m_parent; mutable std::size_t m_indexGuess; @@ -294,14 +301,16 @@ private: bool m_loaded; bool m_expanded; + bool m_sortingStale; Children m_children; FileTreeItem( - FileTreeItem* parent, + FileTreeModel* model, FileTreeItem* parent, std::wstring dataRelativeParentPath, bool isDirectory, std::wstring file); void getFileType() const; + void sort(); }; #endif // MODORGANIZER_FILETREEITEM_INCLUDED diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 169d0a02..01a10afc 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -138,7 +138,7 @@ void* makeInternalPointer(FileTreeItem* item) FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_enabled(true), - m_root(FileTreeItem::createDirectory(nullptr, L"", L"")), + m_root(FileTreeItem::createDirectory(this, nullptr, L"", L"")), m_flags(NoFlags), m_fullyLoaded(false) { m_root->setExpanded(true); @@ -193,6 +193,11 @@ void FileTreeModel::setEnabled(bool b) m_enabled = b; } +const FileTreeModel::SortInfo& FileTreeModel::sortInfo() const +{ + return m_sort; +} + bool FileTreeModel::showArchives() const { return (m_flags.testFlag(Archives) && m_core.getArchiveParsing()); @@ -756,7 +761,7 @@ FileTreeItem::Ptr FileTreeModel::createDirectoryItem( const DirectoryEntry& d) { auto item = FileTreeItem::createDirectory( - &parentItem, parentPath, d.getName()); + this, &parentItem, parentPath, d.getName()); if (d.isEmpty()) { // if this directory is empty, mark the item as loaded so the expand @@ -772,7 +777,7 @@ FileTreeItem::Ptr FileTreeModel::createFileItem( const FileEntry& file) { auto item = FileTreeItem::createFile( - &parentItem, parentPath, file.getName()); + this, &parentItem, parentPath, file.getName()); updateFileItem(*item, file); diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 1bc7f73b..deb6d092 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -34,6 +34,13 @@ public: Q_DECLARE_FLAGS(Flags, Flag); + struct SortInfo + { + int column = 0; + Qt::SortOrder order = Qt::AscendingOrder; + }; + + FileTreeModel(OrganizerCore& core, QObject* parent=nullptr); void setFlags(Flags f) @@ -48,6 +55,8 @@ public: bool enabled() const; void setEnabled(bool b); + const SortInfo& sortInfo() const; + QModelIndex index(int row, int col, const QModelIndex& parent={}) const override; QModelIndex parent(const QModelIndex& index) const override; int rowCount(const QModelIndex& parent={}) const override; @@ -63,12 +72,6 @@ public: FileTreeItem* itemFromIndex(const QModelIndex& index) const; private: - struct Sort - { - int column = 0; - Qt::SortOrder order = Qt::AscendingOrder; - }; - class Range; using DirectoryIterator = std::vector::const_iterator; @@ -80,7 +83,7 @@ private: mutable IconFetcher m_iconFetcher; mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; - Sort m_sort; + SortInfo m_sort; bool m_fullyLoaded; bool showConflictsOnly() const -- cgit v1.3.1 From 2b0a720863a26faebdc240f4db29dd39c7a035b6 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 7 Feb 2020 13:39:35 -0500 Subject: fixed sorting not emitting layout events and fixing persistent indexes --- src/filetreeitem.cpp | 2 +- src/filetreeitem.h | 6 +++++- src/filetreemodel.cpp | 16 +++++++++++----- src/filetreemodel.h | 1 + 4 files changed, 18 insertions(+), 7 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 8602ea6d..9eff9564 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -178,7 +178,7 @@ public: void FileTreeItem::sort() { - sort(m_model->sortInfo().column, m_model->sortInfo().order); + m_model->sortItem(*this); } void FileTreeItem::sort(int column, Qt::SortOrder order) diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 375a05c4..e173a849 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -216,9 +216,13 @@ public: void setExpanded(bool b) { + if (m_expanded == b) { + return; + } + m_expanded = b; - if (m_sortingStale) { + if (m_expanded && m_sortingStale) { sort(); } } diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 01a10afc..40cded09 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -208,6 +208,7 @@ QModelIndex FileTreeModel::index( { if (auto* parentItem=itemFromIndex(parentIndex)) { if (row < 0 || row >= parentItem->children().size()) { + log::error("row {} out of range for {}", row, parentItem->debugName()); return {}; } @@ -378,13 +379,10 @@ Qt::ItemFlags FileTreeModel::flags(const QModelIndex& index) const return f; } -void FileTreeModel::sort(int column, Qt::SortOrder order) +void FileTreeModel::sortItem(FileTreeItem& item) { emit layoutAboutToBeChanged(); - m_sort.column = column; - m_sort.order = order; - const auto oldList = persistentIndexList(); std::vector> oldItems; @@ -396,7 +394,7 @@ void FileTreeModel::sort(int column, Qt::SortOrder order) oldItems.push_back({itemFromIndex(index), index.column()}); } - m_root->sort(column, order); + item.sort(m_sort.column, m_sort.order); QModelIndexList newList; newList.reserve(itemCount); @@ -411,6 +409,14 @@ void FileTreeModel::sort(int column, Qt::SortOrder order) emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); } +void FileTreeModel::sort(int column, Qt::SortOrder order) +{ + m_sort.column = column; + m_sort.order = order; + + sortItem(*m_root); +} + FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const { if (!index.isValid()) { diff --git a/src/filetreemodel.h b/src/filetreemodel.h index deb6d092..dfee597f 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -70,6 +70,7 @@ public: void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override; FileTreeItem* itemFromIndex(const QModelIndex& index) const; + void sortItem(FileTreeItem& item); private: class Range; -- cgit v1.3.1 From 3a80685189967fbf4cfa002ac2b8a4fa421306ca Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 8 Feb 2020 21:38:01 -0500 Subject: don't sort items when they're not expanded --- src/filetreeitem.cpp | 10 ++++++---- src/filetreeitem.h | 2 +- src/filetreemodel.cpp | 8 ++++---- src/filetreemodel.h | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) (limited to 'src/filetreeitem.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 9eff9564..e5425573 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -178,12 +178,14 @@ public: void FileTreeItem::sort() { - m_model->sortItem(*this); + if (!m_children.empty()) { + m_model->sortItem(*this, true); + } } -void FileTreeItem::sort(int column, Qt::SortOrder order) +void FileTreeItem::sort(int column, Qt::SortOrder order, bool force) { - if (!m_expanded) { + if (!force && !m_expanded) { m_sortingStale = true; return; } @@ -220,7 +222,7 @@ void FileTreeItem::sort(int column, Qt::SortOrder order) }); for (auto& child : m_children) { - child->sort(column, order); + child->sort(column, order, force); } } diff --git a/src/filetreeitem.h b/src/filetreeitem.h index e173a849..390d5499 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -92,7 +92,7 @@ public: return -1; } - void sort(int column, Qt::SortOrder order); + void sort(int column, Qt::SortOrder order, bool force); FileTreeItem* parent() { diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 40cded09..2130bf89 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -379,7 +379,7 @@ Qt::ItemFlags FileTreeModel::flags(const QModelIndex& index) const return f; } -void FileTreeModel::sortItem(FileTreeItem& item) +void FileTreeModel::sortItem(FileTreeItem& item, bool force) { emit layoutAboutToBeChanged(); @@ -394,7 +394,7 @@ void FileTreeModel::sortItem(FileTreeItem& item) oldItems.push_back({itemFromIndex(index), index.column()}); } - item.sort(m_sort.column, m_sort.order); + item.sort(m_sort.column, m_sort.order, force); QModelIndexList newList; newList.reserve(itemCount); @@ -414,7 +414,7 @@ void FileTreeModel::sort(int column, Qt::SortOrder order) m_sort.column = column; m_sort.order = order; - sortItem(*m_root); + sortItem(*m_root, false); } FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const @@ -487,7 +487,7 @@ void FileTreeModel::update( } if (added) { - parentItem.sort(m_sort.column, m_sort.order); + parentItem.sort(m_sort.column, m_sort.order, true); } } diff --git a/src/filetreemodel.h b/src/filetreemodel.h index fbc7ec44..093407b0 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -76,7 +76,7 @@ public: void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override; FileTreeItem* itemFromIndex(const QModelIndex& index) const; - void sortItem(FileTreeItem& item); + void sortItem(FileTreeItem& item, bool force); private: class Range; -- cgit v1.3.1