From 87868e1b22c27ebf10646269e89cc2848431c0b6 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 14 Dec 2019 14:28:17 -0500 Subject: added a few missing consts removed incorrect assert about an empty game edition added FileTree to wrap a QTreeView and a FileTreeModel, moved some context menu actions over --- src/shared/directoryentry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 00bf319e..9d49ce1e 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -433,7 +433,7 @@ std::wstring FileEntry::getRelativePath() const return result + L"\\" + m_Name; } -bool FileEntry::isFromArchive(std::wstring archiveName) +bool FileEntry::isFromArchive(std::wstring archiveName) const { if (archiveName.length() == 0) return m_Archive.first.length() != 0; if (m_Archive.first.compare(archiveName) == 0) return true; -- cgit v1.3.1 From e0c605be1d32b74b14da18cc7de4a66a4f6f3ecb Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 18:42:17 -0500 Subject: initial implementation for updating file tree, buggy --- src/filetree.cpp | 449 +++++++++++++++++++++++++++++++++++++++++- src/filetree.h | 26 +++ src/iconfetcher.cpp | 3 + src/shared/directoryentry.cpp | 5 + src/shared/directoryentry.h | 1 + src/shared/util.cpp | 27 +++ src/shared/util.h | 2 + 7 files changed, 503 insertions(+), 10 deletions(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/filetree.cpp b/src/filetree.cpp index ee6d932d..b9741a12 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -133,7 +133,8 @@ FileTreeItem::FileTreeItem( m_flags(flags), m_file(QString::fromStdWString(file)), m_mod(QString::fromStdWString(mod)), - m_loaded(false) + m_loaded(false), + m_expanded(false) { } @@ -142,6 +143,29 @@ void FileTreeItem::add(std::unique_ptr child) m_children.push_back(std::move(child)); } +void FileTreeItem::insert(std::unique_ptr child, std::size_t at) +{ + if (at > m_children.size()) { + log::error( + "{}: can't insert child {} at {}, out of range", + debugName(), child->debugName(), at); + + return; + } + + m_children.insert(m_children.begin() + at, std::move(child)); +} + +void FileTreeItem::remove(std::size_t i) +{ + if (i >= m_children.size()) { + log::error("{}: can't remove child at {}", debugName(), i); + return; + } + + m_children.erase(m_children.begin() + i); +} + const std::vector>& FileTreeItem::children() const { return m_children; @@ -270,6 +294,39 @@ bool FileTreeItem::isLoaded() const return m_loaded; } +void FileTreeItem::unload() +{ + if (!m_loaded) { + return; + } + + m_loaded = false; + m_children.clear(); +} + +void FileTreeItem::setExpanded(bool b) +{ + m_expanded = b; +} + +bool FileTreeItem::isStrictlyExpanded() const +{ + return m_expanded; +} + +bool FileTreeItem::areChildrenVisible() const +{ + if (m_expanded) { + if (m_parent) { + return m_parent->areChildrenVisible(); + } else { + return true; + } + } + + return false; +} + QString FileTreeItem::debugName() const { return QString("%1(ld=%2,cs=%3)") @@ -283,6 +340,16 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_flags(NoFlags) { 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::setFlags(Flags f) @@ -302,10 +369,15 @@ bool FileTreeModel::showArchives() const void FileTreeModel::refresh() { - beginResetModel(); - m_root = {nullptr, 0, L"", L"", FileTreeItem::Directory, L"Data", L""}; - fill(m_root, *m_core.directoryStructure(), L""); - endResetModel(); + if (m_root.hasChildren()) { + update(m_root, *m_core.directoryStructure(), L""); + } else { + beginResetModel(); + m_root = {nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""}; + m_root.setExpanded(true); + fill(m_root, *m_core.directoryStructure(), L""); + endResetModel(); + } } void FileTreeModel::ensureLoaded(FileTreeItem* item) const @@ -359,6 +431,28 @@ void FileTreeModel::fill( parentItem.setLoaded(true); } +void FileTreeModel::update( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath) +{ + log::debug("updating {}", parentItem.debugName()); + + std::wstring path = parentPath; + + if (!parentEntry.isTopLevel()) { + if (!path.empty()) { + path += L"\\"; + } + + path += parentEntry.getName(); + } + + const auto flags = FillFlag::PruneDirectories; + + updateDirectories(parentItem, path, parentEntry, flags); + updateFiles(parentItem, path, parentEntry, flags); +} + bool FileTreeModel::shouldShowFile(const FileEntry& file) const { if (showConflicts() && (file.getAlternatives().size() == 0)) { @@ -458,6 +552,288 @@ void FileTreeModel::fillFiles( } } +void FileTreeModel::updateDirectories( + FileTreeItem& parentItem, const std::wstring& path, + const MOShared::DirectoryEntry& parentEntry, FillFlags flags) +{ + log::debug( + "updating directories in {} from {}", + parentItem.debugName(), (path.empty() ? L"\\" : path)); + + int row = 0; + std::vector remove; + std::set seen; + + for (auto&& item : parentItem.children()) { + if (!item->isDirectory()) { + break; + } + + const auto name = item->filename().toStdWString(); + + if (auto d=parentEntry.findSubDirectory(name)) { + // directory still exists + seen.insert(name); + + if (item->areChildrenVisible()) { + log::debug("{} still exists and is expanded", item->debugName()); + + // node is expanded + update(*item, *d, path); + + if (flags & FillFlag::PruneDirectories) { + if (item->children().empty()) { + log::debug("{} is now empty, will prune", item->debugName()); + remove.push_back(item.get()); + } + } + } else { + if ((flags & FillFlag::PruneDirectories) && !hasFilesAnywhere(*d)) { + log::debug("{} still exists but is empty; pruning", item->debugName()); + remove.push_back(item.get()); + } else if (item->isLoaded()) { + 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 + log::debug("{} is gone, removing", item->debugName()); + remove.push_back(item.get()); + } + + ++row; + } + + if (!remove.empty()) { + 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::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())) { + log::debug( + "{}: new directory {}", + parentItem.debugName(), QString::fromStdWString(dir.getName())); + + if (flags & FillFlag::PruneDirectories) { + if (!hasFilesAnywhere(dir)) { + 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); + + log::debug( + "{}: inserting {} at {}", + parentItem.debugName(), child->debugName(), insertPos); + + beginInsertRows(parentIndex, first, last); + parentItem.insert(std::move(child), insertPos); + endInsertRows(); + } + + ++insertPos; + } +} + +void FileTreeModel::updateFiles( + FileTreeItem& parentItem, const std::wstring& path, + const MOShared::DirectoryEntry& parentEntry, FillFlags) +{ + log::debug( + "updating files in {} from {}", + parentItem.debugName(), (path.empty() ? L"\\" : path)); + + std::set seen; + std::vector remove; + + for (auto&& item : parentItem.children()) { + if (item->isDirectory()) { + continue; + } + + const auto name = item->filename().toStdWString(); + + if (auto f=parentEntry.findFile(name)) { + if (shouldShowFile(*f)) { + // file still exists + log::debug("{} still exists", item->debugName()); + seen.insert(name); + continue; + } + } + + log::debug("{} is gone", item->debugName()); + + remove.push_back(item.get()); + } + + + if (!remove.empty()) { + 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; + } + + ++firstFile; + } + + log::debug("{}: first file index is {}", parentItem.debugName(), firstFile); + std::size_t insertPos = firstFile; + + for (auto&& file : parentEntry.getFiles()) { + if (shouldShowFile(*file)) { + if (!seen.contains(file->getName())) { + 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)); + + 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(); + } + + ++insertPos; + } + } +} + std::wstring FileTreeModel::makeModName(const FileEntry& file, int originID) const { static const std::wstring Unmanaged = UnmanagedModName().toStdWString(); @@ -485,7 +861,18 @@ FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const return nullptr; } - return static_cast(data); + auto* item = static_cast(data); + if (!item->debugName().isEmpty()) { + return item; + } + + return nullptr; +} + +QModelIndex FileTreeModel::indexFromItem( + FileTreeItem* item, int row, int col) const +{ + return createIndex(row, col, item); } QModelIndex FileTreeModel::index( @@ -526,7 +913,7 @@ QModelIndex FileTreeModel::index( } auto* item = parent->children()[static_cast(row)].get(); - return createIndex(row, col, item); + return indexFromItem(item, row, col); } QModelIndex FileTreeModel::parent(const QModelIndex& index) const @@ -750,12 +1137,39 @@ QVariant FileTreeModel::makeIcon( void FileTreeModel::updatePendingIcons() { - for (auto&& index : m_iconPending) { + std::vector v(std::move(m_iconPending)); + m_iconPending.clear(); + + for (auto&& index : v) { emit dataChanged(index, index, {Qt::DecorationRole}); } - m_iconPending.clear(); - m_iconPendingTimer.stop(); + if (m_iconPending.empty()) { + m_iconPendingTimer.stop(); + } +} + +void FileTreeModel::removePendingIcons( + const QModelIndex& parent, 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)"); + } + + itor = m_iconPending.erase(itor); + continue; + } + } + + ++itor; + } } QVariant FileTreeModel::headerData(int i, Qt::Orientation ori, int role) const @@ -793,6 +1207,14 @@ FileTree::FileTree(OrganizerCore& core, PluginContainer& pc, QTreeView* tree) QObject::connect( m_tree, &QTreeWidget::customContextMenuRequested, [&](auto pos){ onContextMenu(pos); }); + + QObject::connect( + m_tree, &QTreeWidget::expanded, + [&](auto&& index){ onExpandedChanged(index, true); }); + + QObject::connect( + m_tree, &QTreeWidget::collapsed, + [&](auto&& index){ onExpandedChanged(index, false); }); } void FileTree::setFlags(FileTreeModel::Flags flags) @@ -1085,6 +1507,13 @@ void FileTree::dumpToFile( }); } +void FileTree::onExpandedChanged(const QModelIndex& index, bool expanded) +{ + if (auto* item=m_model->itemFromIndex(index)) { + item->setExpanded(expanded); + } +} + void FileTree::onContextMenu(const QPoint &pos) { QMenu menu; diff --git a/src/filetree.h b/src/filetree.h index 08ebdcb7..f92f10f2 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -35,6 +35,8 @@ public: 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(); @@ -59,6 +61,11 @@ public: void setLoaded(bool b); bool isLoaded() const; + void unload(); + + void setExpanded(bool b); + bool isStrictlyExpanded() const; + bool areChildrenVisible() const; QString debugName() const; @@ -71,6 +78,7 @@ private: QString m_file; QString m_mod; bool m_loaded; + bool m_expanded; std::vector> m_children; }; @@ -136,15 +144,31 @@ private: FileTreeItem& parentItem, const std::wstring& path, 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); + std::wstring makeModName(const MOShared::FileEntry& file, int originID) const; void ensureLoaded(FileTreeItem* item) const; void updatePendingIcons(); + void removePendingIcons(const QModelIndex& parent, int first, int last); bool shouldShowFile(const MOShared::FileEntry& file) const; bool hasFilesAnywhere(const MOShared::DirectoryEntry& dir) const; QString makeTooltip(const FileTreeItem& item) const; QVariant makeIcon(const FileTreeItem& item, const QModelIndex& index) const; + + QModelIndex indexFromItem(FileTreeItem* item, int row, int col) const; }; Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeModel::Flags); @@ -187,8 +211,10 @@ private: FileTreeModel* m_model; FileTreeItem* singleSelection(); + void onExpandedChanged(const QModelIndex& index, bool expanded); void onContextMenu(const QPoint &pos); + void addDirectoryMenus(QMenu& menu, FileTreeItem& item); void addFileMenus(QMenu& menu, const MOShared::FileEntry& file, int originID); void addOpenMenus(QMenu& menu, const MOShared::FileEntry& file); diff --git a/src/iconfetcher.cpp b/src/iconfetcher.cpp index 9f8e348a..2b19f993 100644 --- a/src/iconfetcher.cpp +++ b/src/iconfetcher.cpp @@ -1,4 +1,5 @@ #include "iconfetcher.h" +#include "util.h" void IconFetcher::Waiter::wait() { @@ -79,6 +80,8 @@ bool IconFetcher::hasOwnIcon(const QString& path) const void IconFetcher::threadFun() { + MOShared::SetThisThreadName("IconFetcher"); + while (!m_stop) { m_waiter.wait(); if (m_stop) { diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 9d49ce1e..146662ad 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -885,6 +885,11 @@ const FileEntry::Ptr DirectoryEntry::findFile(const std::wstring &name) const } } +bool DirectoryEntry::hasFile(const std::wstring& name) const +{ + return m_Files.contains(ToLower(name)); +} + DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool create, int originID) { for (DirectoryEntry *entry : m_SubDirectories) { diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 0a857443..52265583 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -279,6 +279,7 @@ public: * @return fileentry object for the file or nullptr if no file matches */ const FileEntry::Ptr findFile(const std::wstring &name) const; + bool hasFile(const std::wstring& name) const; bool containsArchive(std::wstring archiveName); diff --git a/src/shared/util.cpp b/src/shared/util.cpp index baceddeb..302dea7d 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -20,6 +20,7 @@ along with Mod Organizer. If not, see . #include "util.h" #include "windows_error.h" #include "mainwindow.h" +#include "env.h" #include #include @@ -290,6 +291,32 @@ QString getUsvfsVersionString() } } +void SetThisThreadName(const QString& s) +{ + using SetThreadDescriptionType = HRESULT ( + HANDLE hThread, + PCWSTR lpThreadDescription + ); + + static SetThreadDescriptionType* SetThreadDescription = [] { + SetThreadDescriptionType* p = nullptr; + + env::LibraryPtr kernel32(LoadLibraryW(L"kernel32.dll")); + if (!kernel32) { + return p; + } + + p = reinterpret_cast( + GetProcAddress(kernel32.get(), "SetThreadDescription")); + + return p; + }(); + + if (SetThreadDescription) { + SetThreadDescription(GetCurrentThread(), s.toStdWString().c_str()); + } +} + } // namespace MOShared diff --git a/src/shared/util.h b/src/shared/util.h index e8a58549..b6f898db 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -47,6 +47,8 @@ bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs); MOBase::VersionInfo createVersionInfo(); QString getUsvfsVersionString(); +void SetThisThreadName(const QString& s); + } // namespace MOShared -- 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/shared/directoryentry.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 38eb5ec2..c2f3a1fc 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -1,8 +1,10 @@ #include "filetreeitem.h" #include "modinfo.h" +#include "util.h" #include using namespace MOBase; +using namespace MOShared; FileTreeItem::FileTreeItem() : m_flags(NoFlags), m_loaded(false) @@ -17,6 +19,7 @@ FileTreeItem::FileTreeItem( m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), m_realPath(QString::fromStdWString(realPath)), m_flags(flags), + m_wsFile(file), m_wsLcFile(ToLower(file)), m_file(QString::fromStdWString(file)), m_mod(QString::fromStdWString(mod)), m_loaded(false), @@ -110,6 +113,16 @@ const QString& FileTreeItem::filename() const return m_file; } +const std::wstring& FileTreeItem::filenameWs() const +{ + return m_wsFile; +} + +const std::wstring& FileTreeItem::filenameWsLowerCase() const +{ + return m_wsLcFile; +} + const QString& FileTreeItem::mod() const { return m_mod; diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 516319ac..423038ba 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -37,6 +37,8 @@ public: const QString& virtualParentPath() const; QString virtualPath() const; const QString& filename() const; + const std::wstring& filenameWs() const; + const std::wstring& filenameWsLowerCase() const; const QString& mod() const; QFont font() const; @@ -68,6 +70,7 @@ private: QString m_virtualParentPath; QString m_realPath; Flags m_flags; + std::wstring m_wsFile, m_wsLcFile; QString m_file; QString m_mod; bool m_loaded; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 024ec6ee..b5ae9dc8 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -1,6 +1,7 @@ #include "filetreemodel.h" #include "organizercore.h" #include +#include using namespace MOBase; using namespace MOShared; @@ -247,18 +248,16 @@ void FileTreeModel::updateDirectories( int row = 0; std::vector remove; - std::set seen; + std::unordered_set seen; for (auto&& item : parentItem.children()) { if (!item->isDirectory()) { break; } - const auto name = item->filename().toStdWString(); - - if (auto d=parentEntry.findSubDirectory(name)) { + if (auto d=parentEntry.findSubDirectory(item->filenameWsLowerCase())) { // directory still exists - seen.insert(name); + seen.insert(item->filenameWs()); if (item->areChildrenVisible()) { trace([&]{ log::debug( @@ -419,7 +418,7 @@ void FileTreeModel::updateFiles( parentItem.debugName(), (path.empty() ? L"\\" : path)); }); - std::set seen; + std::unordered_set seen; std::vector remove; for (auto&& item : parentItem.children()) { @@ -427,13 +426,11 @@ void FileTreeModel::updateFiles( continue; } - const auto name = item->filename().toStdWString(); - - if (auto f=parentEntry.findFile(name)) { + if (auto f=parentEntry.findFile(item->filenameWsLowerCase(), true)) { if (shouldShowFile(*f)) { // file still exists trace([&]{ log::debug("{} still exists", item->debugName()); }); - seen.insert(name); + seen.insert(item->filenameWs()); continue; } } @@ -569,12 +566,7 @@ FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const return nullptr; } - auto* item = static_cast(data); - if (!item->debugName().isEmpty()) { - return item; - } - - return nullptr; + return static_cast(data); } QModelIndex FileTreeModel::indexFromItem( diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 146662ad..c6b29fbb 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -875,9 +875,11 @@ DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &pa } -const FileEntry::Ptr DirectoryEntry::findFile(const std::wstring &name) const +const FileEntry::Ptr DirectoryEntry::findFile( + const std::wstring &name, bool alreadyLowerCase) const { - auto iter = m_Files.find(ToLower(name)); + auto iter = m_Files.find(alreadyLowerCase ? name : ToLower(name)); + if (iter != m_Files.end()) { return m_FileRegister->getFile(iter->second); } else { diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 52265583..d33b495a 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -278,7 +278,7 @@ public: * @param name name of the file * @return fileentry object for the file or nullptr if no file matches */ - const FileEntry::Ptr findFile(const std::wstring &name) const; + const FileEntry::Ptr findFile(const std::wstring &name, bool alreadyLowerCase=false) const; bool hasFile(const std::wstring& name) const; bool containsArchive(std::wstring archiveName); -- cgit v1.3.1 From 0a908c49625fe0e54bc45e29fe8c4908d20b0dbe Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 21:13:31 -0500 Subject: added a map for directories in DirectoryEntry more optimizations for filetree --- src/filetreeitem.cpp | 103 ----------------------------------- src/filetreeitem.h | 124 +++++++++++++++++++++++++++++++++++------- src/filetreemodel.cpp | 25 ++++++--- src/shared/directoryentry.cpp | 56 +++++++++++++++---- src/shared/directoryentry.h | 19 ++++++- 5 files changed, 186 insertions(+), 141 deletions(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index c2f3a1fc..0469dfcc 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -27,11 +27,6 @@ FileTreeItem::FileTreeItem( { } -void FileTreeItem::add(std::unique_ptr child) -{ - m_children.push_back(std::move(child)); -} - void FileTreeItem::insert(std::unique_ptr child, std::size_t at) { if (at > m_children.size()) { @@ -55,26 +50,6 @@ void FileTreeItem::remove(std::size_t i) m_children.erase(m_children.begin() + i); } -const std::vector>& FileTreeItem::children() const -{ - return m_children; -} - -FileTreeItem* FileTreeItem::parent() -{ - return m_parent; -} - -int FileTreeItem::originID() const -{ - return m_originID; -} - -const QString& FileTreeItem::virtualParentPath() const -{ - return m_virtualParentPath; -} - QString FileTreeItem::virtualPath() const { QString s = "Data\\"; @@ -88,11 +63,6 @@ QString FileTreeItem::virtualPath() const return s; } -QString FileTreeItem::dataRelativeParentPath() const -{ - return m_virtualParentPath; -} - QString FileTreeItem::dataRelativeFilePath() const { auto path = dataRelativeParentPath(); @@ -103,31 +73,6 @@ QString FileTreeItem::dataRelativeFilePath() const return path += m_file; } -const QString& FileTreeItem::realPath() const -{ - return m_realPath; -} - -const QString& FileTreeItem::filename() const -{ - return m_file; -} - -const std::wstring& FileTreeItem::filenameWs() const -{ - return m_wsFile; -} - -const std::wstring& FileTreeItem::filenameWsLowerCase() const -{ - return m_wsLcFile; -} - -const QString& FileTreeItem::mod() const -{ - return m_mod; -} - QFont FileTreeItem::font() const { QFont f; @@ -150,49 +95,11 @@ QFileIconProvider::IconType FileTreeItem::icon() const } } -bool FileTreeItem::isDirectory() const -{ - return (m_flags & Directory); -} - -bool FileTreeItem::isFromArchive() const -{ - return (m_flags & FromArchive); -} - -bool FileTreeItem::isConflicted() const -{ - return (m_flags & Conflicted); -} - bool FileTreeItem::isHidden() const { return m_file.endsWith(ModInfo::s_HiddenExt); } -bool FileTreeItem::hasChildren() const -{ - if (!isDirectory()) { - return false; - } - - if (isLoaded() && m_children.empty()) { - return false; - } - - return true; -} - -void FileTreeItem::setLoaded(bool b) -{ - m_loaded = b; -} - -bool FileTreeItem::isLoaded() const -{ - return m_loaded; -} - void FileTreeItem::unload() { if (!m_loaded) { @@ -203,16 +110,6 @@ void FileTreeItem::unload() m_children.clear(); } -void FileTreeItem::setExpanded(bool b) -{ - m_expanded = b; -} - -bool FileTreeItem::isStrictlyExpanded() const -{ - return m_expanded; -} - bool FileTreeItem::areChildrenVisible() const { if (m_expanded) { diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 423038ba..1e820f3f 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -27,39 +27,125 @@ public: FileTreeItem(FileTreeItem&&) = default; FileTreeItem& operator=(FileTreeItem&&) = default; - void add(std::unique_ptr child); + void add(std::unique_ptr child) + { + m_children.push_back(std::move(child)); + } + void insert(std::unique_ptr child, std::size_t at); void remove(std::size_t i); - const std::vector>& children() const; - FileTreeItem* parent(); - int originID() const; - const QString& virtualParentPath() const; + const std::vector>& children() const + { + return m_children; + } + + + FileTreeItem* parent() + { + return m_parent; + } + + int originID() const + { + return m_originID; + } + + const QString& virtualParentPath() const + { + return m_virtualParentPath; + } + QString virtualPath() const; - const QString& filename() const; - const std::wstring& filenameWs() const; - const std::wstring& filenameWsLowerCase() const; - const QString& mod() const; + const QString& filename() const + { + return m_file; + } + + const std::wstring& filenameWs() const + { + return m_wsFile; + } + + const std::wstring& filenameWsLowerCase() const + { + return m_wsLcFile; + } + + const QString& mod() const + { + return m_mod; + } + QFont font() const; - const QString& realPath() const; - QString dataRelativeParentPath() const; + const QString& realPath() const + { + return m_realPath; + } + + const QString& dataRelativeParentPath() const + { + return m_virtualParentPath; + } + QString dataRelativeFilePath() const; QFileIconProvider::IconType icon() const; - bool isDirectory() const; - bool isFromArchive() const; - bool isConflicted() const; + bool isDirectory() const + { + return (m_flags & Directory); + } + + bool isFromArchive() const + { + return (m_flags & FromArchive); + } + + bool isConflicted() const + { + return (m_flags & Conflicted); + } + bool isHidden() const; - bool hasChildren() const; - void setLoaded(bool b); - bool isLoaded() const; + bool hasChildren() const + { + if (!isDirectory()) { + return false; + } + + if (isLoaded() && m_children.empty()) { + return false; + } + + return true; + } + + + void setLoaded(bool b) + { + m_loaded = b; + } + + bool isLoaded() const + { + return m_loaded; + } + void unload(); - void setExpanded(bool b); - bool isStrictlyExpanded() const; + void setExpanded(bool b) + { + m_expanded = b; + } + + bool isStrictlyExpanded() const + { + return m_expanded; + } + bool areChildrenVisible() const; QString debugName() const; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index b5ae9dc8..9332b354 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -255,7 +255,7 @@ void FileTreeModel::updateDirectories( break; } - if (auto d=parentEntry.findSubDirectory(item->filenameWsLowerCase())) { + if (auto d=parentEntry.findSubDirectory(item->filenameWsLowerCase(), true)) { // directory still exists seen.insert(item->filenameWs()); @@ -418,7 +418,7 @@ void FileTreeModel::updateFiles( parentItem.debugName(), (path.empty() ? L"\\" : path)); }); - std::unordered_set seen; + std::unordered_set seen; std::vector remove; for (auto&& item : parentItem.children()) { @@ -430,7 +430,7 @@ void FileTreeModel::updateFiles( if (shouldShowFile(*f)) { // file still exists trace([&]{ log::debug("{} still exists", item->debugName()); }); - seen.insert(item->filenameWs()); + seen.emplace(f->getIndex()); continue; } } @@ -483,9 +483,14 @@ void FileTreeModel::updateFiles( std::size_t insertPos = firstFile; - for (auto&& file : parentEntry.getFiles()) { - if (shouldShowFile(*file)) { - if (!seen.contains(file->getName())) { + parentEntry.forEachFileIndex([&](auto&& fileIndex) { + if (!seen.contains(fileIndex)) { + const auto& file = parentEntry.getFileByIndex(fileIndex); + if (!file) { + return true; + } + + if (shouldShowFile(*file)) { trace([&]{ log::debug( "{}: new file {}", parentItem.debugName(), QString::fromStdWString(file->getName())); @@ -532,11 +537,15 @@ void FileTreeModel::updateFiles( beginInsertRows(parentIndex, first, last); parentItem.insert(std::move(child), insertPos); endInsertRows(); + } else { + ++insertPos; } - + } else { ++insertPos; } - } + + return true; + }); } std::wstring FileTreeModel::makeModName(const FileEntry& file, int originID) const diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index c6b29fbb..97da1061 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -485,7 +485,9 @@ void DirectoryEntry::clear() for (DirectoryEntry *entry : m_SubDirectories) { delete entry; } + m_SubDirectories.clear(); + m_SubDirectoriesMap.clear(); } @@ -665,7 +667,9 @@ void DirectoryEntry::removeDirRecursive() entry->removeDirRecursive(); delete entry; } + m_SubDirectories.clear(); + m_SubDirectoriesMap.clear(); } void DirectoryEntry::removeDir(const std::wstring &path) @@ -676,6 +680,20 @@ void DirectoryEntry::removeDir(const std::wstring &path) DirectoryEntry *entry = *iter; if (CaseInsensitiveEqual(entry->getName(), path)) { entry->removeDirRecursive(); + + bool found = false; + for (auto iter2=m_SubDirectoriesMap.begin(); iter2!=m_SubDirectoriesMap.end(); ++iter2) { + if (iter2->second == entry) { + m_SubDirectoriesMap.erase(iter2); + found = true; + break; + } + } + + if (!found) { + log::error("entry {} not in sub directories map", entry->getName()); + } + m_SubDirectories.erase(iter); delete entry; break; @@ -858,14 +876,22 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const } -DirectoryEntry *DirectoryEntry::findSubDirectory(const std::wstring &name) const +DirectoryEntry *DirectoryEntry::findSubDirectory( + const std::wstring &name, bool alreadyLowerCase) const { - for (DirectoryEntry *entry : m_SubDirectories) { - if (CaseInsensitiveEqual(entry->getName(), name)) { - return entry; - } + SubDirectoriesMap::const_iterator itor; + + if (alreadyLowerCase) { + itor = m_SubDirectoriesMap.find(name); + } else { + itor = m_SubDirectoriesMap.find(ToLower(name)); + } + + if (itor == m_SubDirectoriesMap.end()) { + return nullptr; } - return nullptr; + + return itor->second; } @@ -878,7 +904,13 @@ DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &pa const FileEntry::Ptr DirectoryEntry::findFile( const std::wstring &name, bool alreadyLowerCase) const { - auto iter = m_Files.find(alreadyLowerCase ? name : ToLower(name)); + std::map::const_iterator iter; + + if (alreadyLowerCase) { + iter = m_Files.find(name); + } else { + iter = m_Files.find(ToLower(name)); + } if (iter != m_Files.end()) { return m_FileRegister->getFile(iter->second); @@ -900,9 +932,13 @@ DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool c } } if (create) { - std::vector::iterator iter = m_SubDirectories.insert(m_SubDirectories.end(), - new DirectoryEntry(name, this, originID, m_FileRegister, m_OriginConnection)); - return *iter; + auto* entry = new DirectoryEntry( + name, this, originID, m_FileRegister, m_OriginConnection); + + m_SubDirectories.push_back(entry); + m_SubDirectoriesMap.emplace(ToLower(name), entry); + + return entry; } else { return nullptr; } diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index d33b495a..79bc5cf2 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -271,7 +271,22 @@ public: } } - DirectoryEntry *findSubDirectory(const std::wstring &name) const; + template + void forEachFileIndex(F&& f) const + { + for (auto&& p : m_Files) { + if (!f(p.second)) { + break; + } + } + } + + FileEntry::Ptr getFileByIndex(FileEntry::Index index) const + { + return m_FileRegister->getFile(index); + } + + DirectoryEntry *findSubDirectory(const std::wstring &name, bool alreadyLowerCase=false) const; DirectoryEntry *findSubDirectoryRecursive(const std::wstring &path); /** retrieve a file in this directory by name. @@ -352,6 +367,7 @@ private: void removeDirRecursive(); private: + using SubDirectoriesMap = std::unordered_map; boost::shared_ptr m_FileRegister; boost::shared_ptr m_OriginConnection; @@ -359,6 +375,7 @@ private: std::wstring m_Name; std::map m_Files; std::vector m_SubDirectories; + SubDirectoriesMap m_SubDirectoriesMap; DirectoryEntry *m_Parent; std::set m_Origins; -- cgit v1.3.1 From 2be531470d54fa56307e392ad5bdfbc02048a744 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 23:00:18 -0500 Subject: renamed ToLower() to avoid confusion with in-place vs copy pre-hashed file lookup in DirectoryEntry --- src/filetree.cpp | 5 ++ src/filetree.h | 1 + src/filetreeitem.cpp | 26 ++++----- src/filetreeitem.h | 31 ++++++++--- src/filetreemodel.cpp | 40 +++++++------- src/filetreemodel.h | 13 ++++- src/organizercore.cpp | 10 ---- src/organizercore.h | 12 +++- src/shared/directoryentry.cpp | 125 +++++++++++++++++++++++++++++++++++++----- src/shared/directoryentry.h | 88 ++++++++++++++++++----------- src/shared/util.cpp | 12 ++-- src/shared/util.h | 8 +-- 12 files changed, 258 insertions(+), 113 deletions(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/filetree.cpp b/src/filetree.cpp index 8fc9e908..71a49200 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -144,6 +144,11 @@ void FileTree::refresh() m_model->refresh(); } +void FileTree::clear() +{ + m_model->clear(); +} + FileTreeItem* FileTree::singleSelection() { const auto sel = m_tree->selectionModel()->selectedRows(); diff --git a/src/filetree.h b/src/filetree.h index 77d5012c..1a17354f 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -20,6 +20,7 @@ public: FileTreeModel* model(); void refresh(); + void clear(); void open(); void openHooked(); diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 0469dfcc..39cdd9c4 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -6,24 +6,22 @@ using namespace MOBase; using namespace MOShared; -FileTreeItem::FileTreeItem() - : m_flags(NoFlags), m_loaded(false) -{ -} - FileTreeItem::FileTreeItem( FileTreeItem* parent, int originID, std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, std::wstring file, std::wstring mod) : - m_parent(parent), m_originID(originID), - m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), - m_realPath(QString::fromStdWString(realPath)), - m_flags(flags), - m_wsFile(file), m_wsLcFile(ToLower(file)), - m_file(QString::fromStdWString(file)), - m_mod(QString::fromStdWString(mod)), - m_loaded(false), - m_expanded(false) + m_parent(parent), + m_originID(originID), + m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), + m_realPath(QString::fromStdWString(realPath)), + m_flags(flags), + m_wsFile(file), + m_wsLcFile(ToLowerCopy(file)), + m_key(m_wsLcFile), + m_file(QString::fromStdWString(file)), + m_mod(QString::fromStdWString(mod)), + m_loaded(false), + m_expanded(false) { } diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 1e820f3f..fb9eccce 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -1,6 +1,7 @@ #ifndef MODORGANIZER_FILETREEITEM_INCLUDED #define MODORGANIZER_FILETREEITEM_INCLUDED +#include "directoryentry.h" #include class FileTreeItem @@ -16,7 +17,6 @@ public: Q_DECLARE_FLAGS(Flags, Flag); - FileTreeItem(); FileTreeItem( FileTreeItem* parent, int originID, std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, @@ -35,6 +35,12 @@ public: void insert(std::unique_ptr child, std::size_t at); void remove(std::size_t i); + void clear() + { + m_children.clear(); + m_loaded = false; + } + const std::vector>& children() const { return m_children; @@ -57,6 +63,7 @@ public: } QString virtualPath() const; + const QString& filename() const { return m_file; @@ -72,6 +79,11 @@ public: return m_wsLcFile; } + const MOShared::DirectoryEntry::FileKey& key() const + { + return m_key; + } + const QString& mod() const { return m_mod; @@ -152,13 +164,16 @@ public: private: FileTreeItem* m_parent; - int m_originID; - QString m_virtualParentPath; - QString m_realPath; - Flags m_flags; - std::wstring m_wsFile, m_wsLcFile; - QString m_file; - QString m_mod; + + const int m_originID; + const QString m_virtualParentPath; + const QString m_realPath; + const Flags m_flags; + const std::wstring m_wsFile, m_wsLcFile; + const MOShared::DirectoryEntry::FileKey m_key; + const QString m_file; + const QString m_mod; + bool m_loaded; bool m_expanded; std::vector> m_children; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 9332b354..ed62b8ae 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -17,9 +17,13 @@ void trace(F&&) } -FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) - : QAbstractItemModel(parent), m_core(core), m_flags(NoFlags) +FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : + QAbstractItemModel(parent), m_core(core), + m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""), + m_flags(NoFlags) { + m_root.setExpanded(true); + connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); }); connect( @@ -33,21 +37,6 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) }); } -void FileTreeModel::setFlags(Flags f) -{ - m_flags = f; -} - -bool FileTreeModel::showConflicts() const -{ - return (m_flags & Conflicts); -} - -bool FileTreeModel::showArchives() const -{ - return (m_flags & Archives) && m_core.getArchiveParsing(); -} - void FileTreeModel::refresh() { if (m_root.hasChildren()) { @@ -56,13 +45,24 @@ void FileTreeModel::refresh() } else { TimeThis tt("FileTreeModel::fill()"); beginResetModel(); - m_root = {nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""}; - m_root.setExpanded(true); + m_root.clear(); fill(m_root, *m_core.directoryStructure(), L""); endResetModel(); } } +void FileTreeModel::clear() +{ + beginResetModel(); + m_root.clear(); + endResetModel(); +} + +bool FileTreeModel::showArchives() const +{ + return (m_flags & Archives) && m_core.getArchiveParsing(); +} + void FileTreeModel::ensureLoaded(FileTreeItem* item) const { if (!item) { @@ -426,7 +426,7 @@ void FileTreeModel::updateFiles( continue; } - if (auto f=parentEntry.findFile(item->filenameWsLowerCase(), true)) { + if (auto f=parentEntry.findFile(item->key())) { if (shouldShowFile(*f)) { // file still exists trace([&]{ log::debug("{} still exists", item->debugName()); }); diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 0cfe19c7..8a1738b3 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -23,8 +23,13 @@ public: FileTreeModel(OrganizerCore& core, QObject* parent=nullptr); - void setFlags(Flags f); + void setFlags(Flags f) + { + m_flags = f; + } + void refresh(); + void clear(); QModelIndex index(int row, int col, const QModelIndex& parent={}) const override; QModelIndex parent(const QModelIndex& index) const override; @@ -53,7 +58,11 @@ private: mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; - bool showConflicts() const; + bool showConflicts() const + { + return (m_flags & Conflicts); + } + bool showArchives() const; void fill( diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 336be37d..c4f9e081 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -500,16 +500,6 @@ std::wstring OrganizerCore::crashDumpsPath() { ).toStdWString(); } -bool OrganizerCore::getArchiveParsing() const -{ - return m_ArchiveParsing; -} - -void OrganizerCore::setArchiveParsing(const bool archiveParsing) -{ - m_ArchiveParsing = archiveParsing; -} - void OrganizerCore::setCurrentProfile(const QString &profileName) { if ((m_CurrentProfile != nullptr) diff --git a/src/organizercore.h b/src/organizercore.h index 6c9edb9f..4ee6ddc5 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -218,8 +218,16 @@ public: bool onFinishedRun(const std::function &func); void refreshModList(bool saveChanges = true); QStringList modsSortedByProfilePriority() const; - bool getArchiveParsing() const; - void setArchiveParsing(bool archiveParsing); + + bool getArchiveParsing() const + { + return m_ArchiveParsing; + } + + void setArchiveParsing(bool archiveParsing) + { + m_ArchiveParsing = archiveParsing; + } public: // IPluginDiagnose interface diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 97da1061..639d6cac 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -482,6 +482,8 @@ const std::wstring &DirectoryEntry::getName() const void DirectoryEntry::clear() { m_Files.clear(); + m_FilesLookup.clear(); + for (DirectoryEntry *entry : m_SubDirectories) { delete entry; } @@ -663,6 +665,8 @@ void DirectoryEntry::removeDirRecursive() m_FileRegister->removeFile(m_Files.begin()->second); } + m_FilesLookup.clear(); + for (DirectoryEntry *entry : m_SubDirectories) { entry->removeDirRecursive(); delete entry; @@ -709,6 +713,56 @@ void DirectoryEntry::removeDir(const std::wstring &path) } } +bool DirectoryEntry::remove(const std::wstring &fileName, int *origin) +{ + const auto lcFileName = ToLowerCopy(fileName); + + auto iter = m_Files.find(lcFileName); + bool b = false; + + if (iter != m_Files.end()) { + if (origin != nullptr) { + FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); + if (entry.get() != nullptr) { + bool ignore; + *origin = entry->getOrigin(ignore); + } + } + + b = m_FileRegister->removeFile(iter->second); + } + + if (m_Files.size() != m_FilesLookup.size()) { + DebugBreak(); + } + + return b; +} + +void DirectoryEntry::insert( + const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, + const std::wstring &archive, int order) +{ + std::wstring fileNameLower = ToLowerCopy(fileName); + auto iter = m_Files.find(fileNameLower); + FileEntry::Ptr file; + + if (iter != m_Files.end()) { + file = m_FileRegister->getFile(iter->second); + } else { + file = m_FileRegister->createFile(fileName, this); + m_Files.emplace(fileNameLower, file->getIndex()); + m_FilesLookup.emplace(fileNameLower, file->getIndex()); + } + + if (m_Files.size() != m_FilesLookup.size()) { + DebugBreak(); + } + + file->addOrigin(origin.getID(), fileTime, archive, order); + origin.addFile(file->getIndex()); +} + bool DirectoryEntry::hasContentsFromOrigin(int originID) const { return m_Origins.find(originID) != m_Origins.end(); @@ -726,13 +780,34 @@ void DirectoryEntry::insertFile(const std::wstring &filePath, FilesOrigin &origi } } - void DirectoryEntry::removeFile(FileEntry::Index index) { + if (!m_FilesLookup.empty()) { + auto iter = std::find_if( + m_FilesLookup.begin(), m_FilesLookup.end(), + [&index](auto&& pair) { return (pair.second == index); } + ); + + if (iter != m_FilesLookup.end()) { + m_FilesLookup.erase(iter); + } else { + log::error( + "file \"{}\" not in directory for lookup \"{}\"", + m_FileRegister->getFile(index)->getName(), this->getName()); + } + } else { + log::error( + "file \"{}\" not in directory \"{}\" for lookup, directory empty", + m_FileRegister->getFile(index)->getName(), this->getName()); + } + if (!m_Files.empty()) { - auto iter = std::find_if(m_Files.begin(), m_Files.end(), - [&index](const std::pair &iter) -> bool { - return iter.second == index; } ); + auto iter = std::find_if( + m_Files.begin(), m_Files.end(), + [&index](const std::pair &iter) -> bool { + return iter.second == index; } + ); + if (iter != m_Files.end()) { m_Files.erase(iter); } else { @@ -745,14 +820,25 @@ void DirectoryEntry::removeFile(FileEntry::Index index) QObject::tr("file \"{}\" not in directory \"{}\", directory empty").toStdString(), m_FileRegister->getFile(index)->getName(), this->getName()); } -} + if (m_Files.size() != m_FilesLookup.size()) { + DebugBreak(); + } +} void DirectoryEntry::removeFiles(const std::set &indices) { for (auto iter = m_Files.begin(); iter != m_Files.end();) { if (indices.find(iter->second) != indices.end()) { - m_Files.erase(iter++); + iter = m_Files.erase(iter); + } else { + ++iter; + } + } + + for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) { + if (indices.find(iter->second) != indices.end()) { + iter = m_FilesLookup.erase(iter); } else { ++iter; } @@ -851,7 +937,7 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const if (len == std::string::npos) { // no more path components - auto iter = m_Files.find(ToLower(path)); + auto iter = m_Files.find(ToLowerCopy(path)); if (iter != m_Files.end()) { return m_FileRegister->getFile(iter->second); } else if (directory != nullptr) { @@ -884,7 +970,7 @@ DirectoryEntry *DirectoryEntry::findSubDirectory( if (alreadyLowerCase) { itor = m_SubDirectoriesMap.find(name); } else { - itor = m_SubDirectoriesMap.find(ToLower(name)); + itor = m_SubDirectoriesMap.find(ToLowerCopy(name)); } if (itor == m_SubDirectoriesMap.end()) { @@ -904,15 +990,26 @@ DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &pa const FileEntry::Ptr DirectoryEntry::findFile( const std::wstring &name, bool alreadyLowerCase) const { - std::map::const_iterator iter; + FilesLookup::const_iterator iter; if (alreadyLowerCase) { - iter = m_Files.find(name); + iter = m_FilesLookup.find(FileKey(name)); } else { - iter = m_Files.find(ToLower(name)); + iter = m_FilesLookup.find(FileKey(ToLowerCopy(name))); } - if (iter != m_Files.end()) { + if (iter != m_FilesLookup.end()) { + return m_FileRegister->getFile(iter->second); + } else { + return FileEntry::Ptr(); + } +} + +const FileEntry::Ptr DirectoryEntry::findFile(const FileKey& key) const +{ + auto iter = m_FilesLookup.find(key); + + if (iter != m_FilesLookup.end()) { return m_FileRegister->getFile(iter->second); } else { return FileEntry::Ptr(); @@ -921,7 +1018,7 @@ const FileEntry::Ptr DirectoryEntry::findFile( bool DirectoryEntry::hasFile(const std::wstring& name) const { - return m_Files.contains(ToLower(name)); + return m_Files.contains(ToLowerCopy(name)); } DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool create, int originID) @@ -936,7 +1033,7 @@ DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool c name, this, originID, m_FileRegister, m_OriginConnection); m_SubDirectories.push_back(entry); - m_SubDirectoriesMap.emplace(ToLower(name), entry); + m_SubDirectoriesMap.emplace(ToLowerCopy(name), entry); return entry; } else { diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 79bc5cf2..fc68cae7 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -35,6 +35,20 @@ along with Mod Organizer. If not, see . #endif #include "util.h" +namespace MOShared { struct DirectoryEntryFileKey; } + +namespace std +{ + template <> + struct hash + { + using argument_type = MOShared::DirectoryEntryFileKey; + using result_type = std::size_t; + + inline result_type operator()(const argument_type& key) const; + }; +} + namespace MOShared { @@ -203,9 +217,32 @@ private: }; +struct DirectoryEntryFileKey +{ + DirectoryEntryFileKey(std::wstring v) + : value(std::move(v)), hash(getHash(value)) + { + } + + bool operator==(const DirectoryEntryFileKey& o) const + { + return (value == o.value); + } + + static std::size_t getHash(const std::wstring& value) + { + return std::hash()(value); + } + + const std::wstring value; + const std::size_t hash; +}; + + class DirectoryEntry { public: + using FileKey = DirectoryEntryFileKey; DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID); @@ -294,6 +331,7 @@ public: * @return fileentry object for the file or nullptr if no file matches */ const FileEntry::Ptr findFile(const std::wstring &name, bool alreadyLowerCase=false) const; + const FileEntry::Ptr findFile(const FileKey& key) const; bool hasFile(const std::wstring& name) const; bool containsArchive(std::wstring archiveName); @@ -315,21 +353,7 @@ public: */ void removeDir(const std::wstring &path); - bool remove(const std::wstring &fileName, int *origin) { - auto iter = m_Files.find(ToLower(fileName)); - if (iter != m_Files.end()) { - if (origin != nullptr) { - FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); - if (entry.get() != nullptr) { - bool ignore; - *origin = entry->getOrigin(ignore); - } - } - return m_FileRegister->removeFile(iter->second); - } else { - return false; - } - } + bool remove(const std::wstring &fileName, int *origin); bool hasContentsFromOrigin(int originID) const; @@ -342,20 +366,9 @@ private: DirectoryEntry(const DirectoryEntry &reference); DirectoryEntry &operator=(const DirectoryEntry &reference); - void insert(const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) { - std::wstring fileNameLower = ToLower(fileName); - auto iter = m_Files.find(fileNameLower); - FileEntry::Ptr file; - if (iter != m_Files.end()) { - file = m_FileRegister->getFile(iter->second); - } else { - file = m_FileRegister->createFile(fileName, this); - // TODO this has been observed to cause a crash, no clue why - m_Files[fileNameLower] = file->getIndex(); - } - file->addOrigin(origin.getID(), fileTime, archive, order); - origin.addFile(file->getIndex()); - } + void insert( + const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, + const std::wstring &archive, int order); void addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset); void addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName, int order); @@ -367,13 +380,16 @@ private: void removeDirRecursive(); private: + using FilesMap = std::map; + using FilesLookup = std::unordered_map; using SubDirectoriesMap = std::unordered_map; boost::shared_ptr m_FileRegister; boost::shared_ptr m_OriginConnection; std::wstring m_Name; - std::map m_Files; + FilesMap m_Files; + FilesLookup m_FilesLookup; std::vector m_SubDirectories; SubDirectoriesMap m_SubDirectoriesMap; @@ -386,7 +402,17 @@ private: }; - } // namespace MOShared + +namespace std +{ + hash::result_type + hash::operator()( + const argument_type& key) const + { + return key.hash; + } +} + #endif // DIRECTORYENTRY_H diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 4ac95465..009aad70 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -102,32 +102,28 @@ static auto locToLower = [] (char in) -> char { return std::tolower(in, loc); }; -std::string &ToLower(std::string &text) +std::string& ToLowerInPlace(std::string& text) { - //std::transform(text.begin(), text.end(), text.begin(), locToLower); CharLowerBuffA(const_cast(text.c_str()), static_cast(text.size())); return text; } -std::string ToLower(const std::string &text) +std::string ToLowerCopy(const std::string& text) { std::string result(text); - //std::transform(result.begin(), result.end(), result.begin(), locToLower); CharLowerBuffA(const_cast(result.c_str()), static_cast(result.size())); return result; } -std::wstring &ToLower(std::wstring &text) +std::wstring& ToLowerInPlace(std::wstring& text) { - //std::transform(text.begin(), text.end(), text.begin(), locToLowerW); CharLowerBuffW(const_cast(text.c_str()), static_cast(text.size())); return text; } -std::wstring ToLower(const std::wstring &text) +std::wstring ToLowerCopy(const std::wstring& text) { std::wstring result(text); - //std::transform(result.begin(), result.end(), result.begin(), locToLowerW); CharLowerBuffW(const_cast(result.c_str()), static_cast(result.size())); return result; } diff --git a/src/shared/util.h b/src/shared/util.h index 788d2444..79cadf71 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -36,11 +36,11 @@ bool FileExists(const std::wstring &searchPath, const std::wstring &filename); std::string ToString(const std::wstring &source, bool utf8); std::wstring ToWString(const std::string &source, bool utf8); -std::string &ToLower(std::string &text); -std::string ToLower(const std::string &text); +std::string& ToLowerInPlace(std::string& text); +std::string ToLowerCopy(const std::string& text); -std::wstring &ToLower(std::wstring &text); -std::wstring ToLower(const std::wstring &text); +std::wstring& ToLowerInPlace(std::wstring& text); +std::wstring ToLowerCopy(const std::wstring& text); bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs); -- cgit v1.3.1 From 6da813a6330b576bcf565ff397ee6eaae1aaaac4 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 23:11:04 -0500 Subject: refactoring: whitespace and newlines --- src/shared/directoryentry.cpp | 10 +- src/shared/directoryentry.h | 270 ++++++++++++++++++++++++++---------------- 2 files changed, 174 insertions(+), 106 deletions(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 639d6cac..a3a1459c 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -358,14 +358,16 @@ bool FileEntry::removeOrigin(int origin) return false; } -FileEntry::FileEntry() - : m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr), m_LastAccessed(time(nullptr)) +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)) +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; } diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index fc68cae7..bd72c208 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -50,28 +50,23 @@ namespace std } -namespace MOShared { - +namespace MOShared +{ class DirectoryEntry; class OriginConnection; class FileRegister; -class FileEntry { - +class FileEntry +{ public: - typedef unsigned int Index; typedef boost::shared_ptr Ptr; typedef std::vector>> AlternativesVector; -public: - FileEntry(); - FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent); - ~FileEntry(); Index getIndex() const { return m_Index; } @@ -79,38 +74,64 @@ public: time_t lastAccessed() const { return m_LastAccessed; } void addOrigin(int origin, FILETIME fileTime, const std::wstring &archive, int order); + // remove the specified origin from the list of origins that contain this file. if no origin is left, // the file is effectively deleted and true is returned. otherwise, false is returned bool removeOrigin(int origin); + void sortOrigins(); // gets the list of alternative origins (origins with lower priority than the primary one). // if sortOrigins has been called, it is sorted by priority (ascending) - const AlternativesVector &getAlternatives() const { return m_Alternatives; } + const AlternativesVector &getAlternatives() const + { + return m_Alternatives; + } + + const std::wstring &getName() const + { + return m_Name; + } + + int getOrigin() const + { + return m_Origin; + } + + int getOrigin(bool &archive) const + { + archive = (m_Archive.first.length() != 0); + return m_Origin; + } + + const std::pair &getArchive() const + { + return m_Archive; + } - const std::wstring &getName() const { return m_Name; } - int getOrigin() const { return m_Origin; } - int getOrigin(bool &archive) const { archive = (m_Archive.first.length() != 0); return m_Origin; } - const std::pair &getArchive() const { return m_Archive; } bool isFromArchive(std::wstring archiveName = L"") const; std::wstring getFullPath() const; std::wstring getRelativePath() const; - DirectoryEntry *getParent() { return m_Parent; } - - void setFileTime(FILETIME fileTime) const { m_FileTime = fileTime; } - FILETIME getFileTime() const { return m_FileTime; } -private: + DirectoryEntry *getParent() + { + return m_Parent; + } - bool recurseParents(std::wstring &path, const DirectoryEntry *parent) const; + void setFileTime(FILETIME fileTime) const + { + m_FileTime = fileTime; + } - void determineTime(); + FILETIME getFileTime() const + { + return m_FileTime; + } private: - Index m_Index; std::wstring m_Name; - int m_Origin = -1; + int m_Origin; std::pair m_Archive; AlternativesVector m_Alternatives; DirectoryEntry *m_Parent; @@ -118,59 +139,66 @@ private: time_t m_LastAccessed; - friend bool operator<(const FileEntry &lhs, const FileEntry &rhs) { - return _wcsicmp(lhs.m_Name.c_str(), rhs.m_Name.c_str()) < 0; - } - friend bool operator==(const FileEntry &lhs, const FileEntry &rhs) { - return _wcsicmp(lhs.m_Name.c_str(), rhs.m_Name.c_str()) == 0; - } + bool recurseParents(std::wstring &path, const DirectoryEntry *parent) const; }; // represents a mod or the data directory, providing files to the tree -class FilesOrigin { +class FilesOrigin +{ friend class OriginConnection; -public: +public: FilesOrigin(); FilesOrigin(const FilesOrigin &reference); ~FilesOrigin(); - // sets priority for this origin, but it will overwrite the exisiting mapping for this priority, - // the previous origin will no longer be referenced + // sets priority for this origin, but it will overwrite the existing mapping + // for this priority, the previous origin will no longer be referenced void setPriority(int priority); - int getPriority() const { return m_Priority; } + int getPriority() const + { + return m_Priority; + } void setName(const std::wstring &name); - const std::wstring &getName() const { return m_Name; } + const std::wstring &getName() const + { + return m_Name; + } - int getID() const { return m_ID; } - const std::wstring &getPath() const { return m_Path; } + int getID() const + { + return m_ID; + } + + const std::wstring &getPath() const + { + return m_Path; + } std::vector getFiles() const; FileEntry::Ptr findFile(FileEntry::Index index) const; void enable(bool enabled, time_t notAfter = LONG_MAX); - bool isDisabled() const { return m_Disabled; } + bool isDisabled() const + { + return m_Disabled; + } + + void addFile(FileEntry::Index index) + { + m_Files.insert(index); + } - void addFile(FileEntry::Index index) { m_Files.insert(index); } void removeFile(FileEntry::Index index); bool containsArchive(std::wstring archiveName); private: - - FilesOrigin(int ID, const std::wstring &name, const std::wstring &path, int priority, - boost::shared_ptr fileRegister, boost::shared_ptr originConnection); - - -private: - int m_ID; - bool m_Disabled; - std::set m_Files; std::wstring m_Name; std::wstring m_Path; @@ -178,14 +206,16 @@ private: boost::weak_ptr m_FileRegister; boost::weak_ptr m_OriginConnection; + FilesOrigin( + int ID, const std::wstring &name, const std::wstring &path, int priority, + boost::shared_ptr fileRegister, + boost::shared_ptr originConnection); }; class FileRegister { - public: - FileRegister(boost::shared_ptr originConnection); ~FileRegister(); @@ -194,7 +224,10 @@ public: FileEntry::Ptr createFile(const std::wstring &name, DirectoryEntry *parent); FileEntry::Ptr getFile(FileEntry::Index index) const; - size_t size() const { return m_Files.size(); } + size_t size() const + { + return m_Files.size(); + } bool removeFile(FileEntry::Index index); void removeOrigin(FileEntry::Index index, int originID); @@ -203,17 +236,11 @@ public: void sortOrigins(); private: - - FileEntry::Index generateIndex(); - - void unregisterFile(FileEntry::Ptr file); - -private: - std::map m_Files; - boost::shared_ptr m_OriginConnection; + FileEntry::Index generateIndex(); + void unregisterFile(FileEntry::Ptr file); }; @@ -244,32 +271,61 @@ class DirectoryEntry public: using FileKey = DirectoryEntryFileKey; - DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID); + DirectoryEntry( + const std::wstring &name, DirectoryEntry *parent, int originID); - DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID, - boost::shared_ptr fileRegister, - boost::shared_ptr originConnection); + DirectoryEntry( + const std::wstring &name, DirectoryEntry *parent, int originID, + boost::shared_ptr fileRegister, + boost::shared_ptr originConnection); ~DirectoryEntry(); void clear(); - bool isPopulated() const { return m_Populated; } - bool isTopLevel() const { return m_TopLevel; } - bool isEmpty() const { return m_Files.empty() && m_SubDirectories.empty(); } - bool hasFiles() const { return !m_Files.empty(); } + bool isPopulated() const + { + return m_Populated; + } - const DirectoryEntry *getParent() const { return m_Parent; } + bool isTopLevel() const + { + return m_TopLevel; + } + + bool isEmpty() const + { + return m_Files.empty() && m_SubDirectories.empty(); + } - // add files to this directory (and subdirectories) from the specified origin. That origin may exist or not - void addFromOrigin(const std::wstring &originName, const std::wstring &directory, int priority); - void addFromBSA(const std::wstring &originName, std::wstring &directory, const std::wstring &fileName, int priority, int order); + bool hasFiles() const + { + return !m_Files.empty(); + } + + const DirectoryEntry *getParent() const + { + return m_Parent; + } + + // add files to this directory (and subdirectories) from the specified origin. + // That origin may exist or not + void addFromOrigin( + const std::wstring &originName, + const std::wstring &directory, int priority); + + void addFromBSA( + const std::wstring &originName, std::wstring &directory, + const std::wstring &fileName, int priority, int order); void propagateOrigin(int origin); const std::wstring &getName() const; - boost::shared_ptr getFileRegister() { return m_FileRegister; } + boost::shared_ptr getFileRegister() + { + return m_FileRegister; + } bool originExists(const std::wstring &name) const; FilesOrigin &getOriginByID(int ID) const; @@ -277,12 +333,12 @@ public: int anyOrigin() const; - //int getOrigin(const std::wstring &path, bool &archive); - std::vector getFiles() const; - void getSubDirectories(std::vector::const_iterator &begin - , std::vector::const_iterator &end) const { + void getSubDirectories( + std::vector::const_iterator &begin, + std::vector::const_iterator &end) const + { begin = m_SubDirectories.begin(); end = m_SubDirectories.end(); } @@ -323,7 +379,9 @@ public: return m_FileRegister->getFile(index); } - DirectoryEntry *findSubDirectory(const std::wstring &name, bool alreadyLowerCase=false) const; + DirectoryEntry *findSubDirectory( + const std::wstring &name, bool alreadyLowerCase=false) const; + DirectoryEntry *findSubDirectoryRecursive(const std::wstring &path); /** retrieve a file in this directory by name. @@ -332,19 +390,24 @@ public: */ 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 hasFile(const std::wstring& name) const; bool containsArchive(std::wstring archiveName); - /** search through this directory and all subdirectories for a file by the specified name (relative path). - if directory is not nullptr, the referenced variable will be set to the path containing the file */ + // search through this directory and all subdirectories for a file by the + // specified name (relative path). + // + // if directory is not nullptr, the referenced variable will be set to the + // path containing the file + // const FileEntry::Ptr searchFile(const std::wstring &path, const DirectoryEntry **directory) const; void insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime); void removeFile(FileEntry::Index index); - // remove the specified file from the tree. This can be a path leading to a file in a subdirectory + // remove the specified file from the tree. This can be a path leading to a + // file in a subdirectory bool removeFile(const std::wstring &filePath, int *origin = nullptr); /** @@ -357,28 +420,12 @@ public: bool hasContentsFromOrigin(int originID) const; - FilesOrigin &createOrigin(const std::wstring &originName, const std::wstring &directory, int priority); + FilesOrigin &createOrigin( + const std::wstring &originName, + const std::wstring &directory, int priority); void removeFiles(const std::set &indices); -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); - - 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); - - DirectoryEntry *getSubDirectory(const std::wstring &name, bool create, int originID = -1); - - DirectoryEntry *getSubDirectoryRecursive(const std::wstring &path, bool create, int originID = -1); - - void removeDirRecursive(); - private: using FilesMap = std::map; using FilesLookup = std::unordered_map; @@ -395,11 +442,30 @@ private: DirectoryEntry *m_Parent; std::set m_Origins; - bool m_Populated; - bool m_TopLevel; + + DirectoryEntry(const DirectoryEntry &reference); + + 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); + + DirectoryEntry *getSubDirectory( + const std::wstring &name, bool create, int originID = -1); + + DirectoryEntry *getSubDirectoryRecursive( + const std::wstring &path, bool create, int originID = -1); + + void removeDirRecursive(); }; } // namespace MOShared -- cgit v1.3.1 From 1c07f8ddda94254ab4f0e985ffb3e5fdea2da921 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 23:30:00 -0500 Subject: refactoring: whitespace, newlines, auto, removed commented out code --- src/shared/directoryentry.cpp | 491 +++++++++++++++++++++++------------------- 1 file changed, 275 insertions(+), 216 deletions(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index a3a1459c..3b98b7ff 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -34,21 +34,43 @@ along with Mod Organizer. If not, see . #include #include +namespace MOShared +{ -namespace MOShared { +using namespace MOBase; +static const int MAXPATH_UNICODE = 32767; -namespace log = MOBase::log; +static std::wstring tail(const std::wstring &source, const size_t count) +{ + if (count >= source.length()) { + return source; + } -static const int MAXPATH_UNICODE = 32767; + return source.substr(source.length() - count); +} -class OriginConnection { +static bool SupportOptimizedFind() +{ + // large fetch and basic info for FindFirstFileEx is supported on win server 2008 r2, win 7 and newer -public: + OSVERSIONINFOEX versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + versionInfo.dwMajorVersion = 6; + versionInfo.dwMinorVersion = 1; - typedef int Index; - static const int INVALID_INDEX = INT_MIN; + ULONGLONG mask = ::VerSetConditionMask( + ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_GREATER_EQUAL); + + return (::VerifyVersionInfo(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, mask) == TRUE); +} + +class OriginConnection +{ public: + typedef int Index; + static const int INVALID_INDEX = INT_MIN; OriginConnection() : m_NextID(0) @@ -61,25 +83,34 @@ public: LEAK_UNTRACE; } - FilesOrigin& createOrigin(const std::wstring &originName, const std::wstring &directory, int priority, - boost::shared_ptr fileRegister, boost::shared_ptr originConnection) { + FilesOrigin& createOrigin( + const std::wstring &originName, const std::wstring &directory, int priority, + boost::shared_ptr fileRegister, + boost::shared_ptr originConnection) + { int newID = createID(); + m_Origins[newID] = FilesOrigin(newID, originName, directory, priority, fileRegister, originConnection); m_OriginsNameMap[originName] = newID; m_OriginsPriorityMap[priority] = newID; + return m_Origins[newID]; } - bool exists(const std::wstring &name) { + bool exists(const std::wstring &name) + { return m_OriginsNameMap.find(name) != m_OriginsNameMap.end(); } - FilesOrigin &getByID(Index ID) { + FilesOrigin &getByID(Index ID) + { return m_Origins[ID]; } - FilesOrigin &getByName(const std::wstring &name) { + FilesOrigin &getByName(const std::wstring &name) + { std::map::iterator iter = m_OriginsNameMap.find(name); + if (iter != m_OriginsNameMap.end()) { return m_Origins[iter->second]; } else { @@ -92,6 +123,7 @@ public: void changePriorityLookup(int oldPriority, int newPriority) { auto iter = m_OriginsPriorityMap.find(oldPriority); + if (iter != m_OriginsPriorityMap.end()) { Index idx = iter->second; m_OriginsPriorityMap.erase(iter); @@ -102,6 +134,7 @@ public: void changeNameLookup(const std::wstring &oldName, const std::wstring &newName) { auto iter = m_OriginsNameMap.find(oldName); + if (iter != m_OriginsNameMap.end()) { Index idx = iter->second; m_OriginsNameMap.erase(iter); @@ -112,56 +145,16 @@ public: } private: - - Index createID() { - return m_NextID++; - } - -private: - Index m_NextID; - std::map m_Origins; std::map m_OriginsNameMap; std::map m_OriginsPriorityMap; -}; - - -// -// FilesOrigin -// - - -void FilesOrigin::enable(bool enabled, time_t notAfter) -{ - if (!enabled) { - std::set copy = m_Files; - m_FileRegister.lock()->removeOriginMulti(copy, m_ID, notAfter); - m_Files.clear(); - } - m_Disabled = !enabled; -} - - -void FilesOrigin::removeFile(FileEntry::Index index) -{ - auto iter = m_Files.find(index); - if (iter != m_Files.end()) { - m_Files.erase(iter); - } -} - - - -static std::wstring tail(const std::wstring &source, const size_t count) -{ - if (count >= source.length()) { - return source; + Index createID() + { + return m_NextID++; } - - return source.substr(source.length() - count); -} +}; FilesOrigin::FilesOrigin() @@ -183,9 +176,13 @@ FilesOrigin::FilesOrigin(const FilesOrigin &reference) } -FilesOrigin::FilesOrigin(int ID, const std::wstring &name, const std::wstring &path, int priority, boost::shared_ptr fileRegister, boost::shared_ptr originConnection) - : m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path), m_Priority(priority), - m_FileRegister(fileRegister), m_OriginConnection(originConnection) +FilesOrigin::FilesOrigin( + int ID, const std::wstring &name, const std::wstring &path, int priority, + boost::shared_ptr fileRegister, + boost::shared_ptr originConnection) : + m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path), + m_Priority(priority), m_FileRegister(fileRegister), + m_OriginConnection(originConnection) { LEAK_TRACE; } @@ -195,7 +192,6 @@ FilesOrigin::~FilesOrigin() LEAK_UNTRACE; } - void FilesOrigin::setPriority(int priority) { m_OriginConnection.lock()->changePriorityLookup(m_Priority, priority); @@ -203,23 +199,27 @@ void FilesOrigin::setPriority(int priority) m_Priority = priority; } - void FilesOrigin::setName(const std::wstring &name) { m_OriginConnection.lock()->changeNameLookup(m_Name, name); + // change path too if (tail(m_Path, m_Name.length()) == m_Name) { m_Path = m_Path.substr(0, m_Path.length() - m_Name.length()).append(name); } + m_Name = name; } std::vector FilesOrigin::getFiles() const { std::vector result; - for (FileEntry::Index fileIdx : m_Files) - if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) + + for (FileEntry::Index fileIdx : m_Files) { + if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) { result.push_back(p); + } + } return result; } @@ -229,67 +229,101 @@ FileEntry::Ptr FilesOrigin::findFile(FileEntry::Index index) const return m_FileRegister.lock()->getFile(index); } +void FilesOrigin::enable(bool enabled, time_t notAfter) +{ + if (!enabled) { + std::set copy = m_Files; + m_FileRegister.lock()->removeOriginMulti(copy, m_ID, notAfter); + m_Files.clear(); + } + + m_Disabled = !enabled; +} + +void FilesOrigin::removeFile(FileEntry::Index index) +{ + auto iter = m_Files.find(index); + + if (iter != m_Files.end()) { + m_Files.erase(iter); + } +} + bool FilesOrigin::containsArchive(std::wstring archiveName) { - for (FileEntry::Index fileIdx : m_Files) - if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) - if (p->isFromArchive(archiveName)) return true; + for (FileEntry::Index fileIdx : m_Files) { + if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) { + if (p->isFromArchive(archiveName)) { + return true; + } + } + } + return false; } -// -// FileEntry -// -void FileEntry::addOrigin(int origin, FILETIME fileTime, const std::wstring &archive, int order) +void FileEntry::addOrigin( + int origin, FILETIME fileTime, const std::wstring &archive, int order) { m_LastAccessed = time(nullptr); if (m_Parent != nullptr) { m_Parent->propagateOrigin(origin); } - // If this file has no previous origin, this mod is now the origin with no alternatives if (m_Origin == -1) { + // If this file has no previous origin, this mod is now the origin with no + // alternatives m_Origin = origin; m_FileTime = fileTime; m_Archive = std::pair(archive, order); } - - // If this mod has a higher priority than the origin mod OR - // this mod has a loose file and the origin mod has an archived file, - // this mod is now the origin and the previous origin is the first alternative - else if ((m_Parent != nullptr) - && ((m_Parent->getOriginByID(origin).getPriority() > m_Parent->getOriginByID(m_Origin).getPriority()) - || (archive.size() == 0 && m_Archive.first.size() > 0 ))) { - if (std::find_if(m_Alternatives.begin(), m_Alternatives.end(), [&](const std::pair> &i) -> bool { return i.first == m_Origin; }) == m_Alternatives.end()) { - m_Alternatives.push_back(std::pair>(m_Origin, m_Archive)); + else if ( + (m_Parent != nullptr) && ( + (m_Parent->getOriginByID(origin).getPriority() > m_Parent->getOriginByID(m_Origin).getPriority()) || + (archive.size() == 0 && m_Archive.first.size() > 0 )) + ) { + // If this mod has a higher priority than the origin mod OR + // this mod has a loose file and the origin mod has an archived file, + // this mod is now the origin and the previous origin is the first alternative + + auto itor = std::find_if( + m_Alternatives.begin(), m_Alternatives.end(), + [&](auto&& i) { return i.first == m_Origin; }); + + if (itor == m_Alternatives.end()) { + m_Alternatives.push_back({m_Origin, m_Archive}); } + m_Origin = origin; m_FileTime = fileTime; m_Archive = std::pair(archive, order); } - - // This mod is just an alternative else { + // This mod is just an alternative bool found = false; + if (m_Origin == origin) { // already an origin return; } - for (std::vector>>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) { + + for (auto iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) { if (iter->first == origin) { // already an origin return; } - if ((m_Parent != nullptr) - && (m_Parent->getOriginByID(iter->first).getPriority() < m_Parent->getOriginByID(origin).getPriority())) { - m_Alternatives.insert(iter, std::pair>(origin, std::pair(archive, order))); + + if ((m_Parent != nullptr) && + (m_Parent->getOriginByID(iter->first).getPriority() < m_Parent->getOriginByID(origin).getPriority())) { + m_Alternatives.insert(iter, {origin, {archive, order}}); found = true; break; } } + if (!found) { - m_Alternatives.push_back(std::pair>(origin, std::pair(archive, order))); + m_Alternatives.push_back({origin, {archive, order}}); } } } @@ -299,10 +333,9 @@ bool FileEntry::removeOrigin(int origin) if (m_Origin == origin) { if (!m_Alternatives.empty()) { // find alternative with the highest priority - std::vector>>::iterator currentIter = m_Alternatives.begin(); - for (std::vector>>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) { + auto currentIter = m_Alternatives.begin(); + for (auto iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) { if (iter->first != origin) { - //Both files are not from archives. if (!iter->second.first.size() && !currentIter->second.first.size()) { if ((m_Parent->getOriginByID(iter->first).getPriority() > m_Parent->getOriginByID(currentIter->first).getPriority())) { @@ -325,35 +358,25 @@ bool FileEntry::removeOrigin(int origin) } } } + int currentID = currentIter->first; m_Archive = currentIter->second; m_Alternatives.erase(currentIter); m_Origin = currentID; - - // now we need to update the file time... - //std::wstring filePath = getFullPath(); - //HANDLE file = ::CreateFile(filePath.c_str(), GENERIC_READ | GENERIC_WRITE, - // 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - //if (!::GetFileTime(file, nullptr, nullptr, &m_FileTime)) { - // maybe this file is in a bsa, but there is no easy way to find out which. User should refresh - // the view to find out - //m_Archive = std::pair(L"bsa?", -1); - //} else { - //m_Archive = std::pair(L"", -1); - //} - - //::CloseHandle(file); - } else { m_Origin = -1; m_Archive = std::pair(L"", -1); return true; } } else { - auto newEnd = std::remove_if(m_Alternatives.begin(), m_Alternatives.end(), [&](auto &i) -> bool { return i.first == origin; }); - if (newEnd != m_Alternatives.end()) + auto newEnd = std::remove_if( + m_Alternatives.begin(), m_Alternatives.end(), + [&](auto &i) { return i.first == origin; }); + + if (newEnd != m_Alternatives.end()) { m_Alternatives.erase(newEnd, m_Alternatives.end()); + } } return false; } @@ -379,11 +402,19 @@ FileEntry::~FileEntry() void FileEntry::sortOrigins() { - m_Alternatives.push_back(std::pair>(m_Origin, m_Archive)); - std::sort(m_Alternatives.begin(), m_Alternatives.end(), [&](const std::pair> &LHS, const std::pair> &RHS) -> bool { + m_Alternatives.push_back({m_Origin, m_Archive}); + + std::sort(m_Alternatives.begin(), m_Alternatives.end(), [&](auto&& LHS, auto&& RHS) { if (!LHS.second.first.size() && !RHS.second.first.size()) { - int l = m_Parent->getOriginByID(LHS.first).getPriority(); if (l < 0) l = INT_MAX; - int r = m_Parent->getOriginByID(RHS.first).getPriority(); if (r < 0) r = INT_MAX; + int l = m_Parent->getOriginByID(LHS.first).getPriority(); + if (l < 0) { + l = INT_MAX; + } + + int r = m_Parent->getOriginByID(RHS.first).getPriority(); + if (r < 0) { + r = INT_MAX; + } return l < r; } @@ -395,9 +426,13 @@ void FileEntry::sortOrigins() return l < r; } - if (RHS.second.first.size()) return false; + if (RHS.second.first.size()) { + return false; + } + return true; }); + if (!m_Alternatives.empty()) { m_Origin = m_Alternatives.back().first; m_Archive = m_Alternatives.back().second; @@ -405,7 +440,6 @@ void FileEntry::sortOrigins() } } - bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) const { if (parent == nullptr) { @@ -415,42 +449,57 @@ bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) if (recurseParents(path, parent->getParent())) { path.append(L"\\").append(parent->getName()); } + return true; } } std::wstring FileEntry::getFullPath() const { - std::wstring result; bool ignore = false; - result = m_Parent->getOriginByID(getOrigin(ignore)).getPath(); //base directory for origin - recurseParents(result, m_Parent); // all intermediate directories + + // base directory for origin + std::wstring result = m_Parent->getOriginByID(getOrigin(ignore)).getPath(); + + // all intermediate directories + recurseParents(result, m_Parent); + return result + L"\\" + m_Name; } std::wstring FileEntry::getRelativePath() const { std::wstring result; - recurseParents(result, m_Parent); // all intermediate directories + + // all intermediate directories + recurseParents(result, m_Parent); + return result + L"\\" + m_Name; } bool FileEntry::isFromArchive(std::wstring archiveName) const { - if (archiveName.length() == 0) return m_Archive.first.length() != 0; - if (m_Archive.first.compare(archiveName) == 0) return true; + if (archiveName.length() == 0) { + return m_Archive.first.length() != 0; + } + + if (m_Archive.first.compare(archiveName) == 0) { + return true; + } + for (auto alternative : m_Alternatives) { - if (alternative.second.first.compare(archiveName) == 0) return true; + if (alternative.second.first.compare(archiveName) == 0) { + return true; + } } + return false; } -// -// DirectoryEntry -// -DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID) - : m_OriginConnection(new OriginConnection), +DirectoryEntry::DirectoryEntry( + const std::wstring &name, DirectoryEntry *parent, int originID) : + m_OriginConnection(new OriginConnection), m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(true) { m_FileRegister.reset(new FileRegister(m_OriginConnection)); @@ -458,29 +507,28 @@ DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, LEAK_TRACE; } -DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID, - boost::shared_ptr fileRegister, boost::shared_ptr originConnection) - : m_FileRegister(fileRegister), m_OriginConnection(originConnection), +DirectoryEntry::DirectoryEntry( + const std::wstring &name, DirectoryEntry *parent, int originID, + boost::shared_ptr fileRegister, + boost::shared_ptr originConnection) : + 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(); } - const std::wstring &DirectoryEntry::getName() const { return m_Name; } - void DirectoryEntry::clear() { m_Files.clear(); @@ -494,22 +542,24 @@ void DirectoryEntry::clear() m_SubDirectoriesMap.clear(); } - -FilesOrigin &DirectoryEntry::createOrigin(const std::wstring &originName, const std::wstring &directory, int priority) +FilesOrigin &DirectoryEntry::createOrigin( + const std::wstring &originName, const std::wstring &directory, int priority) { if (m_OriginConnection->exists(originName)) { FilesOrigin &origin = m_OriginConnection->getByName(originName); origin.enable(true); return origin; } else { - return m_OriginConnection->createOrigin(originName, directory, priority, m_FileRegister, m_OriginConnection); + return m_OriginConnection->createOrigin( + originName, directory, priority, m_FileRegister, m_OriginConnection); } } - -void DirectoryEntry::addFromOrigin(const std::wstring &originName, const std::wstring &directory, int priority) +void DirectoryEntry::addFromOrigin( + const std::wstring &originName, const std::wstring &directory, int priority) { FilesOrigin &origin = createOrigin(originName, directory, priority); + if (directory.length() != 0) { boost::scoped_array buffer(new wchar_t[MAXPATH_UNICODE + 1]); memset(buffer.get(), L'\0', MAXPATH_UNICODE + 1); @@ -517,11 +567,13 @@ void DirectoryEntry::addFromOrigin(const std::wstring &originName, const std::ws buffer.get()[offset] = L'\0'; addFiles(origin, buffer.get(), offset); } + m_Populated = true; } - -void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &directory, const std::wstring &fileName, int priority, int order) +void DirectoryEntry::addFromBSA( + const std::wstring &originName, std::wstring &directory, + const std::wstring &fileName, int priority, int order) { FilesOrigin &origin = createOrigin(originName, directory, priority); @@ -529,6 +581,7 @@ void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &di if (::GetFileAttributesExW(fileName.c_str(), GetFileExInfoStandard, &fileData) == 0) { throw windows_error(QObject::tr("failed to determine file time").toStdString()); } + FILETIME now; ::GetSystemTimeAsFileTime(&now); @@ -547,9 +600,14 @@ void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &di if (!containsArchive(fileName.substr(namePos)) || ::CompareFileTime(&fileData.ftLastWriteTime, &now) > 0) { BSA::Archive archive; BSA::EErrorCode res = archive.read(ToString(fileName, false).c_str(), false); + if ((res != BSA::ERROR_NONE) && (res != BSA::ERROR_INVALIDHASHES)) { std::ostringstream stream; - stream << QObject::tr("invalid bsa file: ").toStdString() << ToString(fileName, false) << " errorcode " << res << " - " << ::GetLastError(); + + stream + << QObject::tr("invalid bsa file: ").toStdString() + << ToString(fileName, false) + << " errorcode " << res << " - " << ::GetLastError(); throw std::runtime_error(stream.str()); } @@ -561,29 +619,13 @@ void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &di void DirectoryEntry::propagateOrigin(int origin) { m_Origins.insert(origin); + if (m_Parent != nullptr) { m_Parent->propagateOrigin(origin); } } -static bool SupportOptimizedFind() -{ - // large fetch and basic info for FindFirstFileEx is supported on win server 2008 r2, win 7 and newer - - OSVERSIONINFOEX versionInfo; - versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - versionInfo.dwMajorVersion = 6; - versionInfo.dwMinorVersion = 1; - ULONGLONG mask = ::VerSetConditionMask( - ::VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), - VER_MINORVERSION, VER_GREATER_EQUAL); - - bool res = ::VerifyVersionInfo(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, mask) == TRUE; - return res; -} - - static bool DirCompareByName(const DirectoryEntry *lhs, const DirectoryEntry *rhs) { return _wcsicmp(lhs->getName().c_str(), rhs->getName().c_str()) < 0; @@ -599,14 +641,17 @@ void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOf HANDLE searchHandle = nullptr; if (SupportOptimizedFind()) { - searchHandle = ::FindFirstFileExW(buffer, FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, - FIND_FIRST_EX_LARGE_FETCH); + searchHandle = ::FindFirstFileExW( + buffer, FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, + FIND_FIRST_EX_LARGE_FETCH); } else { - searchHandle = ::FindFirstFileExW(buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, nullptr, 0); + searchHandle = ::FindFirstFileExW( + buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, nullptr, 0); } if (searchHandle != INVALID_HANDLE_VALUE) { BOOL result = true; + while (result) { if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if ((wcscmp(findData.cFileName, L".") != 0) && @@ -618,15 +663,18 @@ void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOf } else { insert(findData.cFileName, origin, findData.ftLastWriteTime, L"", -1); } + result = ::FindNextFileW(searchHandle, &findData); } } + std::sort(m_SubDirectories.begin(), m_SubDirectories.end(), &DirCompareByName); ::FindClose(searchHandle); } - -void DirectoryEntry::addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName, int order) +void DirectoryEntry::addFiles( + FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, + const std::wstring &archiveName, int order) { // add files for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) { @@ -643,21 +691,22 @@ void DirectoryEntry::addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolde } } - bool DirectoryEntry::removeFile(const std::wstring &filePath, int *origin) { size_t pos = filePath.find_first_of(L"\\/"); + if (pos == std::string::npos) { return this->remove(filePath, origin); + } + + std::wstring dirName = filePath.substr(0, pos); + std::wstring rest = filePath.substr(pos + 1); + DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); + + if (entry != nullptr) { + return entry->removeFile(rest, origin); } else { - std::wstring dirName = filePath.substr(0, pos); - std::wstring rest = filePath.substr(pos + 1); - DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); - if (entry != nullptr) { - return entry->removeFile(rest, origin); - } else { - return false; - } + return false; } } @@ -681,9 +730,11 @@ void DirectoryEntry::removeDirRecursive() void DirectoryEntry::removeDir(const std::wstring &path) { size_t pos = path.find_first_of(L"\\/"); + if (pos == std::string::npos) { for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) { DirectoryEntry *entry = *iter; + if (CaseInsensitiveEqual(entry->getName(), path)) { entry->removeDirRecursive(); @@ -709,6 +760,7 @@ void DirectoryEntry::removeDir(const std::wstring &path) std::wstring dirName = path.substr(0, pos); std::wstring rest = path.substr(pos + 1); DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); + if (entry != nullptr) { entry->removeDir(rest); } @@ -770,9 +822,11 @@ bool DirectoryEntry::hasContentsFromOrigin(int originID) const return m_Origins.find(originID) != m_Origins.end(); } -void DirectoryEntry::insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime) +void DirectoryEntry::insertFile( + const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime) { size_t pos = filePath.find_first_of(L"\\/"); + if (pos == std::string::npos) { this->insert(filePath, origin, fileTime, std::wstring(), -1); } else { @@ -851,14 +905,18 @@ bool DirectoryEntry::containsArchive(std::wstring archiveName) { for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); - if (entry->isFromArchive(archiveName)) return true; + if (entry->isFromArchive(archiveName)) { + return true; + } } + return false; } int DirectoryEntry::anyOrigin() const { bool ignore; + for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); if ((entry.get() != nullptr) && !entry->isFromArchive()) { @@ -874,53 +932,36 @@ int DirectoryEntry::anyOrigin() const return res; } } + return *(m_Origins.begin()); } - bool DirectoryEntry::originExists(const std::wstring &name) const { return m_OriginConnection->exists(name); } - FilesOrigin &DirectoryEntry::getOriginByID(int ID) const { return m_OriginConnection->getByID(ID); } - FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const { return m_OriginConnection->getByName(name); } -/* -int DirectoryEntry::getOrigin(const std::wstring &path, bool &archive) -{ - const DirectoryEntry *directory = nullptr; - const FileEntry::Ptr file = searchFile(path, &directory); - if (file.get() != nullptr) { - return file->getOrigin(archive); - } else { - if (directory != nullptr) { - return directory->anyOrigin(); - } else { - return -1; - } - } -}*/ - std::vector DirectoryEntry::getFiles() const { std::vector result; + for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { result.push_back(m_FileRegister->getFile(iter->second)); } + return result; } - const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const DirectoryEntry **directory) const { if (directory != nullptr) { @@ -932,14 +973,16 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const if (directory != nullptr) { *directory = this; } + return FileEntry::Ptr(); } - size_t len = path.find_first_of(L"\\/"); + const size_t len = path.find_first_of(L"\\/"); if (len == std::string::npos) { // no more path components auto iter = m_Files.find(ToLowerCopy(path)); + if (iter != m_Files.end()) { return m_FileRegister->getFile(iter->second); } else if (directory != nullptr) { @@ -949,21 +992,23 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const } } } else { - // file is in in a subdirectory, recurse into the matching subdirectory + // file is in a subdirectory, recurse into the matching subdirectory std::wstring pathComponent = path.substr(0, len); DirectoryEntry *temp = findSubDirectory(pathComponent); + if (temp != nullptr) { if (len >= path.size()) { log::error(QObject::tr("unexpected end of path").toStdString()); return FileEntry::Ptr(); } + return temp->searchFile(path.substr(len + 1), directory); } } + return FileEntry::Ptr(); } - DirectoryEntry *DirectoryEntry::findSubDirectory( const std::wstring &name, bool alreadyLowerCase) const { @@ -982,13 +1027,11 @@ DirectoryEntry *DirectoryEntry::findSubDirectory( return itor->second; } - DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &path) { return getSubDirectoryRecursive(path, false, -1); } - const FileEntry::Ptr DirectoryEntry::findFile( const std::wstring &name, bool alreadyLowerCase) const { @@ -1023,13 +1066,15 @@ bool DirectoryEntry::hasFile(const std::wstring& name) const return m_Files.contains(ToLowerCopy(name)); } -DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool create, int originID) +DirectoryEntry *DirectoryEntry::getSubDirectory( + const std::wstring &name, bool create, int originID) { for (DirectoryEntry *entry : m_SubDirectories) { if (CaseInsensitiveEqual(entry->getName(), name)) { return entry; } } + if (create) { auto* entry = new DirectoryEntry( name, this, originID, m_FileRegister, m_OriginConnection); @@ -1043,19 +1088,21 @@ DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool c } } - -DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive(const std::wstring &path, bool create, int originID) +DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive( + const std::wstring &path, bool create, int originID) { if (path.length() == 0) { // path ended with a backslash? return this; } - size_t pos = path.find_first_of(L"\\/"); + const size_t pos = path.find_first_of(L"\\/"); + if (pos == std::wstring::npos) { return getSubDirectory(path, create); } else { DirectoryEntry *nextChild = getSubDirectory(path.substr(0, pos), create, originID); + if (nextChild == nullptr) { return nullptr; } else { @@ -1065,7 +1112,6 @@ DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive(const std::wstring &pat } - FileRegister::FileRegister(boost::shared_ptr originConnection) : m_OriginConnection(originConnection) { @@ -1078,7 +1124,6 @@ FileRegister::~FileRegister() m_Files.clear(); } - FileEntry::Index FileRegister::generateIndex() { static std::atomic sIndex(0); @@ -1087,20 +1132,21 @@ FileEntry::Index FileRegister::generateIndex() bool FileRegister::indexValid(FileEntry::Index index) const { - return m_Files.find(index) != m_Files.end(); + return (m_Files.find(index) != m_Files.end()); } FileEntry::Ptr FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent) { FileEntry::Index index = generateIndex(); m_Files[index] = FileEntry::Ptr(new FileEntry(index, name, parent)); + return m_Files[index]; } - FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const { auto iter = m_Files.find(index); + if (iter != m_Files.end()) { return iter->second; } else { @@ -1111,10 +1157,12 @@ FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const void FileRegister::unregisterFile(FileEntry::Ptr file) { bool ignore; + // unregister from origin int originID = file->getOrigin(ignore); m_OriginConnection->getByID(originID).removeFile(file->getIndex()); - const std::vector>> &alternatives = file->getAlternatives(); + const auto& alternatives = file->getAlternatives(); + for (auto iter = alternatives.begin(); iter != alternatives.end(); ++iter) { m_OriginConnection->getByID(iter->first).removeFile(file->getIndex()); } @@ -1125,10 +1173,10 @@ void FileRegister::unregisterFile(FileEntry::Ptr file) } } - bool FileRegister::removeFile(FileEntry::Index index) { auto iter = m_Files.find(index); + if (iter != m_Files.end()) { unregisterFile(iter->second); m_Files.erase(index); @@ -1142,6 +1190,7 @@ bool FileRegister::removeFile(FileEntry::Index index) void FileRegister::removeOrigin(FileEntry::Index index, int originID) { auto iter = m_Files.find(index); + if (iter != m_Files.end()) { if (iter->second->removeOrigin(originID)) { unregisterFile(iter->second); @@ -1152,11 +1201,14 @@ void FileRegister::removeOrigin(FileEntry::Index index, int originID) } } -void FileRegister::removeOriginMulti(std::set indices, int originID, time_t notAfter) +void FileRegister::removeOriginMulti( + std::set indices, int originID, time_t notAfter) { std::vector removedFiles; - for (auto iter = indices.begin(); iter != indices.end();) { + + for (auto iter = indices.begin(); iter != indices.end(); ) { auto pos = m_Files.find(*iter); + if (pos != m_Files.end() && (pos->second->lastAccessed() < notAfter) && pos->second->removeOrigin(originID)) { @@ -1168,20 +1220,27 @@ void FileRegister::removeOriginMulti(std::set indices, int ori } } - // optimization: this is only called when disabling an origin and in this case we don't have - // to remove the file from the origin + // optimization: this is only called when disabling an origin and in this case + // we don't have to remove the file from the origin + + // need to remove files from their parent directories. multiple ways to go + // about this: + // a) for each file, search its parents file-list (preferably by name) and + // remove what is found + // b) gather the parent directories, go through the file list for each once + // and remove all files that have been removed + // + // the latter should be faster when there are many files in few directories. + // since this is called only when disabling an origin that is probably + // frequently the case - // need to remove files from their parent directories. multiple ways to go about this: - // a) for each file, search its parents file-list (preferably by name) and remove what is found - // b) gather the parent directories, go through the file list for each once and remove all files that have been removed - // the latter should be faster when there are many files in few directories. since this is called - // only when disabling an origin that is probably frequently the case std::set parents; for (const FileEntry::Ptr &file : removedFiles) { if (file->getParent() != nullptr) { parents.insert(file->getParent()); } } + for (DirectoryEntry *parent : parents) { parent->removeFiles(indices); } -- cgit v1.3.1 From ee03c232907a0c340715ed8390d432cf27e13b27 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 19 Dec 2019 23:40:43 -0500 Subject: refactoring: moved member functions in the same order as the header --- src/shared/directoryentry.cpp | 1176 ++++++++++++++++++++--------------------- src/shared/directoryentry.h | 31 +- 2 files changed, 607 insertions(+), 600 deletions(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 3b98b7ff..c93df665 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -65,6 +65,11 @@ static bool SupportOptimizedFind() return (::VerifyVersionInfo(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION, mask) == TRUE); } +static bool DirCompareByName(const DirectoryEntry *lhs, const DirectoryEntry *rhs) +{ + return _wcsicmp(lhs->getName().c_str(), rhs->getName().c_str()) < 0; +} + class OriginConnection { @@ -157,112 +162,25 @@ private: }; -FilesOrigin::FilesOrigin() - : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0) -{ - LEAK_TRACE; -} - -FilesOrigin::FilesOrigin(const FilesOrigin &reference) - : m_ID(reference.m_ID) - , m_Disabled(reference.m_Disabled) - , m_Name(reference.m_Name) - , m_Path(reference.m_Path) - , m_Priority(reference.m_Priority) - , m_FileRegister(reference.m_FileRegister) - , m_OriginConnection(reference.m_OriginConnection) +FileEntry::FileEntry() : + m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr), + m_LastAccessed(time(nullptr)) { LEAK_TRACE; } - -FilesOrigin::FilesOrigin( - int ID, const std::wstring &name, const std::wstring &path, int priority, - boost::shared_ptr fileRegister, - boost::shared_ptr originConnection) : - m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path), - m_Priority(priority), m_FileRegister(fileRegister), - m_OriginConnection(originConnection) +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; } -FilesOrigin::~FilesOrigin() +FileEntry::~FileEntry() { LEAK_UNTRACE; } -void FilesOrigin::setPriority(int priority) -{ - m_OriginConnection.lock()->changePriorityLookup(m_Priority, priority); - - m_Priority = priority; -} - -void FilesOrigin::setName(const std::wstring &name) -{ - m_OriginConnection.lock()->changeNameLookup(m_Name, name); - - // change path too - if (tail(m_Path, m_Name.length()) == m_Name) { - m_Path = m_Path.substr(0, m_Path.length() - m_Name.length()).append(name); - } - - m_Name = name; -} - -std::vector FilesOrigin::getFiles() const -{ - std::vector result; - - for (FileEntry::Index fileIdx : m_Files) { - if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) { - result.push_back(p); - } - } - - return result; -} - -FileEntry::Ptr FilesOrigin::findFile(FileEntry::Index index) const -{ - return m_FileRegister.lock()->getFile(index); -} - -void FilesOrigin::enable(bool enabled, time_t notAfter) -{ - if (!enabled) { - std::set copy = m_Files; - m_FileRegister.lock()->removeOriginMulti(copy, m_ID, notAfter); - m_Files.clear(); - } - - m_Disabled = !enabled; -} - -void FilesOrigin::removeFile(FileEntry::Index index) -{ - auto iter = m_Files.find(index); - - if (iter != m_Files.end()) { - m_Files.erase(iter); - } -} - -bool FilesOrigin::containsArchive(std::wstring archiveName) -{ - for (FileEntry::Index fileIdx : m_Files) { - if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) { - if (p->isFromArchive(archiveName)) { - return true; - } - } - } - - return false; -} - - void FileEntry::addOrigin( int origin, FILETIME fileTime, const std::wstring &archive, int order) { @@ -381,25 +299,6 @@ bool FileEntry::removeOrigin(int origin) return false; } -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::sortOrigins() { m_Alternatives.push_back({m_Origin, m_Archive}); @@ -440,18 +339,23 @@ void FileEntry::sortOrigins() } } -bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) const +bool FileEntry::isFromArchive(std::wstring archiveName) const { - if (parent == nullptr) { - return false; - } else { - // don't append the topmost parent because it is the virtual data-root - if (recurseParents(path, parent->getParent())) { - path.append(L"\\").append(parent->getName()); - } + if (archiveName.length() == 0) { + return m_Archive.first.length() != 0; + } + if (m_Archive.first.compare(archiveName) == 0) { return true; } + + for (auto alternative : m_Alternatives) { + if (alternative.second.first.compare(archiveName) == 0) { + return true; + } + } + + return false; } std::wstring FileEntry::getFullPath() const @@ -477,105 +381,329 @@ std::wstring FileEntry::getRelativePath() const return result + L"\\" + m_Name; } -bool FileEntry::isFromArchive(std::wstring archiveName) const +bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) const { - if (archiveName.length() == 0) { - return m_Archive.first.length() != 0; - } + if (parent == nullptr) { + return false; + } else { + // don't append the topmost parent because it is the virtual data-root + if (recurseParents(path, parent->getParent())) { + path.append(L"\\").append(parent->getName()); + } - if (m_Archive.first.compare(archiveName) == 0) { return true; } - - for (auto alternative : m_Alternatives) { - if (alternative.second.first.compare(archiveName) == 0) { - return true; - } - } - - return false; } -DirectoryEntry::DirectoryEntry( - const std::wstring &name, DirectoryEntry *parent, int originID) : - m_OriginConnection(new OriginConnection), - m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(true) +FilesOrigin::FilesOrigin() + : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0) { - m_FileRegister.reset(new FileRegister(m_OriginConnection)); - m_Origins.insert(originID); LEAK_TRACE; } -DirectoryEntry::DirectoryEntry( - const std::wstring &name, DirectoryEntry *parent, int originID, - boost::shared_ptr fileRegister, - boost::shared_ptr originConnection) : - m_FileRegister(fileRegister), m_OriginConnection(originConnection), - m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(false) +FilesOrigin::FilesOrigin(const FilesOrigin &reference) + : m_ID(reference.m_ID) + , m_Disabled(reference.m_Disabled) + , m_Name(reference.m_Name) + , m_Path(reference.m_Path) + , m_Priority(reference.m_Priority) + , m_FileRegister(reference.m_FileRegister) + , m_OriginConnection(reference.m_OriginConnection) { LEAK_TRACE; - m_Origins.insert(originID); } -DirectoryEntry::~DirectoryEntry() +FilesOrigin::FilesOrigin( + int ID, const std::wstring &name, const std::wstring &path, int priority, + boost::shared_ptr fileRegister, + boost::shared_ptr originConnection) : + m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path), + m_Priority(priority), m_FileRegister(fileRegister), + m_OriginConnection(originConnection) { - LEAK_UNTRACE; - clear(); + LEAK_TRACE; } -const std::wstring &DirectoryEntry::getName() const +FilesOrigin::~FilesOrigin() { - return m_Name; + LEAK_UNTRACE; } -void DirectoryEntry::clear() +void FilesOrigin::setPriority(int priority) { - m_Files.clear(); - m_FilesLookup.clear(); - - for (DirectoryEntry *entry : m_SubDirectories) { - delete entry; - } + m_OriginConnection.lock()->changePriorityLookup(m_Priority, priority); - m_SubDirectories.clear(); - m_SubDirectoriesMap.clear(); + m_Priority = priority; } -FilesOrigin &DirectoryEntry::createOrigin( - const std::wstring &originName, const std::wstring &directory, int priority) +void FilesOrigin::setName(const std::wstring &name) { - if (m_OriginConnection->exists(originName)) { - FilesOrigin &origin = m_OriginConnection->getByName(originName); - origin.enable(true); - return origin; - } else { - return m_OriginConnection->createOrigin( - originName, directory, priority, m_FileRegister, m_OriginConnection); + m_OriginConnection.lock()->changeNameLookup(m_Name, name); + + // change path too + if (tail(m_Path, m_Name.length()) == m_Name) { + m_Path = m_Path.substr(0, m_Path.length() - m_Name.length()).append(name); } + + m_Name = name; } -void DirectoryEntry::addFromOrigin( - const std::wstring &originName, const std::wstring &directory, int priority) +std::vector FilesOrigin::getFiles() const { - FilesOrigin &origin = createOrigin(originName, directory, priority); + std::vector result; - if (directory.length() != 0) { - boost::scoped_array buffer(new wchar_t[MAXPATH_UNICODE + 1]); - memset(buffer.get(), L'\0', MAXPATH_UNICODE + 1); - int offset = _snwprintf(buffer.get(), MAXPATH_UNICODE, L"%ls", directory.c_str()); - buffer.get()[offset] = L'\0'; - addFiles(origin, buffer.get(), offset); + for (FileEntry::Index fileIdx : m_Files) { + if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) { + result.push_back(p); + } } - m_Populated = true; + return result; } -void DirectoryEntry::addFromBSA( - const std::wstring &originName, std::wstring &directory, - const std::wstring &fileName, int priority, int order) +FileEntry::Ptr FilesOrigin::findFile(FileEntry::Index index) const { - FilesOrigin &origin = createOrigin(originName, directory, priority); + return m_FileRegister.lock()->getFile(index); +} + +void FilesOrigin::enable(bool enabled, time_t notAfter) +{ + if (!enabled) { + std::set copy = m_Files; + m_FileRegister.lock()->removeOriginMulti(copy, m_ID, notAfter); + m_Files.clear(); + } + + m_Disabled = !enabled; +} + +void FilesOrigin::removeFile(FileEntry::Index index) +{ + auto iter = m_Files.find(index); + + if (iter != m_Files.end()) { + m_Files.erase(iter); + } +} + +bool FilesOrigin::containsArchive(std::wstring archiveName) +{ + for (FileEntry::Index fileIdx : m_Files) { + if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) { + if (p->isFromArchive(archiveName)) { + return true; + } + } + } + + return false; +} + + +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 +{ + return (m_Files.find(index) != m_Files.end()); +} + +FileEntry::Ptr FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent) +{ + FileEntry::Index index = generateIndex(); + m_Files[index] = FileEntry::Ptr(new FileEntry(index, name, parent)); + + return m_Files[index]; +} + +FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const +{ + auto iter = m_Files.find(index); + + if (iter != m_Files.end()) { + return iter->second; + } else { + return FileEntry::Ptr(); + } +} + +bool FileRegister::removeFile(FileEntry::Index index) +{ + auto iter = m_Files.find(index); + + if (iter != m_Files.end()) { + unregisterFile(iter->second); + m_Files.erase(index); + return true; + } else { + log::error(QObject::tr("invalid file index for remove: {}").toStdString(), index); + return false; + } +} + +void FileRegister::removeOrigin(FileEntry::Index index, int originID) +{ + auto iter = m_Files.find(index); + + if (iter != m_Files.end()) { + if (iter->second->removeOrigin(originID)) { + unregisterFile(iter->second); + m_Files.erase(iter); + } + } else { + log::error(QObject::tr("invalid file index for remove (for origin): {}").toStdString(), index); + } +} + +void FileRegister::removeOriginMulti( + std::set indices, int originID, time_t notAfter) +{ + std::vector removedFiles; + + for (auto iter = indices.begin(); iter != indices.end(); ) { + auto pos = m_Files.find(*iter); + + if (pos != m_Files.end() + && (pos->second->lastAccessed() < notAfter) + && pos->second->removeOrigin(originID)) { + removedFiles.push_back(pos->second); + m_Files.erase(pos); + ++iter; + } else { + indices.erase(iter++); + } + } + + // optimization: this is only called when disabling an origin and in this case + // we don't have to remove the file from the origin + + // need to remove files from their parent directories. multiple ways to go + // about this: + // a) for each file, search its parents file-list (preferably by name) and + // remove what is found + // b) gather the parent directories, go through the file list for each once + // and remove all files that have been removed + // + // the latter should be faster when there are many files in few directories. + // since this is called only when disabling an origin that is probably + // frequently the case + + std::set parents; + for (const FileEntry::Ptr &file : removedFiles) { + if (file->getParent() != nullptr) { + parents.insert(file->getParent()); + } + } + + for (DirectoryEntry *parent : parents) { + parent->removeFiles(indices); + } +} + +void FileRegister::sortOrigins() +{ + for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { + iter->second->sortOrigins(); + } +} + +FileEntry::Index FileRegister::generateIndex() +{ + static std::atomic sIndex(0); + return sIndex++; +} + +void FileRegister::unregisterFile(FileEntry::Ptr file) +{ + bool ignore; + + // unregister from origin + int originID = file->getOrigin(ignore); + m_OriginConnection->getByID(originID).removeFile(file->getIndex()); + const auto& alternatives = file->getAlternatives(); + + for (auto iter = alternatives.begin(); iter != alternatives.end(); ++iter) { + m_OriginConnection->getByID(iter->first).removeFile(file->getIndex()); + } + + // unregister from directory + if (file->getParent() != nullptr) { + file->getParent()->removeFile(file->getIndex()); + } +} + + +DirectoryEntry::DirectoryEntry( + const std::wstring &name, DirectoryEntry *parent, int originID) : + m_OriginConnection(new OriginConnection), + m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(true) +{ + m_FileRegister.reset(new FileRegister(m_OriginConnection)); + m_Origins.insert(originID); + LEAK_TRACE; +} + +DirectoryEntry::DirectoryEntry( + const std::wstring &name, DirectoryEntry *parent, int originID, + boost::shared_ptr fileRegister, + boost::shared_ptr originConnection) : + 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(); +} + +void DirectoryEntry::clear() +{ + m_Files.clear(); + m_FilesLookup.clear(); + + for (DirectoryEntry *entry : m_SubDirectories) { + delete entry; + } + + m_SubDirectories.clear(); + m_SubDirectoriesMap.clear(); +} + +void DirectoryEntry::addFromOrigin( + const std::wstring &originName, const std::wstring &directory, int priority) +{ + FilesOrigin &origin = createOrigin(originName, directory, priority); + + if (directory.length() != 0) { + boost::scoped_array buffer(new wchar_t[MAXPATH_UNICODE + 1]); + memset(buffer.get(), L'\0', MAXPATH_UNICODE + 1); + int offset = _snwprintf(buffer.get(), MAXPATH_UNICODE, L"%ls", directory.c_str()); + buffer.get()[offset] = L'\0'; + addFiles(origin, buffer.get(), offset); + } + + m_Populated = true; +} + +void DirectoryEntry::addFromBSA( + const std::wstring &originName, std::wstring &directory, + const std::wstring &fileName, int priority, int order) +{ + FilesOrigin &origin = createOrigin(originName, directory, priority); WIN32_FILE_ATTRIBUTE_DATA fileData; if (::GetFileAttributesExW(fileName.c_str(), GetFileExInfoStandard, &fileData) == 0) { @@ -608,6 +736,7 @@ void DirectoryEntry::addFromBSA( << QObject::tr("invalid bsa file: ").toStdString() << ToString(fileName, false) << " errorcode " << res << " - " << ::GetLastError(); + throw std::runtime_error(stream.str()); } @@ -625,201 +754,170 @@ void DirectoryEntry::propagateOrigin(int origin) } } - -static bool DirCompareByName(const DirectoryEntry *lhs, const DirectoryEntry *rhs) +bool DirectoryEntry::originExists(const std::wstring &name) const { - return _wcsicmp(lhs->getName().c_str(), rhs->getName().c_str()) < 0; + return m_OriginConnection->exists(name); } - -void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset) +FilesOrigin &DirectoryEntry::getOriginByID(int ID) const { - WIN32_FIND_DATAW findData; + return m_OriginConnection->getByID(ID); +} - _snwprintf_s(buffer + bufferOffset, MAXPATH_UNICODE - bufferOffset, _TRUNCATE, L"\\*"); +FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const +{ + return m_OriginConnection->getByName(name); +} - HANDLE searchHandle = nullptr; +int DirectoryEntry::anyOrigin() const +{ + bool ignore; - if (SupportOptimizedFind()) { - searchHandle = ::FindFirstFileExW( - buffer, FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, - FIND_FIRST_EX_LARGE_FETCH); - } else { - searchHandle = ::FindFirstFileExW( - buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, nullptr, 0); + for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { + FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); + if ((entry.get() != nullptr) && !entry->isFromArchive()) { + return entry->getOrigin(ignore); + } } - if (searchHandle != INVALID_HANDLE_VALUE) { - BOOL result = true; - - while (result) { - if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - if ((wcscmp(findData.cFileName, L".") != 0) && - (wcscmp(findData.cFileName, L"..") != 0)) { - int offset = _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE, L"\\%ls", findData.cFileName); - // recurse into subdirectories - getSubDirectory(findData.cFileName, true, origin.getID())->addFiles(origin, buffer, bufferOffset + offset); - } - } else { - insert(findData.cFileName, origin, findData.ftLastWriteTime, L"", -1); - } - - result = ::FindNextFileW(searchHandle, &findData); + // if we got here, no file directly within this directory is a valid indicator for a mod, thus + // we continue looking in subdirectories + for (DirectoryEntry *entry : m_SubDirectories) { + int res = entry->anyOrigin(); + if (res != -1){ + return res; } } - std::sort(m_SubDirectories.begin(), m_SubDirectories.end(), &DirCompareByName); - ::FindClose(searchHandle); + return *(m_Origins.begin()); } -void DirectoryEntry::addFiles( - FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, - const std::wstring &archiveName, int order) +std::vector DirectoryEntry::getFiles() const { - // 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); - } - - // recurse into subdirectories - for (unsigned int folderIdx = 0; folderIdx < archiveFolder->getNumSubFolders(); ++folderIdx) { - BSA::Folder::Ptr folder = archiveFolder->getSubFolder(folderIdx); - DirectoryEntry *folderEntry = getSubDirectoryRecursive(ToWString(folder->getName(), true), true, origin.getID()); + std::vector result; - folderEntry->addFiles(origin, folder, fileTime, archiveName, order); + for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { + result.push_back(m_FileRegister->getFile(iter->second)); } + + return result; } -bool DirectoryEntry::removeFile(const std::wstring &filePath, int *origin) +DirectoryEntry *DirectoryEntry::findSubDirectory( + const std::wstring &name, bool alreadyLowerCase) const { - size_t pos = filePath.find_first_of(L"\\/"); - - if (pos == std::string::npos) { - return this->remove(filePath, origin); - } - - std::wstring dirName = filePath.substr(0, pos); - std::wstring rest = filePath.substr(pos + 1); - DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); + SubDirectoriesMap::const_iterator itor; - if (entry != nullptr) { - return entry->removeFile(rest, origin); + if (alreadyLowerCase) { + itor = m_SubDirectoriesMap.find(name); } else { - return false; - } -} - -void DirectoryEntry::removeDirRecursive() -{ - while (!m_Files.empty()) { - m_FileRegister->removeFile(m_Files.begin()->second); + itor = m_SubDirectoriesMap.find(ToLowerCopy(name)); } - m_FilesLookup.clear(); - - for (DirectoryEntry *entry : m_SubDirectories) { - entry->removeDirRecursive(); - delete entry; + if (itor == m_SubDirectoriesMap.end()) { + return nullptr; } - m_SubDirectories.clear(); - m_SubDirectoriesMap.clear(); + return itor->second; } -void DirectoryEntry::removeDir(const std::wstring &path) +DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &path) { - size_t pos = path.find_first_of(L"\\/"); - - if (pos == std::string::npos) { - for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) { - 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; - } - } + return getSubDirectoryRecursive(path, false, -1); +} - if (!found) { - log::error("entry {} not in sub directories map", entry->getName()); - } +const FileEntry::Ptr DirectoryEntry::findFile( + const std::wstring &name, bool alreadyLowerCase) const +{ + FilesLookup::const_iterator iter; - m_SubDirectories.erase(iter); - delete entry; - break; - } - } + if (alreadyLowerCase) { + iter = m_FilesLookup.find(FileKey(name)); } else { - std::wstring dirName = path.substr(0, pos); - std::wstring rest = path.substr(pos + 1); - DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); + iter = m_FilesLookup.find(FileKey(ToLowerCopy(name))); + } - if (entry != nullptr) { - entry->removeDir(rest); - } + if (iter != m_FilesLookup.end()) { + return m_FileRegister->getFile(iter->second); + } else { + return FileEntry::Ptr(); } } -bool DirectoryEntry::remove(const std::wstring &fileName, int *origin) +const FileEntry::Ptr DirectoryEntry::findFile(const FileKey& key) const { - const auto lcFileName = ToLowerCopy(fileName); + auto iter = m_FilesLookup.find(key); - auto iter = m_Files.find(lcFileName); - bool b = false; + if (iter != m_FilesLookup.end()) { + return m_FileRegister->getFile(iter->second); + } else { + return FileEntry::Ptr(); + } +} - 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); - } +bool DirectoryEntry::hasFile(const std::wstring& name) const +{ + return m_Files.contains(ToLowerCopy(name)); +} + +bool DirectoryEntry::containsArchive(std::wstring archiveName) +{ + for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { + FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); + if (entry->isFromArchive(archiveName)) { + return true; } + } - b = m_FileRegister->removeFile(iter->second); + return false; +} + +const FileEntry::Ptr DirectoryEntry::searchFile( + const std::wstring &path, const DirectoryEntry **directory) const +{ + if (directory != nullptr) { + *directory = nullptr; } - if (m_Files.size() != m_FilesLookup.size()) { - DebugBreak(); + if ((path.length() == 0) || (path == L"*")) { + // no file name -> the path ended on a (back-)slash + if (directory != nullptr) { + *directory = this; + } + + return FileEntry::Ptr(); } - return b; -} + const size_t len = path.find_first_of(L"\\/"); -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 (len == std::string::npos) { + // no more path components + auto iter = m_Files.find(ToLowerCopy(path)); - if (iter != m_Files.end()) { - file = m_FileRegister->getFile(iter->second); + if (iter != m_Files.end()) { + return m_FileRegister->getFile(iter->second); + } else if (directory != nullptr) { + DirectoryEntry *temp = findSubDirectory(path); + if (temp != nullptr) { + *directory = temp; + } + } } else { - file = m_FileRegister->createFile(fileName, this); - m_Files.emplace(fileNameLower, file->getIndex()); - m_FilesLookup.emplace(fileNameLower, file->getIndex()); - } + // file is in a subdirectory, recurse into the matching subdirectory + std::wstring pathComponent = path.substr(0, len); + DirectoryEntry *temp = findSubDirectory(pathComponent); - if (m_Files.size() != m_FilesLookup.size()) { - DebugBreak(); - } + if (temp != nullptr) { + if (len >= path.size()) { + log::error(QObject::tr("unexpected end of path")); + return FileEntry::Ptr(); + } - file->addOrigin(origin.getID(), fileTime, archive, order); - origin.addFile(file->getIndex()); -} + return temp->searchFile(path.substr(len + 1), directory); + } + } -bool DirectoryEntry::hasContentsFromOrigin(int originID) const -{ - return m_Origins.find(originID) != m_Origins.end(); + return FileEntry::Ptr(); } void DirectoryEntry::insertFile( @@ -848,12 +946,12 @@ void DirectoryEntry::removeFile(FileEntry::Index index) m_FilesLookup.erase(iter); } else { log::error( - "file \"{}\" not in directory for lookup \"{}\"", + QObject::tr("file \"{}\" not in directory for lookup \"{}\"").toStdString(), m_FileRegister->getFile(index)->getName(), this->getName()); } } else { log::error( - "file \"{}\" not in directory \"{}\" for lookup, directory empty", + QObject::tr("file \"{}\" not in directory \"{}\" for lookup, directory empty").toStdString(), m_FileRegister->getFile(index)->getName(), this->getName()); } @@ -882,188 +980,209 @@ void DirectoryEntry::removeFile(FileEntry::Index index) } } -void DirectoryEntry::removeFiles(const std::set &indices) +bool DirectoryEntry::removeFile(const std::wstring &filePath, int *origin) { - for (auto iter = m_Files.begin(); iter != m_Files.end();) { - if (indices.find(iter->second) != indices.end()) { - iter = m_Files.erase(iter); - } else { - ++iter; - } + size_t pos = filePath.find_first_of(L"\\/"); + + if (pos == std::string::npos) { + return this->remove(filePath, origin); } - for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) { - if (indices.find(iter->second) != indices.end()) { - iter = m_FilesLookup.erase(iter); - } else { - ++iter; - } + std::wstring dirName = filePath.substr(0, pos); + std::wstring rest = filePath.substr(pos + 1); + DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); + + if (entry != nullptr) { + return entry->removeFile(rest, origin); + } else { + return false; } } -bool DirectoryEntry::containsArchive(std::wstring archiveName) +void DirectoryEntry::removeDir(const std::wstring &path) { - for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); - if (entry->isFromArchive(archiveName)) { - return true; - } - } + size_t pos = path.find_first_of(L"\\/"); - return false; -} + if (pos == std::string::npos) { + for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) { + DirectoryEntry *entry = *iter; -int DirectoryEntry::anyOrigin() const -{ - bool ignore; + if (CaseInsensitiveEqual(entry->getName(), path)) { + entry->removeDirRecursive(); - for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - FileEntry::Ptr entry = m_FileRegister->getFile(iter->second); - if ((entry.get() != nullptr) && !entry->isFromArchive()) { - return entry->getOrigin(ignore); + 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; + } } - } + } else { + std::wstring dirName = path.substr(0, pos); + std::wstring rest = path.substr(pos + 1); + DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false); - // if we got here, no file directly within this directory is a valid indicator for a mod, thus - // we continue looking in subdirectories - for (DirectoryEntry *entry : m_SubDirectories) { - int res = entry->anyOrigin(); - if (res != -1){ - return res; + if (entry != nullptr) { + entry->removeDir(rest); } } - - return *(m_Origins.begin()); } -bool DirectoryEntry::originExists(const std::wstring &name) const +bool DirectoryEntry::remove(const std::wstring &fileName, int *origin) { - return m_OriginConnection->exists(name); -} + const auto lcFileName = ToLowerCopy(fileName); -FilesOrigin &DirectoryEntry::getOriginByID(int ID) const -{ - return m_OriginConnection->getByID(ID); -} + auto iter = m_Files.find(lcFileName); + bool b = false; -FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const -{ - return m_OriginConnection->getByName(name); -} + 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); + } + } -std::vector DirectoryEntry::getFiles() const -{ - std::vector result; + b = m_FileRegister->removeFile(iter->second); + } - for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - result.push_back(m_FileRegister->getFile(iter->second)); + if (m_Files.size() != m_FilesLookup.size()) { + DebugBreak(); } - return result; + return b; } -const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const DirectoryEntry **directory) const +bool DirectoryEntry::hasContentsFromOrigin(int originID) const { - if (directory != nullptr) { - *directory = nullptr; - } - - if ((path.length() == 0) || (path == L"*")) { - // no file name -> the path ended on a (back-)slash - if (directory != nullptr) { - *directory = this; - } - - return FileEntry::Ptr(); - } - - const size_t len = path.find_first_of(L"\\/"); - - if (len == std::string::npos) { - // no more path components - auto iter = m_Files.find(ToLowerCopy(path)); + return m_Origins.find(originID) != m_Origins.end(); +} - if (iter != m_Files.end()) { - return m_FileRegister->getFile(iter->second); - } else if (directory != nullptr) { - DirectoryEntry *temp = findSubDirectory(path); - if (temp != nullptr) { - *directory = temp; - } - } +FilesOrigin &DirectoryEntry::createOrigin( + const std::wstring &originName, const std::wstring &directory, int priority) +{ + if (m_OriginConnection->exists(originName)) { + FilesOrigin &origin = m_OriginConnection->getByName(originName); + origin.enable(true); + return origin; } else { - // file is in a subdirectory, recurse into the matching subdirectory - std::wstring pathComponent = path.substr(0, len); - DirectoryEntry *temp = findSubDirectory(pathComponent); - - if (temp != nullptr) { - if (len >= path.size()) { - log::error(QObject::tr("unexpected end of path").toStdString()); - return FileEntry::Ptr(); - } + return m_OriginConnection->createOrigin( + originName, directory, priority, m_FileRegister, m_OriginConnection); + } +} - return temp->searchFile(path.substr(len + 1), directory); +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; } } - return FileEntry::Ptr(); + for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) { + if (indices.find(iter->second) != indices.end()) { + iter = m_FilesLookup.erase(iter); + } else { + ++iter; + } + } } -DirectoryEntry *DirectoryEntry::findSubDirectory( - const std::wstring &name, bool alreadyLowerCase) const +void DirectoryEntry::insert( + const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, + const std::wstring &archive, int order) { - SubDirectoriesMap::const_iterator itor; + std::wstring fileNameLower = ToLowerCopy(fileName); + auto iter = m_Files.find(fileNameLower); + FileEntry::Ptr file; - if (alreadyLowerCase) { - itor = m_SubDirectoriesMap.find(name); + if (iter != m_Files.end()) { + file = m_FileRegister->getFile(iter->second); } else { - itor = m_SubDirectoriesMap.find(ToLowerCopy(name)); + file = m_FileRegister->createFile(fileName, this); + m_Files.emplace(fileNameLower, file->getIndex()); + m_FilesLookup.emplace(fileNameLower, file->getIndex()); } - if (itor == m_SubDirectoriesMap.end()) { - return nullptr; + if (m_Files.size() != m_FilesLookup.size()) { + DebugBreak(); } - return itor->second; + file->addOrigin(origin.getID(), fileTime, archive, order); + origin.addFile(file->getIndex()); } -DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &path) +void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset) { - return getSubDirectoryRecursive(path, false, -1); -} + WIN32_FIND_DATAW findData; -const FileEntry::Ptr DirectoryEntry::findFile( - const std::wstring &name, bool alreadyLowerCase) const -{ - FilesLookup::const_iterator iter; + _snwprintf_s(buffer + bufferOffset, MAXPATH_UNICODE - bufferOffset, _TRUNCATE, L"\\*"); - if (alreadyLowerCase) { - iter = m_FilesLookup.find(FileKey(name)); - } else { - iter = m_FilesLookup.find(FileKey(ToLowerCopy(name))); - } + HANDLE searchHandle = nullptr; - if (iter != m_FilesLookup.end()) { - return m_FileRegister->getFile(iter->second); + if (SupportOptimizedFind()) { + searchHandle = ::FindFirstFileExW( + buffer, FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, + FIND_FIRST_EX_LARGE_FETCH); } else { - return FileEntry::Ptr(); + searchHandle = ::FindFirstFileExW( + buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, nullptr, 0); } -} -const FileEntry::Ptr DirectoryEntry::findFile(const FileKey& key) const -{ - auto iter = m_FilesLookup.find(key); + if (searchHandle != INVALID_HANDLE_VALUE) { + BOOL result = true; - if (iter != m_FilesLookup.end()) { - return m_FileRegister->getFile(iter->second); - } else { - return FileEntry::Ptr(); + while (result) { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if ((wcscmp(findData.cFileName, L".") != 0) && + (wcscmp(findData.cFileName, L"..") != 0)) { + int offset = _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE, L"\\%ls", findData.cFileName); + // recurse into subdirectories + getSubDirectory(findData.cFileName, true, origin.getID())->addFiles(origin, buffer, bufferOffset + offset); + } + } else { + insert(findData.cFileName, origin, findData.ftLastWriteTime, L"", -1); + } + + result = ::FindNextFileW(searchHandle, &findData); + } } + + std::sort(m_SubDirectories.begin(), m_SubDirectories.end(), &DirCompareByName); + ::FindClose(searchHandle); } -bool DirectoryEntry::hasFile(const std::wstring& name) const +void DirectoryEntry::addFiles( + FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, + const std::wstring &archiveName, int order) { - return m_Files.contains(ToLowerCopy(name)); + // 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); + } + + // recurse into subdirectories + for (unsigned int folderIdx = 0; folderIdx < archiveFolder->getNumSubFolders(); ++folderIdx) { + BSA::Folder::Ptr folder = archiveFolder->getSubFolder(folderIdx); + DirectoryEntry *folderEntry = getSubDirectoryRecursive(ToWString(folder->getName(), true), true, origin.getID()); + + folderEntry->addFiles(origin, folder, fileTime, archiveName, order); + } } DirectoryEntry *DirectoryEntry::getSubDirectory( @@ -1111,146 +1230,21 @@ DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive( } } - -FileRegister::FileRegister(boost::shared_ptr originConnection) - : m_OriginConnection(originConnection) -{ - LEAK_TRACE; -} - -FileRegister::~FileRegister() -{ - LEAK_UNTRACE; - m_Files.clear(); -} - -FileEntry::Index FileRegister::generateIndex() -{ - static std::atomic sIndex(0); - return sIndex++; -} - -bool FileRegister::indexValid(FileEntry::Index index) const -{ - return (m_Files.find(index) != m_Files.end()); -} - -FileEntry::Ptr FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent) -{ - FileEntry::Index index = generateIndex(); - m_Files[index] = FileEntry::Ptr(new FileEntry(index, name, parent)); - - return m_Files[index]; -} - -FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const -{ - auto iter = m_Files.find(index); - - if (iter != m_Files.end()) { - return iter->second; - } else { - return FileEntry::Ptr(); - } -} - -void FileRegister::unregisterFile(FileEntry::Ptr file) -{ - bool ignore; - - // unregister from origin - int originID = file->getOrigin(ignore); - m_OriginConnection->getByID(originID).removeFile(file->getIndex()); - const auto& alternatives = file->getAlternatives(); - - for (auto iter = alternatives.begin(); iter != alternatives.end(); ++iter) { - m_OriginConnection->getByID(iter->first).removeFile(file->getIndex()); - } - - // unregister from directory - if (file->getParent() != nullptr) { - file->getParent()->removeFile(file->getIndex()); - } -} - -bool FileRegister::removeFile(FileEntry::Index index) -{ - auto iter = m_Files.find(index); - - if (iter != m_Files.end()) { - unregisterFile(iter->second); - m_Files.erase(index); - return true; - } else { - log::error(QObject::tr("invalid file index for remove: {}").toStdString(), index); - return false; - } -} - -void FileRegister::removeOrigin(FileEntry::Index index, int originID) -{ - auto iter = m_Files.find(index); - - if (iter != m_Files.end()) { - if (iter->second->removeOrigin(originID)) { - unregisterFile(iter->second); - m_Files.erase(iter); - } - } else { - log::error(QObject::tr("invalid file index for remove (for origin): {}").toStdString(), index); - } -} - -void FileRegister::removeOriginMulti( - std::set indices, int originID, time_t notAfter) +void DirectoryEntry::removeDirRecursive() { - std::vector removedFiles; - - for (auto iter = indices.begin(); iter != indices.end(); ) { - auto pos = m_Files.find(*iter); - - if (pos != m_Files.end() - && (pos->second->lastAccessed() < notAfter) - && pos->second->removeOrigin(originID)) { - removedFiles.push_back(pos->second); - m_Files.erase(pos); - ++iter; - } else { - indices.erase(iter++); - } + while (!m_Files.empty()) { + m_FileRegister->removeFile(m_Files.begin()->second); } - // optimization: this is only called when disabling an origin and in this case - // we don't have to remove the file from the origin - - // need to remove files from their parent directories. multiple ways to go - // about this: - // a) for each file, search its parents file-list (preferably by name) and - // remove what is found - // b) gather the parent directories, go through the file list for each once - // and remove all files that have been removed - // - // the latter should be faster when there are many files in few directories. - // since this is called only when disabling an origin that is probably - // frequently the case - - std::set parents; - for (const FileEntry::Ptr &file : removedFiles) { - if (file->getParent() != nullptr) { - parents.insert(file->getParent()); - } - } + m_FilesLookup.clear(); - for (DirectoryEntry *parent : parents) { - parent->removeFiles(indices); + for (DirectoryEntry *entry : m_SubDirectories) { + entry->removeDirRecursive(); + delete entry; } -} -void FileRegister::sortOrigins() -{ - for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) { - iter->second->sortOrigins(); - } + m_SubDirectories.clear(); + m_SubDirectoriesMap.clear(); } } // namespace MOShared diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index bd72c208..98366493 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -69,20 +69,29 @@ public: FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent); ~FileEntry(); - Index getIndex() const { return m_Index; } + Index getIndex() const + { + return m_Index; + } - time_t lastAccessed() const { return m_LastAccessed; } + time_t lastAccessed() const + { + return m_LastAccessed; + } - void addOrigin(int origin, FILETIME fileTime, const std::wstring &archive, int order); + void addOrigin( + int origin, FILETIME fileTime, const std::wstring &archive, int order); - // remove the specified origin from the list of origins that contain this file. if no origin is left, - // the file is effectively deleted and true is returned. otherwise, false is returned + // remove the specified origin from the list of origins that contain this + // file. if no origin is left, the file is effectively deleted and true is + // returned. otherwise, false is returned bool removeOrigin(int origin); void sortOrigins(); - // gets the list of alternative origins (origins with lower priority than the primary one). - // if sortOrigins has been called, it is sorted by priority (ascending) + // gets the list of alternative origins (origins with lower priority than + // the primary one). if sortOrigins has been called, it is sorted by priority + // (ascending) const AlternativesVector &getAlternatives() const { return m_Alternatives; @@ -320,7 +329,10 @@ public: void propagateOrigin(int origin); - const std::wstring &getName() const; + const std::wstring &getName() const + { + return m_Name; + } boost::shared_ptr getFileRegister() { @@ -400,7 +412,8 @@ public: // if directory is not nullptr, the referenced variable will be set to the // path containing the file // - const FileEntry::Ptr searchFile(const std::wstring &path, const DirectoryEntry **directory) const; + const FileEntry::Ptr searchFile( + const std::wstring &path, const DirectoryEntry **directory) const; void insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime); -- cgit v1.3.1 From aa63bffd350727041e8fdd4a7fc884fda80a0a4a Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 14 Jan 2020 15:58:11 -0500 Subject: missing toStdString() from merge --- src/shared/directoryentry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index c93df665..c44be9a1 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -909,7 +909,7 @@ const FileEntry::Ptr DirectoryEntry::searchFile( if (temp != nullptr) { if (len >= path.size()) { - log::error(QObject::tr("unexpected end of path")); + log::error(QObject::tr("unexpected end of path").toStdString()); return FileEntry::Ptr(); } -- 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/shared/directoryentry.cpp') diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 39cdd9c4..33a70798 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -48,6 +48,19 @@ void FileTreeItem::remove(std::size_t i) m_children.erase(m_children.begin() + i); } +void FileTreeItem::remove(std::size_t from, std::size_t n) +{ + if ((from + n) > m_children.size()) { + log::error("{}: can't remove children from {} n={}", debugName(), from, n); + return; + } + + auto begin = m_children.begin() + from; + auto end = begin + n; + + m_children.erase(begin, end); +} + QString FileTreeItem::virtualPath() const { QString s = "Data\\"; diff --git a/src/filetreeitem.h b/src/filetreeitem.h index fb9eccce..70757ca2 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -34,6 +34,7 @@ public: void insert(std::unique_ptr child, std::size_t at); void remove(std::size_t i); + void remove(std::size_t from, std::size_t n); void clear() { diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index ed62b8ae..91dd4b90 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -20,7 +20,7 @@ void trace(F&&) FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""), - m_flags(NoFlags) + m_flags(NoFlags), m_isRefreshing(false) { m_root.setExpanded(true); @@ -39,6 +39,9 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : void FileTreeModel::refresh() { + m_isRefreshing = true; + Guard g([&]{ m_isRefreshing = false; }); + if (m_root.hasChildren()) { TimeThis tt("FileTreeModel::update()"); update(m_root, *m_core.directoryStructure(), L""); @@ -65,6 +68,10 @@ bool FileTreeModel::showArchives() const void FileTreeModel::ensureLoaded(FileTreeItem* item) const { + if (m_isRefreshing) { + return; + } + if (!item) { log::error("ensureLoaded(): item is null"); return; @@ -247,7 +254,7 @@ void FileTreeModel::updateDirectories( }); int row = 0; - std::vector remove; + std::list remove; std::unordered_set seen; for (auto&& item : parentItem.children()) { @@ -329,26 +336,59 @@ void FileTreeModel::updateDirectories( parentItem.debugName()); }); - for (auto* toRemove : remove) { - const auto& cs = parentItem.children(); - for (std::size_t i=0; i(i), 0); + if (!parentIndex.isValid()) { + parentIndex = parent(indexFromItem( + toRemove, static_cast(i), 0)); + } - const auto parentIndex = parent(itemIndex); - const int first = static_cast(i); - const int last = static_cast(i); + if (first == -1) { + first = i; + last = i; + } else if (i == (last + 1)) { + last = i; + } else { + beginRemoveRows(parentIndex, first, last); - beginRemoveRows(parentIndex, first, last); - parentItem.remove(i); - endRemoveRows(); + parentItem.remove( + static_cast(first), + static_cast(last - first + 1)); + + endRemoveRows(); + + first = i; + last = i; + } + remove.erase(itor); break; } } } + + if (first != -1) { + beginRemoveRows(parentIndex, first, last); + + parentItem.remove( + static_cast(first), + static_cast(last - first + 1)); + + endRemoveRows(); + } } diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 8a1738b3..c363266a 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -57,6 +57,7 @@ private: mutable IconFetcher m_iconFetcher; mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; + bool m_isRefreshing; bool showConflicts() const { diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index c44be9a1..819075ae 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -19,7 +19,6 @@ along with Mod Organizer. If not, see . #include "directoryentry.h" #include "windows_error.h" -#include "leaktrace.h" #include "error_report.h" #include #include @@ -32,7 +31,6 @@ along with Mod Organizer. If not, see . #include #include #include -#include namespace MOShared { @@ -80,12 +78,6 @@ public: OriginConnection() : m_NextID(0) { - LEAK_TRACE; - } - - ~OriginConnection() - { - LEAK_UNTRACE; } FilesOrigin& createOrigin( @@ -166,19 +158,12 @@ FileEntry::FileEntry() : m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr), m_LastAccessed(time(nullptr)) { - LEAK_TRACE; } FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) : m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent), m_LastAccessed(time(nullptr)) { - LEAK_TRACE; -} - -FileEntry::~FileEntry() -{ - LEAK_UNTRACE; } void FileEntry::addOrigin( @@ -399,7 +384,6 @@ bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) FilesOrigin::FilesOrigin() : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0) { - LEAK_TRACE; } FilesOrigin::FilesOrigin(const FilesOrigin &reference) @@ -411,7 +395,6 @@ FilesOrigin::FilesOrigin(const FilesOrigin &reference) , m_FileRegister(reference.m_FileRegister) , m_OriginConnection(reference.m_OriginConnection) { - LEAK_TRACE; } FilesOrigin::FilesOrigin( @@ -422,12 +405,6 @@ FilesOrigin::FilesOrigin( m_Priority(priority), m_FileRegister(fileRegister), m_OriginConnection(originConnection) { - LEAK_TRACE; -} - -FilesOrigin::~FilesOrigin() -{ - LEAK_UNTRACE; } void FilesOrigin::setPriority(int priority) @@ -504,13 +481,6 @@ bool FilesOrigin::containsArchive(std::wstring archiveName) FileRegister::FileRegister(boost::shared_ptr originConnection) : m_OriginConnection(originConnection) { - LEAK_TRACE; -} - -FileRegister::~FileRegister() -{ - LEAK_UNTRACE; - m_Files.clear(); } bool FileRegister::indexValid(FileEntry::Index index) const @@ -650,7 +620,6 @@ DirectoryEntry::DirectoryEntry( { m_FileRegister.reset(new FileRegister(m_OriginConnection)); m_Origins.insert(originID); - LEAK_TRACE; } DirectoryEntry::DirectoryEntry( @@ -660,13 +629,11 @@ DirectoryEntry::DirectoryEntry( m_FileRegister(fileRegister), m_OriginConnection(originConnection), m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(false) { - LEAK_TRACE; m_Origins.insert(originID); } DirectoryEntry::~DirectoryEntry() { - LEAK_UNTRACE; clear(); } @@ -680,7 +647,7 @@ void DirectoryEntry::clear() } m_SubDirectories.clear(); - m_SubDirectoriesMap.clear(); + m_SubDirectoriesLookup.clear(); } void DirectoryEntry::addFromOrigin( @@ -735,7 +702,7 @@ void DirectoryEntry::addFromBSA( stream << QObject::tr("invalid bsa file: ").toStdString() << ToString(fileName, false) - << " errorcode " << res << " - " << ::GetLastError(); + << " error code " << res << " - " << ::GetLastError(); throw std::runtime_error(stream.str()); } @@ -806,15 +773,15 @@ std::vector DirectoryEntry::getFiles() const DirectoryEntry *DirectoryEntry::findSubDirectory( const std::wstring &name, bool alreadyLowerCase) const { - SubDirectoriesMap::const_iterator itor; + SubDirectoriesLookup::const_iterator itor; if (alreadyLowerCase) { - itor = m_SubDirectoriesMap.find(name); + itor = m_SubDirectoriesLookup.find(name); } else { - itor = m_SubDirectoriesMap.find(ToLowerCopy(name)); + itor = m_SubDirectoriesLookup.find(ToLowerCopy(name)); } - if (itor == m_SubDirectoriesMap.end()) { + if (itor == m_SubDirectoriesLookup.end()) { return nullptr; } @@ -936,48 +903,7 @@ void DirectoryEntry::insertFile( void DirectoryEntry::removeFile(FileEntry::Index index) { - if (!m_FilesLookup.empty()) { - auto iter = std::find_if( - m_FilesLookup.begin(), m_FilesLookup.end(), - [&index](auto&& pair) { return (pair.second == index); } - ); - - if (iter != m_FilesLookup.end()) { - m_FilesLookup.erase(iter); - } else { - log::error( - QObject::tr("file \"{}\" not in directory for lookup \"{}\"").toStdString(), - m_FileRegister->getFile(index)->getName(), this->getName()); - } - } else { - log::error( - QObject::tr("file \"{}\" not in directory \"{}\" for lookup, directory empty").toStdString(), - m_FileRegister->getFile(index)->getName(), this->getName()); - } - - if (!m_Files.empty()) { - auto iter = std::find_if( - m_Files.begin(), m_Files.end(), - [&index](const std::pair &iter) -> bool { - return iter.second == index; } - ); - - if (iter != m_Files.end()) { - m_Files.erase(iter); - } else { - log::error( - QObject::tr("file \"{}\" not in directory \"{}\"").toStdString(), - m_FileRegister->getFile(index)->getName(), this->getName()); - } - } else { - log::error( - QObject::tr("file \"{}\" not in directory \"{}\", directory empty").toStdString(), - m_FileRegister->getFile(index)->getName(), this->getName()); - } - - if (m_Files.size() != m_FilesLookup.size()) { - DebugBreak(); - } + removeFileFromList(index); } bool DirectoryEntry::removeFile(const std::wstring &filePath, int *origin) @@ -1009,21 +935,7 @@ void DirectoryEntry::removeDir(const std::wstring &path) if (CaseInsensitiveEqual(entry->getName(), path)) { entry->removeDirRecursive(); - - bool found = false; - for (auto iter2=m_SubDirectoriesMap.begin(); iter2!=m_SubDirectoriesMap.end(); ++iter2) { - if (iter2->second == entry) { - m_SubDirectoriesMap.erase(iter2); - found = true; - break; - } - } - - if (!found) { - log::error("entry {} not in sub directories map", entry->getName()); - } - - m_SubDirectories.erase(iter); + removeDirectoryFromList(iter); delete entry; break; } @@ -1058,10 +970,6 @@ bool DirectoryEntry::remove(const std::wstring &fileName, int *origin) b = m_FileRegister->removeFile(iter->second); } - if (m_Files.size() != m_FilesLookup.size()) { - DebugBreak(); - } - return b; } @@ -1085,41 +993,21 @@ FilesOrigin &DirectoryEntry::createOrigin( void DirectoryEntry::removeFiles(const std::set &indices) { - for (auto iter = m_Files.begin(); iter != m_Files.end();) { - if (indices.find(iter->second) != indices.end()) { - iter = m_Files.erase(iter); - } else { - ++iter; - } - } - - for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) { - if (indices.find(iter->second) != indices.end()) { - iter = m_FilesLookup.erase(iter); - } else { - ++iter; - } - } + removeFilesFromList(indices); } void DirectoryEntry::insert( const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) { - std::wstring fileNameLower = ToLowerCopy(fileName); - auto iter = m_Files.find(fileNameLower); + auto iter = m_Files.find(ToLowerCopy(fileName)); FileEntry::Ptr file; if (iter != m_Files.end()) { file = m_FileRegister->getFile(iter->second); } else { file = m_FileRegister->createFile(fileName, this); - m_Files.emplace(fileNameLower, file->getIndex()); - m_FilesLookup.emplace(fileNameLower, file->getIndex()); - } - - if (m_Files.size() != m_FilesLookup.size()) { - DebugBreak(); + addFileToList(*file); } file->addOrigin(origin.getID(), fileTime, archive, order); @@ -1198,8 +1086,7 @@ DirectoryEntry *DirectoryEntry::getSubDirectory( auto* entry = new DirectoryEntry( name, this, originID, m_FileRegister, m_OriginConnection); - m_SubDirectories.push_back(entry); - m_SubDirectoriesMap.emplace(ToLowerCopy(name), entry); + addDirectoryToList(entry); return entry; } else { @@ -1244,7 +1131,87 @@ void DirectoryEntry::removeDirRecursive() } m_SubDirectories.clear(); - m_SubDirectoriesMap.clear(); + m_SubDirectoriesLookup.clear(); +} + +void DirectoryEntry::addDirectoryToList(DirectoryEntry* e) +{ + m_SubDirectories.push_back(e); + m_SubDirectoriesLookup.emplace(ToLowerCopy(e->getName()), e); +} + +void DirectoryEntry::removeDirectoryFromList(SubDirectories::iterator itor) +{ + const auto* entry = *itor; + + { + auto itor2 = std::find_if( + m_SubDirectoriesLookup.begin(), m_SubDirectoriesLookup.end(), + [&](auto&& d) { return (d.second == entry); }); + + if (itor2 == m_SubDirectoriesLookup.end()) { + log::error("entry {} not in sub directories map", entry->getName()); + } else { + m_SubDirectoriesLookup.erase(itor2); + } + } + + m_SubDirectories.erase(itor); +} + +void DirectoryEntry::removeFileFromList(FileEntry::Index index) +{ + auto removeFrom = [&](auto& list) { + auto iter = std::find_if( + list.begin(), list.end(), + [&index](auto&& pair) { return (pair.second == index); } + ); + + if (iter == list.end()) { + auto f = m_FileRegister->getFile(index); + + if (f) { + log::error( + "can't remove file '{}', not in directory entry '{}'", + f->getName(), getName()); + } else { + log::error( + "can't remove file with index {}, not in directory entry '{}' and " + "not in register", + index, getName()); + } + } else { + list.erase(iter); + } + }; + + removeFrom(m_FilesLookup); + removeFrom(m_Files); +} + +void DirectoryEntry::removeFilesFromList(const std::set& indices) +{ + for (auto iter = m_Files.begin(); iter != m_Files.end();) { + if (indices.find(iter->second) != indices.end()) { + iter = m_Files.erase(iter); + } else { + ++iter; + } + } + + for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) { + if (indices.find(iter->second) != indices.end()) { + iter = m_FilesLookup.erase(iter); + } else { + ++iter; + } + } +} + +void DirectoryEntry::addFileToList(const FileEntry& f) +{ + m_Files.emplace(ToLowerCopy(f.getName()), f.getIndex()); + m_FilesLookup.emplace(ToLowerCopy(f.getName()), f.getIndex()); } } // namespace MOShared diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 98366493..91c2a140 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -67,7 +67,6 @@ public: FileEntry(); FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent); - ~FileEntry(); Index getIndex() const { @@ -160,7 +159,6 @@ class FilesOrigin public: FilesOrigin(); FilesOrigin(const FilesOrigin &reference); - ~FilesOrigin(); // sets priority for this origin, but it will overwrite the existing mapping // for this priority, the previous origin will no longer be referenced @@ -226,7 +224,6 @@ class FileRegister { public: FileRegister(boost::shared_ptr originConnection); - ~FileRegister(); bool indexValid(FileEntry::Index index) const; @@ -351,7 +348,8 @@ public: std::vector::const_iterator &begin, std::vector::const_iterator &end) const { - begin = m_SubDirectories.begin(); end = m_SubDirectories.end(); + begin = m_SubDirectories.begin(); + end = m_SubDirectories.end(); } template @@ -442,7 +440,8 @@ public: private: using FilesMap = std::map; using FilesLookup = std::unordered_map; - using SubDirectoriesMap = std::unordered_map; + using SubDirectories = std::vector; + using SubDirectoriesLookup = std::unordered_map; boost::shared_ptr m_FileRegister; boost::shared_ptr m_OriginConnection; @@ -450,8 +449,8 @@ private: std::wstring m_Name; FilesMap m_Files; FilesLookup m_FilesLookup; - std::vector m_SubDirectories; - SubDirectoriesMap m_SubDirectoriesMap; + SubDirectories m_SubDirectories; + SubDirectoriesLookup m_SubDirectoriesLookup; DirectoryEntry *m_Parent; std::set m_Origins; @@ -479,6 +478,13 @@ private: const std::wstring &path, bool create, int originID = -1); void removeDirRecursive(); + + void addDirectoryToList(DirectoryEntry* e); + void removeDirectoryFromList(SubDirectories::iterator itor); + + void addFileToList(const FileEntry& f); + void removeFileFromList(FileEntry::Index index); + void removeFilesFromList(const std::set& indices); }; } // namespace MOShared -- cgit v1.3.1 From a7a406e5538b343d87b3221b13b7bf3503bbfd70 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 22 Jan 2020 01:03:00 -0500 Subject: preparing for multiple origins shell menus split exec() from createMenu() --- src/envshell.cpp | 128 ++++++++++++++++++++++++++++-------------- src/envshell.h | 9 ++- src/filetree.cpp | 68 +++++++++++++++++++++- src/shared/directoryentry.cpp | 16 ++++++ src/shared/directoryentry.h | 12 +++- 5 files changed, 185 insertions(+), 48 deletions(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/envshell.cpp b/src/envshell.cpp index 7754928e..992ce1ef 100644 --- a/src/envshell.cpp +++ b/src/envshell.cpp @@ -65,15 +65,7 @@ public: WndProcFilter(QMainWindow* mw, IContextMenu* cm) : m_mw(mw), m_cm(cm), m_cm2(nullptr), m_cm3(nullptr) { - IContextMenu2* cm2 = nullptr; - if (SUCCEEDED(cm->QueryInterface(IID_IContextMenu2, (void**)&cm2))) { - m_cm2.reset(cm2); - } - - IContextMenu3* cm3 = nullptr; - if (SUCCEEDED(cm->QueryInterface(IID_IContextMenu3, (void**)&cm3))) { - m_cm3.reset(cm3); - } + createInterfaces(); } ~WndProcFilter() @@ -86,6 +78,9 @@ public: bool nativeEventFilter(const QByteArray& type, void* m, long* lresultOut) override { MSG* msg = (MSG*)m; + if (!msg) { + return false; + } if (msg->message == WM_MENUSELECT) { HANDLE_WM_MENUSELECT(msg->hwnd, msg->wParam, msg->lParam, onMenuSelect); @@ -129,6 +124,23 @@ private: COMPtr m_cm2; COMPtr m_cm3; + void createInterfaces() + { + if (!m_cm) { + return; + } + + IContextMenu2* cm2 = nullptr; + if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu2, (void**)&cm2))) { + m_cm2.reset(cm2); + } + + IContextMenu3* cm3 = nullptr; + if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu3, (void**)&cm3))) { + m_cm3.reset(cm3); + } + } + // adapted from // https://devblogs.microsoft.com/oldnewthing/20040928-00/?p=37723 // @@ -232,14 +244,52 @@ void ShellMenu::addFile(QFileInfo fi) } void ShellMenu::exec(QWidget* parent, const QPoint& pos) +{ + auto* mw = getMainWindow(parent); + HMENU menu = getMenu(); + if (!menu) { + return; + } + + try + { + const int cmd = runMenu(mw, m_cm.get(), m_menu.get(), pos); + if (cmd <= 0) { + return; + } + + invoke(mw, pos, cmd - QCM_FIRST, m_cm.get()); + } + catch(MenuFailed& e) + { + if (m_files.size() == 1) { + log::error( + "can't exec shell menu for '{}': {}", + QDir::toNativeSeparators(m_files[0].absoluteFilePath()), e.what()); + } else { + log::error( + "can't exec shell menu for {} files: {}", + m_files.size(), e.what()); + } + } +} + +HMENU ShellMenu::getMenu() +{ + if (!m_menu) { + createMenu(); + } + + return m_menu.get(); +} + +void ShellMenu::createMenu() { if (m_files.empty()) { log::warn("showShellMenu(): no files given"); return; } - auto* mw = getMainWindow(parent); - try { auto idls = createIdls(m_files); @@ -252,27 +302,12 @@ void ShellMenu::exec(QWidget* parent, const QPoint& pos) IdlsFreer freer(idls); auto array = createItemArray(idls); - auto cm = createContextMenu(array.get()); - auto hmenu = createMenu(cm.get()); - - const int cmd = runMenu(mw, cm.get(), hmenu.get(), pos); - if (cmd <= 0) { - return; - } - - invoke(mw, pos, cmd - QCM_FIRST, cm.get()); + m_cm = createContextMenu(array.get()); + m_menu = createMenu(m_cm.get()); } catch(DummyMenu& dm) { - try - { - showDummyMenu(mw, pos, dm.what()); - } - catch(MenuFailed& e) - { - log::error("{}", dm.what()); - log::error("additionally, creating the dummy menu failed: {}", e.what()); - } + m_menu = createDummyMenu(dm.what()); } catch(MenuFailed& e) { @@ -285,27 +320,34 @@ void ShellMenu::exec(QWidget* parent, const QPoint& pos) "can't create shell menu for {} files: {}", m_files.size(), e.what()); } + + m_menu = createDummyMenu(QObject::tr("No menu available")); } } -void ShellMenu::showDummyMenu( - QMainWindow* mw, const QPoint& pos, const QString& what) +HMenuPtr ShellMenu::createDummyMenu(const QString& what) { - HMENU menu = CreatePopupMenu(); - if (!menu) { - const auto e = GetLastError(); - throw MenuFailed(e, "CreatePopupMenu failed"); - } + try + { + HMENU menu = CreatePopupMenu(); + if (!menu) { + const auto e = GetLastError(); + throw MenuFailed(e, "CreatePopupMenu failed"); + } - if (!AppendMenuW(menu, MF_STRING | MF_DISABLED, 0, what.toStdWString().c_str())) { - const auto e = GetLastError(); - throw MenuFailed(e, "AppendMenuW failed"); + if (!AppendMenuW(menu, MF_STRING | MF_DISABLED, 0, what.toStdWString().c_str())) { + const auto e = GetLastError(); + throw MenuFailed(e, "AppendMenuW failed"); + } + + return HMenuPtr(menu); } + catch(MenuFailed& e) + { + log::error("{}", what); + log::error("additionally, creating the dummy menu failed: {}", e.what()); - const auto hwnd = (HWND)mw->winId(); - if (!TrackPopupMenuEx(menu, 0, pos.x(), pos.y(), hwnd, nullptr)) { - const auto e = GetLastError(); - throw MenuFailed(e, "TrackPopupMenuEx failed"); + return {}; } } diff --git a/src/envshell.h b/src/envshell.h index 86d9b0fc..f25aeda7 100644 --- a/src/envshell.h +++ b/src/envshell.h @@ -12,10 +12,16 @@ class ShellMenu { public: void addFile(QFileInfo fi); + void exec(QWidget* parent, const QPoint& pos); + HMENU getMenu(); private: std::vector m_files; + COMPtr m_cm; + HMenuPtr m_menu; + + void createMenu(); QMainWindow* getMainWindow(QWidget* w); COMPtr createShellItem(const std::wstring& path); @@ -25,9 +31,10 @@ private: COMPtr createItemArray(std::vector& idls); COMPtr createContextMenu(IShellItemArray* array); HMenuPtr createMenu(IContextMenu* cm); + HMenuPtr createDummyMenu(const QString& what); + int runMenu(QMainWindow* mw, IContextMenu* cm, HMENU menu, const QPoint& p); void invoke(QMainWindow* mw, const QPoint& p, int cmd, IContextMenu* cm); - void showDummyMenu(QMainWindow* mw, const QPoint& pos, const QString& what); }; } // namespace diff --git a/src/filetree.cpp b/src/filetree.cpp index 404c994b..7128665d 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -468,7 +468,8 @@ void FileTree::onContextMenu(const QPoint &pos) void FileTree::showShellMenu(QPoint pos) { - env::ShellMenu menu; + // menus by origin + std::map menus; for (auto&& index : m_tree->selectionModel()->selectedRows()) { auto* item = m_model->itemFromIndex(index); @@ -476,10 +477,71 @@ void FileTree::showShellMenu(QPoint pos) continue; } - menu.addFile(item->realPath()); + menus[item->originID()].addFile(item->realPath()); + + if (item->isConflicted()) { + const auto file = m_core.directoryStructure()->searchFile( + item->dataRelativeFilePath().toStdWString(), nullptr); + + if (!file) { + log::error( + "file '{}' not found, data path={}, real path={}", + item->filename(), item->dataRelativeFilePath(), item->realPath()); + + continue; + } + + const auto alts = file->getAlternatives(); + if (alts.empty()) { + log::warn( + "file '{}' has no alternative origins but is marked as conflicted", + item->dataRelativeFilePath()); + } + + for (auto&& alt : alts) { + auto* dir = file->getParent(); + if (!dir) { + log::error( + "file {} from origin {} has no parent", + item->dataRelativeFilePath(), alt.first); + + continue; + } + + const auto* origin = dir->findOriginByID(alt.first); + if (!origin) { + log::error( + "origin {} for file {} cannot be found", + alt.first, item->dataRelativeFilePath()); + + continue; + } + + const auto originFile = origin->findFile(file->getIndex()); + if (!originFile) { + log::error( + "file {} not found in origin {} ({})", + item->dataRelativeFilePath(), origin->getName(), file->getIndex()); + + continue; + } + + menus[alt.first].addFile( + QString::fromStdWString(originFile->getFullPath())); + } + } } - menu.exec(m_tree, m_tree->viewport()->mapToGlobal(pos)); + if (menus.empty()) { + log::warn("no menus to show"); + return; + } + else if (menus.size() == 1) { + auto& menu = menus.begin()->second; + menu.exec(m_tree, m_tree->viewport()->mapToGlobal(pos)); + } else { + + } } void FileTree::addDirectoryMenus(QMenu&, FileTreeItem&) diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 819075ae..460431a4 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -104,6 +104,17 @@ public: return m_Origins[ID]; } + const FilesOrigin* findByID(Index ID) const + { + auto itor = m_Origins.find(ID); + + if (itor == m_Origins.end()) { + return nullptr; + } else { + return &itor->second; + } + } + FilesOrigin &getByName(const std::wstring &name) { std::map::iterator iter = m_OriginsNameMap.find(name); @@ -736,6 +747,11 @@ FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const return m_OriginConnection->getByName(name); } +const FilesOrigin* DirectoryEntry::findOriginByID(int ID) const +{ + return m_OriginConnection->findByID(ID); +} + int DirectoryEntry::anyOrigin() const { bool ignore; diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index b5a1dced..69eb7574 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -63,7 +63,16 @@ class FileEntry public: typedef unsigned int Index; typedef boost::shared_ptr Ptr; - typedef std::vector>> AlternativesVector; + + // a vector of {originId, {archiveName, order}} + // + // if a file is in an archive, archiveName is the name of the bsa and order + // is the order of the associated plugin in the plugins list + // + // is a file is not in an archive, archiveName is empty and order is usually + // -1 + typedef std::vector>> + AlternativesVector; FileEntry(); FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent); @@ -339,6 +348,7 @@ public: bool originExists(const std::wstring &name) const; FilesOrigin &getOriginByID(int ID) const; FilesOrigin &getOriginByName(const std::wstring &name) const; + const FilesOrigin* findOriginByID(int ID) const; int anyOrigin() const; -- cgit v1.3.1 From 2a0e78e3cf0c1106a1fb7e470148f6e0b093b2b1 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 22 Jan 2020 02:54:29 -0500 Subject: fixed bad path for alternate origins finished ShellMenuCollection, had to split a bunch of things --- src/envshell.cpp | 499 ++++++++++++++++++++++++------------------ src/envshell.h | 36 ++- src/filetree.cpp | 60 ++--- src/shared/directoryentry.cpp | 14 +- src/shared/directoryentry.h | 7 +- 5 files changed, 358 insertions(+), 258 deletions(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/envshell.cpp b/src/envshell.cpp index 58948d31..e01bb804 100644 --- a/src/envshell.cpp +++ b/src/envshell.cpp @@ -62,17 +62,12 @@ struct IdlsFreer class WndProcFilter : public QAbstractNativeEventFilter { public: - WndProcFilter(QMainWindow* mw, IContextMenu* cm) - : m_mw(mw), m_cm(cm), m_cm2(nullptr), m_cm3(nullptr) - { - createInterfaces(); - } + using function_type = std::function< + bool (HWND hwnd, UINT m, WPARAM wp, LPARAM lp, LRESULT* out)>; - ~WndProcFilter() + WndProcFilter(function_type f) + : m_f(std::move(f)) { - if (auto* sb=m_mw->statusBar()) { - sb->clearMessage(); - } } bool nativeEventFilter(const QByteArray& type, void* m, long* lresultOut) override @@ -82,194 +77,116 @@ public: return false; } - if (msg->message == WM_MENUSELECT) { - HANDLE_WM_MENUSELECT(msg->hwnd, msg->wParam, msg->lParam, onMenuSelect); - return true; - } - - if (m_cm3) { - LRESULT lresult = 0; + LRESULT lr = 0; - const auto r = m_cm3->HandleMenuMsg2( - msg->message, msg->wParam, msg->lParam, &lresult); + const bool r = m_f(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lr); - if (SUCCEEDED(r)) { - if (lresultOut) { - *lresultOut = lresult; - } - - return true; - } + if (lresultOut) { + *lresultOut = lr; } - if (m_cm2) { - const auto r = m_cm2->HandleMenuMsg( - msg->message, msg->wParam, msg->lParam); - - if (SUCCEEDED(r)) { - if (lresultOut) { - *lresultOut = 0; - } - - return true; - } - } - - return false; + return r; } private: - QMainWindow* m_mw; - IContextMenu* m_cm; - COMPtr m_cm2; - COMPtr m_cm3; - - void createInterfaces() - { - if (!m_cm) { - return; - } - - IContextMenu2* cm2 = nullptr; - if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu2, (void**)&cm2))) { - m_cm2.reset(cm2); - } - - IContextMenu3* cm3 = nullptr; - if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu3, (void**)&cm3))) { - m_cm3.reset(cm3); - } - } + function_type m_f; +}; - // adapted from - // https://devblogs.microsoft.com/oldnewthing/20040928-00/?p=37723 - // - void onMenuSelect( - HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags) - { - if (m_cm && item >= QCM_FIRST && item <= QCM_LAST) { - WCHAR szBuf[MAX_PATH]; - const auto r = IContextMenu_GetCommandString( - m_cm, item - QCM_FIRST, GCS_HELPTEXTW, NULL, szBuf, MAX_PATH); - if (FAILED(r)) { - lstrcpynW(szBuf, L"No help available.", MAX_PATH); - } - if (m_mw) { - if (auto* sb=m_mw->statusBar()) { - sb->showMessage(QString::fromWCharArray(szBuf)); - } - } - } +HWND getHWND(QMainWindow* mw) +{ + if (mw) { + return (HWND)mw->winId(); + } else { + return 0; } +} - // adapted from - // https://devblogs.microsoft.com/oldnewthing/20040928-00/?p=37723 - // - HRESULT IContextMenu_GetCommandString( - IContextMenu *pcm, UINT_PTR idCmd, UINT uFlags, - UINT *pwReserved, LPWSTR pszName, UINT cchMax) - { - // Callers are expected to be using Unicode. - if (!(uFlags & GCS_UNICODE)) { - return E_INVALIDARG; - } +// adapted from +// https://devblogs.microsoft.com/oldnewthing/20040928-00/?p=37723 +// +HRESULT IContextMenu_GetCommandString( + IContextMenu *pcm, UINT_PTR idCmd, UINT uFlags, + UINT *pwReserved, LPWSTR pszName, UINT cchMax) +{ + // Callers are expected to be using Unicode. + if (!(uFlags & GCS_UNICODE)) { + return E_INVALIDARG; + } - // Some context menu handlers have off-by-one bugs and will - // overflow the output buffer. Let’s artificially reduce the - // buffer size so a one-character overflow won’t corrupt memory. - if (cchMax <= 1) { - return E_FAIL; - } + // Some context menu handlers have off-by-one bugs and will + // overflow the output buffer. Let’s artificially reduce the + // buffer size so a one-character overflow won’t corrupt memory. + if (cchMax <= 1) { + return E_FAIL; + } - cchMax--; + cchMax--; - // First try the Unicode message. Preset the output buffer - // with a known value because some handlers return S_OK without - // doing anything. - pszName[0] = L'\0'; + // First try the Unicode message. Preset the output buffer + // with a known value because some handlers return S_OK without + // doing anything. + pszName[0] = L'\0'; - HRESULT hr = pcm->GetCommandString( - idCmd, uFlags, pwReserved, (LPSTR)pszName, cchMax); + HRESULT hr = pcm->GetCommandString( + idCmd, uFlags, pwReserved, (LPSTR)pszName, cchMax); - if (SUCCEEDED(hr) && pszName[0] == L'\0') { - // Rats, a buggy IContextMenu handler that returned success - // even though it failed. - hr = E_NOTIMPL; - } + if (SUCCEEDED(hr) && pszName[0] == L'\0') { + // Rats, a buggy IContextMenu handler that returned success + // even though it failed. + hr = E_NOTIMPL; + } - if (FAILED(hr)) { - // try again with ANSI – pad the buffer with one extra character - // to compensate for context menu handlers that overflow by - // one character. - LPSTR pszAnsi = (LPSTR)LocalAlloc( - LMEM_FIXED, (cchMax + 1) * sizeof(CHAR)); + if (FAILED(hr)) { + // try again with ANSI – pad the buffer with one extra character + // to compensate for context menu handlers that overflow by + // one character. + LPSTR pszAnsi = (LPSTR)LocalAlloc( + LMEM_FIXED, (cchMax + 1) * sizeof(CHAR)); - if (pszAnsi) { - pszAnsi[0] = '\0'; + if (pszAnsi) { + pszAnsi[0] = '\0'; - hr = pcm->GetCommandString( - idCmd, uFlags & ~GCS_UNICODE, pwReserved, pszAnsi, cchMax); + hr = pcm->GetCommandString( + idCmd, uFlags & ~GCS_UNICODE, pwReserved, pszAnsi, cchMax); - if (SUCCEEDED(hr) && pszAnsi[0] == '\0') { - // Rats, a buggy IContextMenu handler that returned success - // even though it failed. - hr = E_NOTIMPL; - } + if (SUCCEEDED(hr) && pszAnsi[0] == '\0') { + // Rats, a buggy IContextMenu handler that returned success + // even though it failed. + hr = E_NOTIMPL; + } - if (SUCCEEDED(hr)) { - if (MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, pszName, cchMax) == 0) { - hr = E_FAIL; - } + if (SUCCEEDED(hr)) { + if (MultiByteToWideChar(CP_ACP, 0, pszAnsi, -1, pszName, cchMax) == 0) { + hr = E_FAIL; } - - LocalFree(pszAnsi); - - } else { - hr = E_OUTOFMEMORY; } - } - - return hr; - } -}; + LocalFree(pszAnsi); -QMainWindow* getMainWindow(QWidget* w) -{ - QWidget* p = w; - - while (p) { - if (auto* mw=dynamic_cast(p)) { - return mw; + } else { + hr = E_OUTOFMEMORY; } - - p = p->parentWidget(); } - return nullptr; + return hr; } -HWND getHWND(QMainWindow* mw) + +ShellMenu::ShellMenu(QMainWindow* mw) + : m_mw(mw) { - if (mw) { - return (HWND)mw->winId(); - } else { - return 0; - } } - void ShellMenu::addFile(QFileInfo fi) { m_files.emplace_back(std::move(fi)); } -void ShellMenu::exec(QWidget* parent, const QPoint& pos) +void ShellMenu::exec(const QPoint& pos) { - auto* mw = getMainWindow(parent); HMENU menu = getMenu(); if (!menu) { return; @@ -277,12 +194,29 @@ void ShellMenu::exec(QWidget* parent, const QPoint& pos) try { - const int cmd = runMenu(mw, m_cm.get(), m_menu.get(), pos); + const auto hwnd = getHWND(m_mw); + + auto filter = std::make_unique( + [&](HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out) { + return wndProc(h, m, wp, lp, out); + }); + + QCoreApplication::instance()->installNativeEventFilter(filter.get()); + + const int cmd = TrackPopupMenuEx( + menu, TPM_RETURNCMD, pos.x(), pos.y(), hwnd, nullptr); + + if (m_mw) { + if (auto* sb=m_mw->statusBar()) { + sb->clearMessage(); + } + } + if (cmd <= 0) { return; } - invoke(mw, pos, cmd - QCM_FIRST, m_cm.get()); + invoke(pos, cmd - QCM_FIRST); } catch(MenuFailed& e) { @@ -301,13 +235,67 @@ void ShellMenu::exec(QWidget* parent, const QPoint& pos) HMENU ShellMenu::getMenu() { if (!m_menu) { - createMenu(); + create(); } return m_menu.get(); } -void ShellMenu::createMenu() +bool ShellMenu::wndProc(HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out) +{ + if (m == WM_MENUSELECT) { + HANDLE_WM_MENUSELECT(h, wp, lp, onMenuSelect); + return true; + } + + if (m_cm3) { + const auto r = m_cm3->HandleMenuMsg2(m, wp, lp, out); + + if (SUCCEEDED(r)) { + return true; + } + } + + if (m_cm2) { + const auto r = m_cm2->HandleMenuMsg(m, wp, lp); + + if (SUCCEEDED(r)) { + if (out) { + *out = 0; + } + + return true; + } + } + + return false; +} + +// adapted from +// https://devblogs.microsoft.com/oldnewthing/20040928-00/?p=37723 +// +void ShellMenu::onMenuSelect( + HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags) +{ + if (m_cm && item >= QCM_FIRST && item <= QCM_LAST) { + WCHAR szBuf[MAX_PATH]; + + const auto r = IContextMenu_GetCommandString( + m_cm.get(), item - QCM_FIRST, GCS_HELPTEXTW, NULL, szBuf, MAX_PATH); + + if (FAILED(r)) { + lstrcpynW(szBuf, L"No help available.", MAX_PATH); + } + + if (m_mw) { + if (auto* sb=m_mw->statusBar()) { + sb->showMessage(QString::fromWCharArray(szBuf)); + } + } + } +} + +void ShellMenu::create() { if (m_files.empty()) { log::warn("showShellMenu(): no files given"); @@ -326,8 +314,9 @@ void ShellMenu::createMenu() IdlsFreer freer(idls); auto array = createItemArray(idls); - m_cm = createContextMenu(array.get()); - m_menu = createMenu(m_cm.get()); + + createContextMenu(array.get()); + createPopupMenu(m_cm.get()); } catch(DummyMenu& dm) { @@ -375,44 +364,6 @@ HMenuPtr ShellMenu::createDummyMenu(const QString& what) } } -COMPtr ShellMenu::createShellItem(const std::wstring& path) -{ - IShellItem* item = nullptr; - - auto r = SHCreateItemFromParsingName( - path.c_str(), nullptr, IID_IShellItem, (void**)&item); - - if (FAILED(r)) { - throw MenuFailed(r, "SHCreateItemFromParsingName failed"); - } - - return COMPtr(item); -} - -COMPtr ShellMenu::getPersistIDList(IShellItem* item) -{ - IPersistIDList* idl = nullptr; - auto r = item->QueryInterface(IID_IPersistIDList, (void**)&idl); - - if (FAILED(r)) { - throw MenuFailed(r, "QueryInterface IID_IPersistIDList failed"); - } - - return COMPtr(idl); -} - -CoTaskMemPtr ShellMenu::getIDList(IPersistIDList* pidlist) -{ - LPITEMIDLIST absIdl = nullptr; - auto r = pidlist->GetIDList(&absIdl); - - if (FAILED(r)) { - throw MenuFailed(r, "GetIDList failed"); - } - - return CoTaskMemPtr(absIdl); -} - std::vector ShellMenu::createIdls( const std::vector& files) { @@ -455,7 +406,7 @@ COMPtr ShellMenu::createItemArray( return COMPtr(array); } -COMPtr ShellMenu::createContextMenu(IShellItemArray* array) +void ShellMenu::createContextMenu(IShellItemArray* array) { IContextMenu* cm = nullptr; @@ -466,10 +417,24 @@ COMPtr ShellMenu::createContextMenu(IShellItemArray* array) throw MenuFailed(r, "BindToHandler failed"); } - return COMPtr(cm); + m_cm.reset(cm); + + { + IContextMenu2* cm2 = nullptr; + if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu2, (void**)&cm2))) { + m_cm2.reset(cm2); + } + } + + { + IContextMenu3* cm3 = nullptr; + if (SUCCEEDED(m_cm->QueryInterface(IID_IContextMenu3, (void**)&cm3))) { + m_cm3.reset(cm3); + } + } } -HMenuPtr ShellMenu::createMenu(IContextMenu* cm) +void ShellMenu::createPopupMenu(IContextMenu* cm) { HMENU hmenu = CreatePopupMenu(); if (!hmenu) { @@ -484,24 +449,50 @@ HMenuPtr ShellMenu::createMenu(IContextMenu* cm) throw MenuFailed(r, "QueryContextMenu failed"); } - return HMenuPtr(hmenu); + m_menu.reset(hmenu); } -int ShellMenu::runMenu( - QMainWindow* mw, IContextMenu* cm, HMENU menu, const QPoint& p) +COMPtr ShellMenu::createShellItem(const std::wstring& path) { - const auto hwnd = getHWND(mw); + IShellItem* item = nullptr; - auto filter = std::make_unique(mw, cm); - QCoreApplication::instance()->installNativeEventFilter(filter.get()); + auto r = SHCreateItemFromParsingName( + path.c_str(), nullptr, IID_IShellItem, (void**)&item); + + if (FAILED(r)) { + throw MenuFailed(r, "SHCreateItemFromParsingName failed"); + } + + return COMPtr(item); +} + +COMPtr ShellMenu::getPersistIDList(IShellItem* item) +{ + IPersistIDList* idl = nullptr; + auto r = item->QueryInterface(IID_IPersistIDList, (void**)&idl); + + if (FAILED(r)) { + throw MenuFailed(r, "QueryInterface IID_IPersistIDList failed"); + } - return TrackPopupMenuEx(menu, TPM_RETURNCMD, p.x(), p.y(), hwnd, nullptr); + return COMPtr(idl); } -void ShellMenu::invoke( - QMainWindow* mw, const QPoint& p, int cmd, IContextMenu* cm) +CoTaskMemPtr ShellMenu::getIDList(IPersistIDList* pidlist) { - const auto hwnd = getHWND(mw); + LPITEMIDLIST absIdl = nullptr; + auto r = pidlist->GetIDList(&absIdl); + + if (FAILED(r)) { + throw MenuFailed(r, "GetIDList failed"); + } + + return CoTaskMemPtr(absIdl); +} + +void ShellMenu::invoke(const QPoint& p, int cmd) +{ + const auto hwnd = getHWND(m_mw); CMINVOKECOMMANDINFOEX info = {}; @@ -525,7 +516,7 @@ void ShellMenu::invoke( info.fMask |= CMIC_MASK_CONTROL_DOWN; } - const auto r = cm->InvokeCommand((CMINVOKECOMMANDINFO*)&info); + const auto r = m_cm->InvokeCommand((CMINVOKECOMMANDINFO*)&info); if (FAILED(r)) { throw MenuFailed(r, fmt::format("InvokeCommand failed, verb={}", cmd)); @@ -533,12 +524,17 @@ void ShellMenu::invoke( } +ShellMenuCollection::ShellMenuCollection(QMainWindow* mw) + : m_mw(mw), m_active(nullptr) +{ +} + void ShellMenuCollection::add(QString name, ShellMenu m) { m_menus.push_back({name, std::move(m)}); } -void ShellMenuCollection::exec(QWidget* parent, const QPoint& pos) +void ShellMenuCollection::exec(const QPoint& pos) { HMENU menu = ::CreatePopupMenu(); if (!menu) { @@ -572,10 +568,77 @@ void ShellMenuCollection::exec(QWidget* parent, const QPoint& pos) } } - auto* mw = getMainWindow(parent); - auto hwnd = getHWND(mw); + auto hwnd = getHWND(m_mw); + + auto filter = std::make_unique( + [&](HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out) { + return wndProc(h, m, wp, lp, out); + }); + + QCoreApplication::instance()->installNativeEventFilter(filter.get()); + + const int cmd = TrackPopupMenuEx( + menu, TPM_RETURNCMD, pos.x(), pos.y(), hwnd, nullptr); + + if (m_mw) { + if (auto* sb=m_mw->statusBar()) { + sb->clearMessage(); + } + } + + if (cmd <= 0) { + return; + } + + if (!m_active) { + log::debug("SMC: command {} selected without active submenu"); + return; + } + + const auto realCmd = cmd - QCM_FIRST; + + log::debug("SMC: invoking {} on {}", realCmd, m_active->name); + m_active->menu.invoke(pos, realCmd); +} + +bool ShellMenuCollection::wndProc( + HWND h, UINT m, WPARAM wp, LPARAM lp, LRESULT* out) +{ + if (m == WM_MENUSELECT) { + auto* oldActive = m_active; + m_active = nullptr; - TrackPopupMenuEx(menu, TPM_RETURNCMD, pos.x(), pos.y(), hwnd, nullptr); + HANDLE_WM_MENUSELECT(h, wp, lp, onMenuSelect); + + if (!m_active && oldActive) { + // this was not a top level, forward to active + m_active = oldActive; + } else if (m_active && m_active == oldActive) { + // same top level menu was selected twice, ignore + return true; + } else if (m_active && m_active != oldActive) { + // new top level selected + log::debug("SMC: switching to {}", m_active->name); + } + } + + if (!m_active) { + // no active menu, forward it to the default handler + return false; + } + + return m_active->menu.wndProc(h, m, wp, lp, out); +} + +void ShellMenuCollection::onMenuSelect( + HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags) +{ + for (auto&& m : m_menus) { + if (m.menu.getMenu() == hmenuPopup) { + m_active = &m; + break; + } + } } } // namespace diff --git a/src/envshell.h b/src/envshell.h index 79dce552..52125a5c 100644 --- a/src/envshell.h +++ b/src/envshell.h @@ -11,7 +11,7 @@ namespace env class ShellMenu { public: - ShellMenu() = default; + ShellMenu(QMainWindow* mw); // noncopyable ShellMenu(const ShellMenu&) = delete; @@ -21,35 +21,44 @@ public: void addFile(QFileInfo fi); - void exec(QWidget* parent, const QPoint& pos); + void exec(const QPoint& pos); HMENU getMenu(); + bool wndProc(HWND hwnd, UINT m, WPARAM wp, LPARAM lp, LRESULT* out); + void invoke(const QPoint& p, int cmd); private: + QMainWindow* m_mw; std::vector m_files; COMPtr m_cm; + COMPtr m_cm2; + COMPtr m_cm3; HMenuPtr m_menu; - void createMenu(); + void create(); + + std::vector createIdls(const std::vector& files); + COMPtr createItemArray(std::vector& idls); + + void createContextMenu(IShellItemArray* array); + void createPopupMenu(IContextMenu* cm); COMPtr createShellItem(const std::wstring& path); COMPtr getPersistIDList(IShellItem* item); CoTaskMemPtr getIDList(IPersistIDList* pidlist); - std::vector createIdls(const std::vector& files); - COMPtr createItemArray(std::vector& idls); - COMPtr createContextMenu(IShellItemArray* array); - HMenuPtr createMenu(IContextMenu* cm); HMenuPtr createDummyMenu(const QString& what); - int runMenu(QMainWindow* mw, IContextMenu* cm, HMENU menu, const QPoint& p); - void invoke(QMainWindow* mw, const QPoint& p, int cmd, IContextMenu* cm); + void onMenuSelect( + HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags); }; class ShellMenuCollection { public: + ShellMenuCollection(QMainWindow* mw); + void add(QString name, ShellMenu m); - void exec(QWidget* parent, const QPoint& pos); + void exec(const QPoint& pos); private: struct MenuInfo @@ -58,7 +67,14 @@ private: ShellMenu menu; }; + QMainWindow* m_mw; std::vector m_menus; + MenuInfo* m_active; + + bool wndProc(HWND hwnd, UINT m, WPARAM wp, LPARAM lp, LRESULT* out); + + void onMenuSelect( + HWND hwnd, HMENU hmenu, int item, HMENU hmenuPopup, UINT flags); }; } // namespace diff --git a/src/filetree.cpp b/src/filetree.cpp index 543be82b..4e3db1f7 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -466,8 +466,25 @@ void FileTree::onContextMenu(const QPoint &pos) menu.exec(m_tree->viewport()->mapToGlobal(pos)); } +QMainWindow* getMainWindow(QWidget* w) +{ + QWidget* p = w; + + while (p) { + if (auto* mw=dynamic_cast(p)) { + return mw; + } + + p = p->parentWidget(); + } + + return nullptr; +} + void FileTree::showShellMenu(QPoint pos) { + auto* mw = getMainWindow(m_tree); + // menus by origin std::map menus; @@ -477,7 +494,12 @@ void FileTree::showShellMenu(QPoint pos) continue; } - menus[item->originID()].addFile(item->realPath()); + auto itor = menus.find(item->originID()); + if (itor == menus.end()) { + itor = menus.emplace(item->originID(), mw).first; + } + + itor->second.addFile(item->realPath()); if (item->isConflicted()) { const auto file = m_core.directoryStructure()->searchFile( @@ -499,35 +521,21 @@ void FileTree::showShellMenu(QPoint pos) } for (auto&& alt : alts) { - auto* dir = file->getParent(); - if (!dir) { - log::error( - "file {} from origin {} has no parent", - item->dataRelativeFilePath(), alt.first); - - continue; + auto itor = menus.find(alt.first); + if (itor == menus.end()) { + itor = menus.emplace(alt.first, mw).first; } - const auto* origin = dir->findOriginByID(alt.first); - if (!origin) { + const auto fullPath = file->getFullPath(alt.first); + if (fullPath.empty()) { log::error( - "origin {} for file {} cannot be found", - alt.first, item->dataRelativeFilePath()); - - continue; - } - - const auto originFile = origin->findFile(file->getIndex()); - if (!originFile) { - log::error( - "file {} not found in origin {} ({})", - item->dataRelativeFilePath(), origin->getName(), file->getIndex()); + "file {} not found in origin {}", + item->dataRelativeFilePath(), alt.first); continue; } - menus[alt.first].addFile( - QString::fromStdWString(originFile->getFullPath())); + itor->second.addFile(QString::fromStdWString(fullPath)); } } } @@ -538,9 +546,9 @@ void FileTree::showShellMenu(QPoint pos) } else if (menus.size() == 1) { auto& menu = menus.begin()->second; - menu.exec(m_tree, m_tree->viewport()->mapToGlobal(pos)); + menu.exec(m_tree->viewport()->mapToGlobal(pos)); } else { - env::ShellMenuCollection mc; + env::ShellMenuCollection mc(mw); for (auto&& m : menus) { const auto* origin = m_core.directoryStructure()->findOriginByID(m.first); @@ -552,7 +560,7 @@ void FileTree::showShellMenu(QPoint pos) mc.add(QString::fromStdWString(origin->getName()), std::move(m.second)); } - mc.exec(m_tree, m_tree->viewport()->mapToGlobal(pos)); + mc.exec(m_tree->viewport()->mapToGlobal(pos)); } } diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 460431a4..4181098c 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -354,12 +354,20 @@ bool FileEntry::isFromArchive(std::wstring archiveName) const return false; } -std::wstring FileEntry::getFullPath() const +std::wstring FileEntry::getFullPath(int originID) const { - bool ignore = false; + if (originID == -1) { + bool ignore = false; + originID = getOrigin(ignore); + } // base directory for origin - std::wstring result = m_Parent->getOriginByID(getOrigin(ignore)).getPath(); + const auto* o = m_Parent->findOriginByID(originID); + if (!o) { + return {}; + } + + std::wstring result = o->getPath(); // all intermediate directories recurseParents(result, m_Parent); diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 69eb7574..6102c88f 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -127,7 +127,12 @@ public: } bool isFromArchive(std::wstring archiveName = L"") const; - std::wstring getFullPath() const; + + // if originID is -1, uses the main origin; if this file doesn't exist in the + // given origin, returns an empty string + // + std::wstring getFullPath(int originID=-1) const; + std::wstring getRelativePath() const; DirectoryEntry *getParent() -- 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/shared/directoryentry.cpp') diff --git a/src/datatab.cpp b/src/datatab.cpp index af457b33..6b403f66 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -59,7 +59,7 @@ void DataTab::saveState(Settings& s) const void DataTab::restoreState(const Settings& s) { s.geometry().restoreState(ui.tree->header()); - + // prior to 2.3, the list was not sortable, and this remembered in the // widget state, for whatever reason ui.tree->setSortingEnabled(true); @@ -67,7 +67,7 @@ void DataTab::restoreState(const Settings& s) void DataTab::activated() { - //refreshDataTreeKeepExpandedNodes(); + updateTree(); } void DataTab::onRefresh() @@ -79,76 +79,11 @@ void DataTab::onRefresh() m_core.refreshDirectoryStructure(); } -void DataTab::refreshDataTree() +void DataTab::updateTree() { m_filetree->refresh(); } -void DataTab::refreshDataTreeKeepExpandedNodes() -{ - //m_model->refreshKeepExpandedNodes(); - m_filetree->refresh(); // temp - - /*QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); - QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); - QStringList expandedNodes; - QTreeWidgetItemIterator it1(ui.tree, QTreeWidgetItemIterator::NotHidden | QTreeWidgetItemIterator::HasChildren); - while (*it1) { - QTreeWidgetItem *current = (*it1); - if (current->isExpanded() && !(current->text(0)=="data")) { - expandedNodes.append(current->text(0)+"/"+current->parent()->text(0)); - } - ++it1; - } - - ui.tree->clear(); - QStringList columns("data"); - columns.append(""); - QTreeWidgetItem *subTree = new QTreeWidgetItem(columns); - subTree->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); - updateTo(subTree, L"", *m_core.directoryStructure(), ui.conflicts->isChecked(), &fileIcon, &folderIcon); - ui.tree->insertTopLevelItem(0, subTree); - subTree->setExpanded(true); - QTreeWidgetItemIterator it2(ui.tree, QTreeWidgetItemIterator::HasChildren); - while (*it2) { - QTreeWidgetItem *current = (*it2); - if (!(current->text(0)=="data") && expandedNodes.contains(current->text(0)+"/"+current->parent()->text(0))) { - current->setExpanded(true); - } - ++it2; - }*/ -} - -void DataTab::onItemExpanded(QTreeWidgetItem* item) -{ - /*if ((item->childCount() == 1) && (item->child(0)->data(0, Qt::UserRole).toString() == "__loaded_on_demand__")) { - // read the data we need from the sub-item, then dispose of it - QTreeWidgetItem *onDemandDataItem = item->child(0); - const QString path = onDemandDataItem->data(0, Qt::UserRole + 1).toString(); - std::wstring wspath = path.toStdWString(); - bool conflictsOnly = onDemandDataItem->data(0, Qt::UserRole + 2).toBool(); - - std::wstring virtualPath = (wspath + L"\\").substr(6) + item->text(0).toStdWString(); - DirectoryEntry *dir = m_core.directoryStructure()->findSubDirectoryRecursive(virtualPath); - if (dir != nullptr) { - QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); - QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); - updateTo(item, wspath, *dir, conflictsOnly, &fileIcon, &folderIcon); - } else { - log::warn("failed to update view of {}", path); - } - - m_removeLater.push_back(item); - - QTimer::singleShot(5, [this]{ - for (QTreeWidgetItem *item : m_removeLater) { - item->removeChild(item->child(0)); - } - m_removeLater.clear(); - }); - }*/ -} - void DataTab::onConflicts() { updateOptions(); @@ -172,5 +107,5 @@ void DataTab::updateOptions() } m_filetree->model()->setFlags(flags); - refreshDataTree(); + updateTree(); } diff --git a/src/datatab.h b/src/datatab.h index fd93514c..4545dc5a 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -25,8 +25,7 @@ public: void restoreState(const Settings& s); void activated(); - void refreshDataTreeKeepExpandedNodes(); - void refreshDataTree(); + void updateTree(); signals: void executablesChanged(); diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index a506f0f0..6d42f2dc 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -4,9 +4,38 @@ #include "util.h" #include "modinfodialogfwd.h" #include +#include using namespace MOBase; using namespace MOShared; +namespace fs = std::filesystem; + +const QString& directoryFileType() +{ + static QString name; + + if (name.isEmpty()) { + const DWORD flags = SHGFI_TYPENAME; + SHFILEINFOW sfi = {}; + + // "." for the current directory, which should always exist + const auto r = SHGetFileInfoW(L".", 0, &sfi, sizeof(sfi), flags); + + if (!r) { + const auto e = GetLastError(); + + log::error( + "SHGetFileInfoW failed for folder file type, {}", + formatSystemMessage(e)); + + name = "File folder"; + } else { + name = QString::fromWCharArray(sfi.szTypeName); + } + } + + return name; +} FileTreeItem::FileTreeItem( @@ -16,6 +45,7 @@ FileTreeItem::FileTreeItem( m_parent(parent), m_indexGuess(NoIndexGuess), m_originID(originID), m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), + m_wsRealPath(realPath), m_realPath(QString::fromStdWString(realPath)), m_flags(flags), m_wsFile(file), @@ -94,13 +124,19 @@ public: return naturalCompare(a->m_mod, b->m_mod); case FileTreeModel::FileType: - return naturalCompare(a->meta().type, b->meta().type); + return naturalCompare( + a->fileType().value_or(QString()), + b->fileType().value_or(QString())); case FileTreeModel::FileSize: - return threeWayCompare(a->meta().size, b->meta().size); + return threeWayCompare( + a->fileSize().value_or(0), + b->fileSize().value_or(0)); case FileTreeModel::LastModified: - return threeWayCompare(a->meta().lastModified, b->meta().lastModified); + return threeWayCompare( + a->lastModified().value_or(QDateTime()), + b->lastModified().value_or(QDateTime())); default: return 0; @@ -166,34 +202,88 @@ QFont FileTreeItem::font() const return f; } -const FileTreeItem::Meta& FileTreeItem::meta() const +std::optional FileTreeItem::fileSize() const { - if (!m_meta) { - QFileInfo fi(m_realPath); - - SHFILEINFOW sfi = {}; - const auto r = SHGetFileInfoW( - m_realPath.toStdWString().c_str(), 0, &sfi, sizeof(sfi), - SHGFI_TYPENAME); + if (m_fileSize.empty()) { + std::error_code ec; + const auto size = fs::file_size(fs::path(m_wsRealPath), ec); - if (!r) { - const auto e = GetLastError(); + if (ec) { + log::error("can't get file size for '{}', {}", m_realPath, ec.message()); + m_fileSize.fail(); + } else { + m_fileSize.set(size); + } + } - log::error( - "SHGetFileInfoW failed for '{}', {}", - m_realPath, e); + return m_fileSize.value; +} - sfi = {}; +std::optional FileTreeItem::lastModified() const +{ + if (m_lastModified.empty()) { + if (m_realPath.isEmpty()) { + // this is a virtual directory + m_lastModified.set({}); + } else if (isFromArchive()) { + // can't get last modified date for files in archives + m_lastModified.set({}); + } else { + // looks like a regular file on the filesystem + const QFileInfo fi(m_realPath); + const auto d = fi.lastModified(); + + if (!d.isValid()) { + log::error("can't get last modified date for '{}'", m_realPath); + m_lastModified.fail(); + } else { + m_lastModified.set(d); + } } + } + + return m_lastModified.value; +} - m_meta = { - static_cast(fi.size()), - fi.lastModified(), - QString::fromWCharArray(sfi.szTypeName) - }; +std::optional FileTreeItem::fileType() const +{ + if (m_fileType.empty()) { + getFileType(); } - return *m_meta; + return m_fileType.value; +} + +void FileTreeItem::getFileType() const +{ + if (isDirectory()) { + m_fileType.set(directoryFileType()); + return; + } + + DWORD flags = SHGFI_TYPENAME; + + if (isFromArchive()) { + // files from archives are not on the filesystem; this flag forces + // SHGetFileInfoW() to only work with the filename + flags |= SHGFI_USEFILEATTRIBUTES; + } + + SHFILEINFOW sfi = {}; + const auto r = SHGetFileInfoW( + m_wsRealPath.c_str(), 0, &sfi, sizeof(sfi), flags); + + if (!r) { + const auto e = GetLastError(); + + log::error( + "SHGetFileInfoW failed for '{}', {}", + m_realPath, formatSystemMessage(e)); + + m_fileType.fail(); + } else { + m_fileType.set(QString::fromWCharArray(sfi.szTypeName)); + } } QFileIconProvider::IconType FileTreeItem::icon() const diff --git a/src/filetreeitem.h b/src/filetreeitem.h index fc27b0c0..c5418d43 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -19,13 +19,6 @@ public: Conflicted = 0x04 }; - struct Meta - { - uint64_t size; - QDateTime lastModified; - QString type; - }; - Q_DECLARE_FLAGS(Flags, Flag); @@ -136,7 +129,24 @@ public: QFont font() const; - const Meta& meta() const; + std::optional fileSize() const; + std::optional lastModified() const; + std::optional fileType() const; + + std::optional compressedFileSize() const + { + return m_compressedFileSize; + } + + void setFileSize(uint64_t size) + { + m_fileSize.set(size); + } + + void setCompressedFileSize(uint64_t compressedSize) + { + m_compressedFileSize = compressedSize; + } const QString& realPath() const { @@ -210,6 +220,30 @@ public: QString debugName() const; private: + template + struct Cached + { + std::optional value; + bool failed = false; + + bool empty() const + { + return !failed && !value; + } + + void set(T t) + { + value = std::move(t); + failed = false; + } + + void fail() + { + value = {}; + failed = true; + } + }; + static constexpr std::size_t NoIndexGuess = std::numeric_limits::max(); @@ -218,17 +252,24 @@ private: const int m_originID; const QString m_virtualParentPath; + const std::wstring m_wsRealPath; const QString m_realPath; const Flags m_flags; const std::wstring m_wsFile, m_wsLcFile; const MOShared::DirectoryEntry::FileKey m_key; const QString m_file; const QString m_mod; - mutable std::optional m_meta; + + mutable Cached m_fileSize; + mutable Cached m_lastModified; + mutable Cached m_fileType; + mutable std::optional m_compressedFileSize; bool m_loaded; bool m_expanded; Children m_children; + + void getFileType() const; }; #endif // MODORGANIZER_FILETREEITEM_INCLUDED diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index aa053eeb..cb7e94fa 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -127,7 +127,7 @@ private: FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""), - m_flags(NoFlags), m_isRefreshing(false) + m_flags(NoFlags) { m_root.setExpanded(true); @@ -136,9 +136,6 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : void FileTreeModel::refresh() { - m_isRefreshing = true; - Guard g([&]{ m_isRefreshing = false; }); - TimeThis tt("FileTreeModel::refresh()"); update(m_root, *m_core.directoryStructure(), L""); } @@ -695,6 +692,14 @@ std::unique_ptr FileTreeModel::createFileItem( &parentItem, originID, parentPath, file.getFullPath(), flags, file.getName(), makeModName(file, originID)); + if (file.getFileSize() != FileEntry::NoFileSize) { + item->setFileSize(file.getFileSize()); + } + + if (file.getCompressedFileSize() != FileEntry::NoFileSize) { + item->setCompressedFileSize(file.getCompressedFileSize()); + } + item->setLoaded(true); return item; @@ -716,7 +721,7 @@ QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const case FileType: { - return item->meta().type; + return item->fileType().value_or(QString()); } case FileSize: @@ -724,13 +729,29 @@ QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const if (item->isDirectory()) { return {}; } else { - return localizedByteSize(item->meta().size); + QString fs; + + if (auto n=item->fileSize()) { + fs = localizedByteSize(*n); + } + + if (auto n=item->compressedFileSize()) { + return QString("%1 (%2)").arg(fs).arg(localizedByteSize(*n)); + } else { + return fs; + } } } case LastModified: { - return item->meta().lastModified.toString(Qt::SystemLocaleDate); + if (auto d=item->lastModified()) { + if (d->isValid()) { + return d->toString(Qt::SystemLocaleDate); + } + } + + return {}; } default: diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 38f611f9..0fbcf521 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -81,7 +81,6 @@ private: mutable IconFetcher m_iconFetcher; mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; - bool m_isRefreshing; Sort m_sort; bool showConflicts() const diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c71a877d..bf0dc61f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2364,7 +2364,7 @@ void MainWindow::directory_refreshed() //Some better check for the current tab is needed. if (ui->tabWidget->currentIndex() == 2) { - m_DataTab->refreshDataTreeKeepExpandedNodes(); + m_DataTab->updateTree(); } } diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 4181098c..92b6c2bf 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -167,13 +167,15 @@ private: FileEntry::FileEntry() : m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr), + m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize), m_LastAccessed(time(nullptr)) { } FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) : - m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), - m_Parent(parent), m_LastAccessed(time(nullptr)) + m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent), + m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize), + m_LastAccessed(time(nullptr)) { } @@ -1020,7 +1022,7 @@ void DirectoryEntry::removeFiles(const std::set &indices) removeFilesFromList(indices); } -void DirectoryEntry::insert( +FileEntry::Ptr DirectoryEntry::insert( const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) { @@ -1036,6 +1038,8 @@ void DirectoryEntry::insert( file->addOrigin(origin.getID(), fileTime, archive, order); origin.addFile(file->getIndex()); + + return file; } void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset) @@ -1085,7 +1089,16 @@ void DirectoryEntry::addFiles( // add files for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) { BSA::File::Ptr file = archiveFolder->getFile(fileIdx); - insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order); + + auto f = insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order); + + if (f) { + if (file->getUncompressedFileSize() > 0) { + f->setFileSize(file->getFileSize(), file->getUncompressedFileSize()); + } else { + f->setFileSize(file->getFileSize(), FileEntry::NoFileSize); + } + } } // recurse into subdirectories diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 6102c88f..7e45493d 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -61,6 +61,9 @@ class FileRegister; class FileEntry { public: + static constexpr uint64_t NoFileSize = + std::numeric_limits::max(); + typedef unsigned int Index; typedef boost::shared_ptr Ptr; @@ -150,6 +153,22 @@ public: return m_FileTime; } + void setFileSize(uint64_t size, uint64_t compressedSize) + { + m_FileSize = size; + m_CompressedFileSize = compressedSize; + } + + uint64_t getFileSize() const + { + return m_FileSize; + } + + uint64_t getCompressedFileSize() const + { + return m_CompressedFileSize; + } + private: Index m_Index; std::wstring m_Name; @@ -158,6 +177,7 @@ private: AlternativesVector m_Alternatives; DirectoryEntry *m_Parent; mutable FILETIME m_FileTime; + uint64_t m_FileSize, m_CompressedFileSize; time_t m_LastAccessed; @@ -480,7 +500,7 @@ private: DirectoryEntry(const DirectoryEntry &reference); - void insert( + FileEntry::Ptr insert( const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order); -- cgit v1.3.1 From 593ed64f860dcd5a5d9329233bafc9530f60cc6b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 5 Feb 2020 21:55:52 -0500 Subject: filter on filename column only because loading the meta for every file takes forever some small optimizations when enabling mods --- src/datatab.cpp | 1 + src/shared/directoryentry.cpp | 26 ++++++++++++++++++-------- src/shared/directoryentry.h | 2 +- 3 files changed, 20 insertions(+), 9 deletions(-) (limited to 'src/shared/directoryentry.cpp') diff --git a/src/datatab.cpp b/src/datatab.cpp index 4c42339f..212d6754 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -28,6 +28,7 @@ DataTab::DataTab( m_filter.setEdit(mwui->dataTabFilter); m_filter.setList(mwui->dataTree); m_filter.setUseSourceSort(true); + m_filter.setFilterColumn(FileTreeModel::FileName); if (auto* m=m_filter.proxyModel()) { m->setDynamicSortFilter(false); diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 92b6c2bf..6e44cc91 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -512,9 +512,11 @@ bool FileRegister::indexValid(FileEntry::Index index) const FileEntry::Ptr FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent) { FileEntry::Index index = generateIndex(); - m_Files[index] = FileEntry::Ptr(new FileEntry(index, name, parent)); - return m_Files[index]; + auto r = m_Files.insert_or_assign( + index, FileEntry::Ptr(new FileEntry(index, name, parent))); + + return r.first->second; } FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const @@ -1026,14 +1028,18 @@ FileEntry::Ptr DirectoryEntry::insert( const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) { - auto iter = m_Files.find(ToLowerCopy(fileName)); + 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); - addFileToList(*file); + addFileToList(std::move(fileNameLower), file->getIndex()); + + // fileNameLower has moved from this point } file->addOrigin(origin.getID(), fileTime, archive, order); @@ -1067,8 +1073,10 @@ void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOf if ((wcscmp(findData.cFileName, L".") != 0) && (wcscmp(findData.cFileName, L"..") != 0)) { int offset = _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE, L"\\%ls", findData.cFileName); + // recurse into subdirectories - getSubDirectory(findData.cFileName, true, origin.getID())->addFiles(origin, buffer, bufferOffset + offset); + DirectoryEntry* sd = getSubDirectory(findData.cFileName, true, origin.getID()); + sd->addFiles(origin, buffer, bufferOffset + offset); } } else { insert(findData.cFileName, origin, findData.ftLastWriteTime, L"", -1); @@ -1245,10 +1253,12 @@ void DirectoryEntry::removeFilesFromList(const std::set& indic } } -void DirectoryEntry::addFileToList(const FileEntry& f) +void DirectoryEntry::addFileToList( + std::wstring fileNameLower, FileEntry::Index index) { - m_Files.emplace(ToLowerCopy(f.getName()), f.getIndex()); - m_FilesLookup.emplace(ToLowerCopy(f.getName()), f.getIndex()); + m_FilesLookup.emplace(fileNameLower, index); + m_Files.emplace(std::move(fileNameLower), index); + // fileNameLower has been moved from this point } } // namespace MOShared diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 7e45493d..e26011d7 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -522,7 +522,7 @@ private: void addDirectoryToList(DirectoryEntry* e); void removeDirectoryFromList(SubDirectories::iterator itor); - void addFileToList(const FileEntry& f); + void addFileToList(std::wstring fileNameLower, FileEntry::Index index); void removeFileFromList(FileEntry::Index index); void removeFilesFromList(const std::set& indices); }; -- cgit v1.3.1