diff options
| -rw-r--r-- | src/filetreeitem.h | 6 | ||||
| -rw-r--r-- | src/filetreemodel.cpp | 240 | ||||
| -rw-r--r-- | src/filetreemodel.h | 10 |
3 files changed, 143 insertions, 113 deletions
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<std::unique_ptr<FileTreeItem>>; + enum Flag { NoFlags = 0x00, @@ -55,7 +57,7 @@ public: m_loaded = false; } - const std::vector<std::unique_ptr<FileTreeItem>>& children() const + const Children& children() const { return m_children; } @@ -211,7 +213,7 @@ private: bool m_loaded; bool m_expanded; - std::vector<std::unique_ptr<FileTreeItem>> 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<int>(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<std::size_t>(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<std::size_t>(m_first), + static_cast<std::size_t>(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"<root>"), @@ -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<std::unique_ptr<FileTreeItem>> 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<std::size_t>(first), - static_cast<std::size_t>(last - first + 1)); - - endRemoveRows(); + range.add(std::move(toAdd)); } -void FileTreeModel::addRange( - FileTreeItem& parentItem, int at, - std::vector<std::unique_ptr<FileTreeItem>>& items) -{ - const auto parentIndex = indexFromItem(parentItem); - beginInsertRows(parentIndex, at, at + static_cast<int>(items.size()) - 1); - - parentItem.insert( - std::make_move_iterator(items.begin()), - std::make_move_iterator(items.end()), - static_cast<std::size_t>(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<int>(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<std::unique_ptr<FileTreeItem>> 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<MOShared::DirectoryEntry*>::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<std::wstring_view>& seen); - void removeRange(FileTreeItem& parentItem, int first, int last); - - void addRange( - FileTreeItem& parentItem, int at, - std::vector<std::unique_ptr<FileTreeItem>>& items); - void updateFiles( FileTreeItem& parentItem, const std::wstring& path, @@ -108,6 +105,7 @@ private: const std::wstring& parentPath, int firstFileRow, const std::unordered_set<MOShared::FileEntry::Index>& seen); + std::wstring makeModName(const MOShared::FileEntry& file, int originID) const; void ensureLoaded(FileTreeItem* item) const; |
