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.h | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/filetreeitem.h (limited to 'src/filetreeitem.h') diff --git a/src/filetreeitem.h b/src/filetreeitem.h new file mode 100644 index 00000000..516319ac --- /dev/null +++ b/src/filetreeitem.h @@ -0,0 +1,78 @@ +#ifndef MODORGANIZER_FILETREEITEM_INCLUDED +#define MODORGANIZER_FILETREEITEM_INCLUDED + +#include + +class FileTreeItem +{ +public: + enum Flag + { + NoFlags = 0x00, + Directory = 0x01, + FromArchive = 0x02, + Conflicted = 0x04 + }; + + Q_DECLARE_FLAGS(Flags, Flag); + + FileTreeItem(); + FileTreeItem( + FileTreeItem* parent, int originID, + std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, + std::wstring file, std::wstring mod); + + FileTreeItem(const FileTreeItem&) = delete; + FileTreeItem& operator=(const FileTreeItem&) = delete; + FileTreeItem(FileTreeItem&&) = default; + FileTreeItem& operator=(FileTreeItem&&) = default; + + void add(std::unique_ptr 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; + QString virtualPath() const; + const QString& filename() const; + const QString& mod() const; + QFont font() const; + + const QString& realPath() const; + QString dataRelativeParentPath() const; + QString dataRelativeFilePath() const; + + QFileIconProvider::IconType icon() const; + + bool isDirectory() const; + bool isFromArchive() const; + bool isConflicted() const; + bool isHidden() const; + bool hasChildren() const; + + void setLoaded(bool b); + bool isLoaded() const; + void unload(); + + void setExpanded(bool b); + bool isStrictlyExpanded() const; + bool areChildrenVisible() const; + + QString debugName() const; + +private: + FileTreeItem* m_parent; + int m_originID; + QString m_virtualParentPath; + QString m_realPath; + Flags m_flags; + QString m_file; + QString m_mod; + bool m_loaded; + bool m_expanded; + std::vector> m_children; +}; + +#endif // MODORGANIZER_FILETREEITEM_INCLUDED -- 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.h') 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.h') 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.h') 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.h') 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 bb9a4600bb3e83ad35a061db228c81cf8e108407 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 15 Jan 2020 16:14:34 -0500 Subject: rewriting FileTreeModel, implemented add/remove folders in root --- src/filetreeitem.h | 7 + src/filetreemodel.cpp | 957 ++++++++------------------------------------ src/filetreemodel.h | 38 +- src/pch.h | 1 + src/shared/directoryentry.h | 5 + 5 files changed, 220 insertions(+), 788 deletions(-) (limited to 'src/filetreeitem.h') diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 70757ca2..2819afbd 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -33,6 +33,13 @@ public: } void insert(std::unique_ptr child, std::size_t at); + + template + void insert(Itor begin, Itor end, std::size_t at) + { + m_children.insert(m_children.begin() + at, begin, end); + } + void remove(std::size_t i); void remove(std::size_t from, std::size_t n); diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 91dd4b90..4c6cdf6b 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -1,7 +1,6 @@ #include "filetreemodel.h" #include "organizercore.h" #include -#include using namespace MOBase; using namespace MOShared; @@ -11,9 +10,9 @@ QString UnmanagedModName(); template -void trace(F&&) +void trace(F&& f) { - //f(); + f(); } @@ -24,17 +23,17 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : { m_root.setExpanded(true); - connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); }); - - connect( - this, &QAbstractItemModel::modelAboutToBeReset, - [&]{ m_iconPending.clear(); }); - - connect( - this, &QAbstractItemModel::rowsAboutToBeRemoved, - [&](auto&& parent, int first, int last){ - removePendingIcons(parent, first, last); - }); + //connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); }); + // + //connect( + // this, &QAbstractItemModel::modelAboutToBeReset, + // [&]{ m_iconPending.clear(); }); + // + //connect( + // this, &QAbstractItemModel::rowsAboutToBeRemoved, + // [&](auto&& parent, int first, int last){ + // removePendingIcons(parent, first, last); + // }); } void FileTreeModel::refresh() @@ -42,16 +41,8 @@ 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""); - } else { - TimeThis tt("FileTreeModel::fill()"); - beginResetModel(); - m_root.clear(); - fill(m_root, *m_core.directoryStructure(), L""); - endResetModel(); - } + TimeThis tt("FileTreeModel::refresh()"); + update(m_root, *m_core.directoryStructure(), L""); } void FileTreeModel::clear() @@ -66,883 +57,291 @@ bool FileTreeModel::showArchives() const return (m_flags & Archives) && m_core.getArchiveParsing(); } -void FileTreeModel::ensureLoaded(FileTreeItem* item) const -{ - if (m_isRefreshing) { - return; - } - - if (!item) { - log::error("ensureLoaded(): item is null"); - return; - } - - if (item->isLoaded()) { - return; - } - - trace([&]{ log::debug("{}: loading on demand", item->debugName()); }); - - const auto path = item->dataRelativeFilePath(); - auto* dir = m_core.directoryStructure()->findSubDirectoryRecursive( - path.toStdWString()); - - if (!dir) { - log::error("{}: directory '{}' not found", item->debugName(), path); - return; - } - - const_cast(this) - ->fill(*item, *dir, item->dataRelativeParentPath().toStdWString()); -} - -void FileTreeModel::fill( - FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, - const std::wstring& parentPath) +QModelIndex FileTreeModel::index( + int row, int col, const QModelIndex& parentIndex) const { - trace([&]{ log::debug("filling {}", parentItem.debugName()); }); - - std::wstring path = parentPath; - - if (!parentEntry.isTopLevel()) { - if (!path.empty()) { - path += L"\\"; + if (auto* parentItem=itemFromIndex(parentIndex)) { + if (row < 0 || row >= parentItem->children().size()) { + return {}; } - path += parentEntry.getName(); + return createIndex(row, col, parentItem); } - const auto flags = FillFlag::PruneDirectories; - - std::vector::const_iterator begin, end; - parentEntry.getSubDirectories(begin, end); - fillDirectories(parentItem, path, begin, end, flags); - - fillFiles(parentItem, path, parentEntry.getFiles(), flags); - - parentItem.setLoaded(true); + log::error("FileTreeModel::index(): parentIndex has no internal pointer"); + return {}; } -void FileTreeModel::update( - FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, - const std::wstring& parentPath) +QModelIndex FileTreeModel::parent(const QModelIndex& index) const { - trace([&]{ log::debug("updating {}", parentItem.debugName()); }); - - std::wstring path = parentPath; + if (!index.isValid()) { + return {}; + } - if (!parentEntry.isTopLevel()) { - if (!path.empty()) { - path += L"\\"; + if (auto* item=itemFromIndex(index)) { + if (auto* parent=item->parent()) { + return indexFromItem(*parent); + } else { + return {}; } - - path += parentEntry.getName(); } - const auto flags = FillFlag::PruneDirectories; - - updateDirectories(parentItem, path, parentEntry, flags); - updateFiles(parentItem, path, parentEntry, flags); + log::error("FileTreeModel::parent(): no internal pointer"); + return {}; } -bool FileTreeModel::shouldShowFile(const FileEntry& file) const +int FileTreeModel::rowCount(const QModelIndex& parent) const { - if (showConflicts() && (file.getAlternatives().size() == 0)) { - return false; - } - - bool isArchive = false; - int originID = file.getOrigin(isArchive); - if (!showArchives() && isArchive) { - return false; + if (auto* item=itemFromIndex(parent)) { + return static_cast(item->children().size()); } - return true; + return 0; } -bool FileTreeModel::hasFilesAnywhere(const DirectoryEntry& dir) const +int FileTreeModel::columnCount(const QModelIndex&) const { - bool foundFile = false; - - dir.forEachFile([&](auto&& f) { - if (shouldShowFile(f)) { - foundFile = true; - - // stop - return false; - } - - // continue - return true; - }); - - if (foundFile) { - return true; - } - - std::vector::const_iterator begin, end; - dir.getSubDirectories(begin, end); - - for (auto itor=begin; itor!=end; ++itor) { - if (hasFilesAnywhere(**itor)) { - return true; - } - } - - return false; + return 2; } -void FileTreeModel::fillDirectories( - FileTreeItem& parentItem, const std::wstring& path, - DirectoryIterator begin, DirectoryIterator end, FillFlags flags) +bool FileTreeModel::hasChildren(const QModelIndex& parent) const { - for (auto itor=begin; itor!=end; ++itor) { - const auto& dir = **itor; - - if (flags & FillFlag::PruneDirectories) { - if (!hasFilesAnywhere(dir)) { - continue; - } - } - - auto child = std::make_unique( - &parentItem, 0, path, L"", FileTreeItem::Directory, dir.getName(), L""); - - if (dir.isEmpty()) { - child->setLoaded(true); - } - - parentItem.add(std::move(child)); + if (auto* item=itemFromIndex(parent)) { + return item->hasChildren(); + } else { + return m_root.hasChildren(); } } -void FileTreeModel::fillFiles( - FileTreeItem& parentItem, const std::wstring& path, - const std::vector& files, FillFlags) +bool FileTreeModel::canFetchMore(const QModelIndex& parent) const { - for (auto&& file : files) { - if (!shouldShowFile(*file)) { - continue; - } - - 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; - } - - parentItem.add(std::make_unique( - &parentItem, originID, path, file->getFullPath(), flags, file->getName(), - makeModName(*file, originID))); - } + return false; } -void FileTreeModel::updateDirectories( - FileTreeItem& parentItem, const std::wstring& path, - const MOShared::DirectoryEntry& parentEntry, FillFlags flags) +QVariant FileTreeModel::data(const QModelIndex& index, int role) const { - trace([&]{ log::debug( - "updating directories in {} from {}", - parentItem.debugName(), (path.empty() ? L"\\" : path)); - }); - - int row = 0; - std::list remove; - std::unordered_set seen; - - for (auto&& item : parentItem.children()) { - if (!item->isDirectory()) { - break; - } - - if (auto d=parentEntry.findSubDirectory(item->filenameWsLowerCase(), true)) { - // directory still exists - seen.insert(item->filenameWs()); - - if (item->areChildrenVisible()) { - trace([&]{ log::debug( - "{} still exists and is expanded", item->debugName()); - }); - - // node is expanded - update(*item, *d, path); - - if (flags & FillFlag::PruneDirectories) { - if (item->children().empty()) { - trace([&]{ log::debug( - "{} is now empty, will prune", item->debugName()); - }); - - remove.push_back(item.get()); - } - } - } else { - if ((flags & FillFlag::PruneDirectories) && !hasFilesAnywhere(*d)) { - trace([&]{ log::debug( - "{} still exists but is empty; pruning", - item->debugName()); - }); - - remove.push_back(item.get()); - } else if (item->isLoaded()) { - trace([&]{ log::debug( - "{} still exists, is loaded, but is not expanded; unloading", - item->debugName()); - }); - - // node is not expanded, unload - - bool mustEnd = false; - - if (!item->children().empty()) { - const auto itemIndex = indexFromItem(item.get(), row, 0); - const int first = 0; - const int last = static_cast(item->children().size()); - - beginRemoveRows(itemIndex, first, last); - mustEnd = true; - } - - item->unload(); - - if (mustEnd) { - endRemoveRows(); - } - - if (d->isEmpty()) { - item->setLoaded(true); - } - } - } - } else { - // directory is gone - trace([&]{ log::debug("{} is gone, removing", item->debugName()); }); - remove.push_back(item.get()); - } - - ++row; - } - - if (!remove.empty()) { - trace([&]{ log::debug( - "{}: removing disappearing items", - parentItem.debugName()); - }); - - - QModelIndex parentIndex; - int first = -1; - int last = -1; - - const auto& cs = parentItem.children(); - for (std::size_t i=0; i(i), 0)); - } - - if (first == -1) { - first = i; - last = i; - } else if (i == (last + 1)) { - last = i; - } else { - beginRemoveRows(parentIndex, first, last); - - parentItem.remove( - static_cast(first), - static_cast(last - first + 1)); - - endRemoveRows(); - - first = i; - last = i; - } - - remove.erase(itor); - break; + switch (role) + { + case Qt::DisplayRole: + { + if (auto* item=itemFromIndex(index)) { + if (index.column() == 0) { + return item->filename(); + } else if (index.column() == 1) { + return item->mod(); } } - } - - if (first != -1) { - beginRemoveRows(parentIndex, first, last); - - parentItem.remove( - static_cast(first), - static_cast(last - first + 1)); - endRemoveRows(); + break; } } - - std::vector::const_iterator begin, end; - parentEntry.getSubDirectories(begin, end); - - std::size_t insertPos = 0; - for (auto itor=begin; itor!=end; ++itor) { - const auto& dir = **itor; - - if (!seen.contains(dir.getName())) { - trace([&]{ log::debug( - "{}: new directory {}", - parentItem.debugName(), QString::fromStdWString(dir.getName())); - }); - - if (flags & FillFlag::PruneDirectories) { - if (!hasFilesAnywhere(dir)) { - trace([&]{ log::debug("has no files and pruning is set, skipping"); }); - continue; - } - } - - auto child = std::make_unique( - &parentItem, 0, path, L"", FileTreeItem::Directory, dir.getName(), L""); - - if (dir.isEmpty()) { - child->setLoaded(true); - } - - QModelIndex parentIndex; - - if (parentItem.parent()) { - const auto& cs = parentItem.parent()->children(); - - for (std::size_t i=0; i(i), 0); - break; - } - } - } - - const auto first = static_cast(insertPos); - const auto last = static_cast(insertPos); - - trace([&]{ log::debug( - "{}: inserting {} at {}", - parentItem.debugName(), child->debugName(), insertPos); - }); - - beginInsertRows(parentIndex, first, last); - parentItem.insert(std::move(child), insertPos); - endInsertRows(); - } - - ++insertPos; - } + return {}; } -void FileTreeModel::updateFiles( - FileTreeItem& parentItem, const std::wstring& path, - const MOShared::DirectoryEntry& parentEntry, FillFlags) +QVariant FileTreeModel::headerData(int i, Qt::Orientation ori, int role) const { - trace([&]{ log::debug( - "updating files in {} from {}", - parentItem.debugName(), (path.empty() ? L"\\" : path)); - }); - - std::unordered_set seen; - std::vector remove; - - for (auto&& item : parentItem.children()) { - if (item->isDirectory()) { - continue; - } - - if (auto f=parentEntry.findFile(item->key())) { - if (shouldShowFile(*f)) { - // file still exists - trace([&]{ log::debug("{} still exists", item->debugName()); }); - seen.emplace(f->getIndex()); - continue; - } - } - - trace([&]{ log::debug("{} is gone", item->debugName()); }); - - remove.push_back(item.get()); - } - - - if (!remove.empty()) { - trace([&]{ log::debug( - "{}: removing disappearing items", parentItem.debugName()); - }); - - for (auto* toRemove : remove) { - const auto& cs = parentItem.children(); - - for (std::size_t i=0; i(i), 0); - - const auto parentIndex = parent(itemIndex); - const int first = static_cast(i); - const int last = static_cast(i); - - beginRemoveRows(parentIndex, first, last); - parentItem.remove(i); - endRemoveRows(); - - break; - } - } - } - } - - std::size_t firstFile = 0; - for (std::size_t i=0; iisDirectory()) { - break; + if (role == Qt::DisplayRole) { + if (i == 0) { + return tr("File"); + } else if (i == 1) { + return tr("Mod"); } - - ++firstFile; } - trace([&]{ log::debug( - "{}: first file index is {}", parentItem.debugName(), firstFile); - }); - - std::size_t insertPos = firstFile; - - 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())); - }); - - 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 child = std::make_unique( - &parentItem, originID, path, file->getFullPath(), flags, file->getName(), - makeModName(*file, originID)); - - trace([&]{ log::debug( - "{}: inserting {} at {}", - parentItem.debugName(), child->debugName(), insertPos); - }); - - QModelIndex parentIndex; - - if (parentItem.parent()) { - const auto& cs = parentItem.parent()->children(); - - for (std::size_t i=0; i(i), 0); - break; - } - } - } - - const auto first = static_cast(insertPos); - const auto last = static_cast(insertPos); - - beginInsertRows(parentIndex, first, last); - parentItem.insert(std::move(child), insertPos); - endInsertRows(); - } else { - ++insertPos; - } - } else { - ++insertPos; - } - - return true; - }); + return {}; } -std::wstring FileTreeModel::makeModName(const FileEntry& file, int originID) const +Qt::ItemFlags FileTreeModel::flags(const QModelIndex& index) 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; + return QAbstractItemModel::flags(index); } FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const { - auto* data = index.internalPointer(); - if (!data) { - return nullptr; - } - - return static_cast(data); -} - -QModelIndex FileTreeModel::indexFromItem( - FileTreeItem* item, int row, int col) const -{ - return createIndex(row, col, item); -} - -QModelIndex FileTreeModel::index( - int row, int col, const QModelIndex& parentIndex) const -{ - FileTreeItem* parent = nullptr; - - if (!parentIndex.isValid()) { - parent = &m_root; - } else { - parent = itemFromIndex(parentIndex); - } - - if (!parent) { - log::error("FileTreeModel::index(): parent is null"); - return {}; + if (!index.isValid()) { + return &m_root; } - ensureLoaded(parent); - - if (static_cast(row) >= parent->children().size()) { - // don't warn if the tree hasn't been refreshed yet - if (!m_root.children().empty()) { - log::error( - "FileTreeModel::index(): row {} is out of range for {}", - row, parent->debugName()); - } - - return {}; + auto* parentItem = static_cast(index.internalPointer()); + if (!parentItem) { + log::error("FileTreeModel::itemFromIndex(): no internal pointer"); + return nullptr; } - if (col >= columnCount({})) { + if (index.row() < 0 || index.row() >= parentItem->children().size()) { log::error( - "FileTreeModel::index(): col {} is out of range for {}", - col, parent->debugName()); + "FileeTreeModel::itemFromIndex(): row {} is out of range for {}", + index.row(), parentItem->debugName()); - return {}; + return nullptr; } - auto* item = parent->children()[static_cast(row)].get(); - return indexFromItem(item, row, col); + return parentItem->children()[index.row()].get(); } -QModelIndex FileTreeModel::parent(const QModelIndex& index) const +QModelIndex FileTreeModel::indexFromItem(FileTreeItem& item) const { - if (!index.isValid()) { - return {}; - } - - auto* item = itemFromIndex(index); - if (!item) { - return {}; - } - - auto* parent = item->parent(); + auto* parent = item.parent(); if (!parent) { return {}; } - ensureLoaded(parent); + const auto& cs = parent->children(); - int row = 0; - for (auto&& child : parent->children()) { - if (child.get() == item) { - return createIndex(row, 0, parent); + for (std::size_t i=0; i(i), 0, &item); } - - ++row; } log::error( - "FileTreeModel::parent(): item {} has no child {}", - parent->debugName(), item->debugName()); + "FileTreeMode::indexFromItem(): item {} not found in parent", + item.debugName()); return {}; } -int FileTreeModel::rowCount(const QModelIndex& parent) const +void FileTreeModel::update( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath) { - FileTreeItem* item = nullptr; - - if (!parent.isValid()) { - item = &m_root; - } else { - item = itemFromIndex(parent); - } - - if (!item) { - return 0; - } - - ensureLoaded(item); - return static_cast(item->children().size()); + trace([&]{ log::debug("updating {}", parentItem.debugName()); }); + updateDirectories(parentItem, parentPath, parentEntry, FillFlag::None); } -int FileTreeModel::columnCount(const QModelIndex&) const -{ - return 2; -} -bool FileTreeModel::hasChildren(const QModelIndex& parent) const +void FileTreeModel::updateDirectories( + FileTreeItem& parentItem, const std::wstring& parentPath, + const MOShared::DirectoryEntry& parentEntry, FillFlags flags) { - const FileTreeItem* item = nullptr; - - if (!parent.isValid()) { - item = &m_root; - } else { - item = itemFromIndex(parent); - } - - if (!item) { - return false; - } + std::unordered_set seen; - return item->hasChildren(); + removeDisappearingDirectories(parentItem, parentEntry, seen); + addNewDirectories(parentItem, parentEntry, parentPath, seen); } -QVariant FileTreeModel::data(const QModelIndex& index, int role) const +void FileTreeModel::removeDisappearingDirectories( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + std::unordered_set& seen) { - switch (role) - { - case Qt::DisplayRole: - { - if (auto* item=itemFromIndex(index)) { - if (index.column() == 0) { - return item->filename(); - } else if (index.column() == 1) { - return item->mod(); - } - } + auto& children = parentItem.children(); + auto itor = children.begin(); - break; - } + int removeStart = -1; + int row = 0; - case Qt::FontRole: - { - if (auto* item=itemFromIndex(index)) { - return item->font(); - } + while (itor != children.end()) { + const auto& item = *itor; + if (!item->isDirectory()) { + // directories are always first, no point continuing once a file has been + // seen break; } - case Qt::ToolTipRole: - { - if (auto* item=itemFromIndex(index)) { - return makeTooltip(*item); - } + auto d = parentEntry.findSubDirectory(item->filenameWsLowerCase(), true); - return {}; - } + if (d) { + trace([&]{ log::debug("{} still there", item->filename()); }); - case Qt::ForegroundRole: - { - if (index.column() == 1) { - if (auto* item=itemFromIndex(index)) { - if (item->isConflicted()) { - return QBrush(Qt::red); - } - } - } + // directory is still there + seen.insert(item->filenameWs()); - break; - } + if (removeStart != -1) { + removeRange(parentItem, removeStart, row - 1); - case Qt::DecorationRole: - { - if (index.column() == 0) { - if (auto* item=itemFromIndex(index)) { - return makeIcon(*item, index); - } + removeStart = -1; + row -= (row - removeStart); + itor = children.begin() + row; } + } else { + // directory is gone from the parent entry + trace([&]{ log::debug("{} is gone", item->filename()); }); - break; + if (removeStart == -1) { + removeStart = row; + } } + + ++row; + ++itor; } - return {}; + if (removeStart != -1) { + removeRange(parentItem, removeStart, row -1 ); + } } -QString FileTreeModel::makeTooltip(const FileTreeItem& item) const +void FileTreeModel::addNewDirectories( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath, + const std::unordered_set& seen) { - if (item.isDirectory()) { - return {}; - } + std::vector> toAdd; + int addStart = -1; + int row = 0; - auto nowrap = [&](auto&& s) { - return "

" + s + "

"; - }; + for (auto&& d : parentEntry.getSubDirectories()) { + if (seen.contains(d->getName())) { + // already seen in the parent item - auto line = [&](auto&& caption, auto&& value) { - if (value.isEmpty()) { - return nowrap("" + caption + ":\n"); + if (addStart != -1) { + addRange(parentItem, addStart, toAdd); + toAdd.clear(); + addStart = -1; + } } else { - return nowrap("" + caption + ": " + value.toHtmlEscaped()) + "\n"; - } - }; - - static const QString ListStart = - "
    "; - - static const QString ListEnd = "
"; - - - QString s = - line(tr("Virtual path"), item.virtualPath()) + - line(tr("Real path"), item.realPath()) + - line(tr("From"), item.mod()); - + // this is a new directory + trace([&]{ log::debug("new {}", QString::fromStdWString(d->getName())); }); - const auto file = m_core.directoryStructure()->searchFile( - item.dataRelativeFilePath().toStdWString(), nullptr); + auto item = std::make_unique( + &parentItem, 0, parentPath, L"", FileTreeItem::Directory, + d->getName(), L""); - if (file) { - const auto alternatives = file->getAlternatives(); - QStringList list; - - for (auto&& alt : file->getAlternatives()) { - const auto& origin = m_core.directoryStructure()->getOriginByID(alt.first); - list.push_back(QString::fromStdWString(origin.getName())); - } + if (d->isEmpty()) { + item->setLoaded(true); + } - if (list.size() == 1) { - s += line(tr("Also in"), list[0]); - } else if (list.size() >= 2) { - s += line(tr("Also in"), QString()) + ListStart; + toAdd.push_back(std::move(item)); - for (auto&& alt : list) { - s += "
  • " + alt +"
  • "; + if (addStart == -1) { + addStart = row; } - - s += ListEnd; } - } - - return s; -} - -QVariant FileTreeModel::makeIcon( - const FileTreeItem& item, const QModelIndex& index) const -{ - if (item.isDirectory()) { - return m_iconFetcher.genericDirectoryIcon(); - } - - auto v = m_iconFetcher.icon(item.realPath()); - if (!v.isNull()) { - return v; - } - - m_iconPending.push_back(index); - m_iconPendingTimer.start(std::chrono::milliseconds(1)); - - return m_iconFetcher.genericFileIcon(); -} - -void FileTreeModel::updatePendingIcons() -{ - std::vector v(std::move(m_iconPending)); - m_iconPending.clear(); - for (auto&& index : v) { - emit dataChanged(index, index, {Qt::DecorationRole}); + ++row; } - if (m_iconPending.empty()) { - m_iconPendingTimer.stop(); + if (addStart != -1) { + addRange(parentItem, addStart, toAdd); } } -void FileTreeModel::removePendingIcons( - const QModelIndex& parent, int first, int last) +void FileTreeModel::removeRange(FileTreeItem& parentItem, int first, int last) { - auto itor = m_iconPending.begin(); - - while (itor != m_iconPending.end()) { - if (itor->parent() == parent) { - if (itor->row() >= first && itor->row() <= last) { - if (auto* item=itemFromIndex(*itor)) { - log::debug("removing pending icon {}", item->debugName()); - } else { - log::debug("removing pending icon (can't get item)"); - } + const auto parentIndex = indexFromItem(parentItem); - itor = m_iconPending.erase(itor); - continue; - } - } + beginRemoveRows(parentIndex, first, last); - ++itor; - } -} - -QVariant FileTreeModel::headerData(int i, Qt::Orientation ori, int role) const -{ - if (role == Qt::DisplayRole) { - if (i == 0) { - return tr("File"); - } else if (i == 1) { - return tr("Mod"); - } - } + parentItem.remove( + static_cast(first), + static_cast(last - first + 1)); - return {}; + endRemoveRows(); } -Qt::ItemFlags FileTreeModel::flags(const QModelIndex& index) const +void FileTreeModel::addRange( + FileTreeItem& parentItem, int at, + std::vector>& items) { - auto f = QAbstractItemModel::flags(index); + const auto parentIndex = indexFromItem(parentItem); + beginInsertRows(parentIndex, at, at + static_cast(items.size()) - 1); - if (auto* item=itemFromIndex(index)) { - if (!item->hasChildren()) { - f |= Qt::ItemNeverHasChildren; - } - } + parentItem.insert( + std::make_move_iterator(items.begin()), + std::make_move_iterator(items.end()), + static_cast(at)); - return f; + endInsertRows(); } diff --git a/src/filetreemodel.h b/src/filetreemodel.h index c363266a..c2f7d8cf 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -4,6 +4,7 @@ #include "filetreeitem.h" #include "iconfetcher.h" #include "directoryentry.h" +#include class OrganizerCore; @@ -36,9 +37,11 @@ public: int rowCount(const QModelIndex& parent={}) const override; int columnCount(const QModelIndex& parent={}) const override; bool hasChildren(const QModelIndex& parent={}) const override; + bool canFetchMore(const QModelIndex& parent) const override; 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; + FileTreeItem* itemFromIndex(const QModelIndex& index) const; private: @@ -66,6 +69,31 @@ private: bool showArchives() const; + + void update( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath); + + void updateDirectories( + FileTreeItem& parentItem, const std::wstring& path, + const MOShared::DirectoryEntry& parentEntry, FillFlags flags); + + void removeDisappearingDirectories( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + std::unordered_set& seen); + + void addNewDirectories( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath, + const std::unordered_set& seen); + + void removeRange(FileTreeItem& parentItem, int first, int last); + + void addRange( + FileTreeItem& parentItem, int at, + std::vector>& items); + + void fill( FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, const std::wstring& parentPath); @@ -79,14 +107,6 @@ private: const std::vector& files, FillFlags flags); - void update( - FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, - const std::wstring& parentPath); - - void updateDirectories( - FileTreeItem& parentItem, const std::wstring& path, - const MOShared::DirectoryEntry& parentEntry, FillFlags flags); - void updateFiles( FileTreeItem& parentItem, const std::wstring& path, const MOShared::DirectoryEntry& parentEntry, FillFlags flags); @@ -102,7 +122,7 @@ private: QString makeTooltip(const FileTreeItem& item) const; QVariant makeIcon(const FileTreeItem& item, const QModelIndex& index) const; - QModelIndex indexFromItem(FileTreeItem* item, int row, int col) const; + QModelIndex indexFromItem(FileTreeItem& item) const; }; Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeModel::Flags); diff --git a/src/pch.h b/src/pch.h index b7c5d695..8e8d33f7 100644 --- a/src/pch.h +++ b/src/pch.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 91c2a140..f1d3ba03 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -352,6 +352,11 @@ public: end = m_SubDirectories.end(); } + const std::vector& getSubDirectories() const + { + return m_SubDirectories; + } + template void forEachDirectory(F&& f) const { -- 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.h') 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 f49643075ac4a0bac1f4d1983398b8f19297b2f4 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 20 Jan 2020 19:27:47 -0500 Subject: moved a bunch of duplicate stuff in a new class Range --- src/filetreeitem.h | 6 +- src/filetreemodel.cpp | 240 ++++++++++++++++++++++++++++---------------------- src/filetreemodel.h | 10 +-- 3 files changed, 143 insertions(+), 113 deletions(-) (limited to 'src/filetreeitem.h') diff --git a/src/filetreeitem.h b/src/filetreeitem.h index e060f63a..2677d590 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -7,6 +7,8 @@ class FileTreeItem { public: + using Children = std::vector>; + enum Flag { NoFlags = 0x00, @@ -55,7 +57,7 @@ public: m_loaded = false; } - const std::vector>& children() const + const Children& children() const { return m_children; } @@ -211,7 +213,7 @@ private: bool m_loaded; bool m_expanded; - std::vector> m_children; + Children m_children; }; #endif // MODORGANIZER_FILETREEITEM_INCLUDED diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 00ab13d6..493a432e 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -16,6 +16,112 @@ void trace(F&& f) } +// tracks a contiguous range in the model to avoid calling begin*Rows(), etc. +// for every single item that's added/removed +// +class FileTreeModel::Range +{ +public: + // note that file ranges can start from an index higher than 0 if there are + // directories + // + Range(FileTreeModel* model, FileTreeItem& parentItem, int start=0) + : m_model(model), m_parentItem(parentItem), m_first(-1), m_current(start) + { + } + + // includes the current index in the range + // + void includeCurrent() + { + // just remember the start of the range, m_current will be used in add() + // or remove() to figure out the actual range + if (m_first == -1) { + m_first = m_current; + } + } + + // moves to the next row + // + void next() + { + ++m_current; + } + + // returns the current row + // + int current() const + { + return m_current; + } + + // adds the given items to this range + // + void add(FileTreeItem::Children toAdd) + { + if (m_first == -1) { + // nothing to add + Q_ASSERT(toAdd.empty()); + return; + } + + const auto last = m_current - 1; + 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)); + + m_model->beginInsertRows(parentIndex, m_first, last); + + m_parentItem.insert( + std::make_move_iterator(toAdd.begin()), + std::make_move_iterator(toAdd.end()), + static_cast(m_first)); + + m_model->endInsertRows(); + + // reset + m_first = -1; + } + + // removes the item in this range, returns an iterator to first item passed + // this range once removed, which can be end() + // + FileTreeItem::Children::const_iterator remove() + { + if (m_first == -1) { + // nothing to remove + return m_parentItem.children().begin() + m_current + 1; + } + + const auto last = m_current - 1; + const auto parentIndex = m_model->indexFromItem(m_parentItem); + + m_model->beginRemoveRows(parentIndex, m_first, last); + + m_parentItem.remove( + static_cast(m_first), + static_cast(last - m_first + 1)); + + m_model->endRemoveRows(); + + // adjust current row to account for those that were just removed + m_current -= (m_current - m_first); + + // reset + m_first = -1; + + return m_parentItem.children().begin() + m_current + 1; + } + +private: + FileTreeModel* m_model; + FileTreeItem& m_parentItem; + int m_first; + int m_current; +}; + + FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""), @@ -251,7 +357,6 @@ void FileTreeModel::update( parentItem.setLoaded(true); } - void FileTreeModel::updateDirectories( FileTreeItem& parentItem, const std::wstring& parentPath, const MOShared::DirectoryEntry& parentEntry, FillFlags flags) @@ -274,8 +379,7 @@ void FileTreeModel::removeDisappearingDirectories( // keeps track of the contiguous directories that need to be removed to // avoid calling beginRemoveRows(), etc. for each item - int removeStart = -1; - int row = 0; + Range range(this, parentItem); // for each item in this tree item while (itor != children.end()) { @@ -297,15 +401,7 @@ void FileTreeModel::removeDisappearingDirectories( // if there were directories 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; - } + itor = range.remove(); if (item->areChildrenVisible()) { update(*item, *d, parentPath); @@ -314,20 +410,15 @@ void FileTreeModel::removeDisappearingDirectories( // directory is gone from the parent entry trace([&]{ log::debug("dir {} is gone", item->filename()); }); - if (removeStart == -1) { - // start a new contiguous sequence - removeStart = row; - } + range.includeCurrent(); + ++itor; } - ++row; - ++itor; + range.next(); } // remove the last directory range, if any - if (removeStart != -1) { - removeRange(parentItem, removeStart, row -1 ); - } + range.remove(); } void FileTreeModel::addNewDirectories( @@ -337,9 +428,8 @@ void 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; - int addStart = -1; - int row = 0; // for each directory on the filesystem for (auto&& d : parentEntry.getSubDirectories()) { @@ -348,11 +438,8 @@ void FileTreeModel::addNewDirectories( // 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; - } + range.add(std::move(toAdd)); + toAdd.clear(); } else { // this is a new directory trace([&]{ log::debug("new dir {}", QString::fromStdWString(d->getName())); }); @@ -368,51 +455,16 @@ void FileTreeModel::addNewDirectories( } toAdd.push_back(std::move(item)); - - if (addStart == -1) { - // start a new contiguous sequence - addStart = row; - } + range.includeCurrent(); } - ++row; + range.next(); } // add the last directory range, if any - if (addStart != -1) { - addRange(parentItem, addStart, toAdd); - } -} - -void FileTreeModel::removeRange(FileTreeItem& parentItem, int first, int last) -{ - const auto parentIndex = indexFromItem(parentItem); - - beginRemoveRows(parentIndex, first, last); - - parentItem.remove( - static_cast(first), - static_cast(last - first + 1)); - - endRemoveRows(); + range.add(std::move(toAdd)); } -void FileTreeModel::addRange( - FileTreeItem& parentItem, int at, - std::vector>& items) -{ - const auto parentIndex = indexFromItem(parentItem); - beginInsertRows(parentIndex, at, at + static_cast(items.size()) - 1); - - parentItem.insert( - std::make_move_iterator(items.begin()), - std::make_move_iterator(items.end()), - static_cast(at)); - - endInsertRows(); -} - - void FileTreeModel::updateFiles( FileTreeItem& parentItem, const std::wstring& parentPath, const MOShared::DirectoryEntry& parentEntry) @@ -439,8 +491,7 @@ void FileTreeModel::removeDisappearingFiles( // keeps track of the contiguous directories that need to be removed to // avoid calling beginRemoveRows(), etc. for each item - int removeStart = -1; - int row = 0; + Range range(this, parentItem); // for each item in this tree item while (itor != children.end()) { @@ -448,7 +499,7 @@ void FileTreeModel::removeDisappearingFiles( if (!item->isDirectory()) { if (firstFileRow == -1) { - firstFileRow = row; + firstFileRow = range.current(); } auto f = parentEntry.findFile(item->key()); @@ -461,38 +512,27 @@ void FileTreeModel::removeDisappearingFiles( // 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; - } + itor = range.remove(); } 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; - } + range.includeCurrent(); + ++itor; } + } else { + ++itor; } - ++row; - ++itor; + range.next(); } + // remove the last file range, if any + range.remove(); + if (firstFileRow == -1) { firstFileRow = static_cast(children.size()); } - - // remove the last file range, if any - if (removeStart != -1) { - removeRange(parentItem, removeStart, row -1 ); - } } void FileTreeModel::addNewFiles( @@ -503,8 +543,7 @@ void FileTreeModel::addNewFiles( // 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; + Range range(this, parentItem, firstFileRow); // for each directory on the filesystem parentEntry.forEachFileIndex([&](auto&& fileIndex) { @@ -513,11 +552,8 @@ void FileTreeModel::addNewFiles( // 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; - } + range.add(std::move(toAdd)); + toAdd.clear(); } else { const auto file = parentEntry.getFileByIndex(fileIndex); @@ -552,22 +588,16 @@ void FileTreeModel::addNewFiles( item->setLoaded(true); toAdd.push_back(std::move(item)); - - if (addStart == -1) { - // start a new contiguous sequence - addStart = row; - } + range.includeCurrent(); } - ++row; + range.next(); return true; }); // add the last file range, if any - if (addStart != -1) { - addRange(parentItem, addStart, toAdd); - } + range.add(std::move(toAdd)); } std::wstring FileTreeModel::makeModName( diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 70291b61..ac82a66a 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -52,6 +52,8 @@ private: PruneDirectories = 0x01 }; + class Range; + Q_DECLARE_FLAGS(FillFlags, FillFlag); using DirectoryIterator = std::vector::const_iterator; @@ -75,6 +77,7 @@ private: FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, const std::wstring& parentPath); + void updateDirectories( FileTreeItem& parentItem, const std::wstring& path, const MOShared::DirectoryEntry& parentEntry, FillFlags flags); @@ -88,12 +91,6 @@ private: const std::wstring& parentPath, const std::unordered_set& seen); - void removeRange(FileTreeItem& parentItem, int first, int last); - - void addRange( - FileTreeItem& parentItem, int at, - std::vector>& items); - void updateFiles( FileTreeItem& parentItem, const std::wstring& path, @@ -108,6 +105,7 @@ private: const std::wstring& parentPath, int firstFileRow, const std::unordered_set& seen); + std::wstring makeModName(const MOShared::FileEntry& file, int originID) const; void ensureLoaded(FileTreeItem* item) 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.h') 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.h') 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.h') 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.h') 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.h') 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.h') 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 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.h') 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.h') 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.h') 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