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/shared/util.cpp | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
(limited to 'src/shared/util.cpp')
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
--
cgit v1.3.1
From 2d276cad0b46d68e6886645559cd47c0165392fe Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 19 Dec 2019 19:35:36 -0500
Subject: put expensive logging in an optional function
---
src/filetree.cpp | 4 +++
src/filetreemodel.cpp | 73 +++++++++++++++++++++++++++++++++++++--------------
src/shared/util.cpp | 21 +++++++++++++++
src/shared/util.h | 14 ++++++++++
4 files changed, 93 insertions(+), 19 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/filetree.cpp b/src/filetree.cpp
index 5e5debc5..8fc9e908 100644
--- a/src/filetree.cpp
+++ b/src/filetree.cpp
@@ -594,4 +594,8 @@ void FileTree::addCommonMenus(QMenu& menu)
.callback([&]{ refresh(); })
.hint(QObject::tr("Refreshes the list"))
.addTo(menu);
+
+ MenuItem(QObject::tr("E&xpand All"))
+ .callback([&]{ m_tree->expandAll(); })
+ .addTo(menu);
}
diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp
index aa9d52e3..024ec6ee 100644
--- a/src/filetreemodel.cpp
+++ b/src/filetreemodel.cpp
@@ -9,6 +9,13 @@ using namespace MOShared;
QString UnmanagedModName();
+template
+void trace(F&&)
+{
+ //f();
+}
+
+
FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent)
: QAbstractItemModel(parent), m_core(core), m_flags(NoFlags)
{
@@ -43,8 +50,10 @@ bool FileTreeModel::showArchives() const
void FileTreeModel::refresh()
{
if (m_root.hasChildren()) {
+ TimeThis tt("FileTreeModel::update()");
update(m_root, *m_core.directoryStructure(), L"");
} else {
+ TimeThis tt("FileTreeModel::fill()");
beginResetModel();
m_root = {nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""};
m_root.setExpanded(true);
@@ -64,7 +73,7 @@ void FileTreeModel::ensureLoaded(FileTreeItem* item) const
return;
}
- log::debug("{}: loading on demand", item->debugName());
+ trace([&]{ log::debug("{}: loading on demand", item->debugName()); });
const auto path = item->dataRelativeFilePath();
auto* dir = m_core.directoryStructure()->findSubDirectoryRecursive(
@@ -83,6 +92,8 @@ void FileTreeModel::fill(
FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry,
const std::wstring& parentPath)
{
+ trace([&]{ log::debug("filling {}", parentItem.debugName()); });
+
std::wstring path = parentPath;
if (!parentEntry.isTopLevel()) {
@@ -108,7 +119,7 @@ void FileTreeModel::update(
FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry,
const std::wstring& parentPath)
{
- log::debug("updating {}", parentItem.debugName());
+ trace([&]{ log::debug("updating {}", parentItem.debugName()); });
std::wstring path = parentPath;
@@ -229,9 +240,10 @@ void FileTreeModel::updateDirectories(
FileTreeItem& parentItem, const std::wstring& path,
const MOShared::DirectoryEntry& parentEntry, FillFlags flags)
{
- log::debug(
+ trace([&]{ log::debug(
"updating directories in {} from {}",
parentItem.debugName(), (path.empty() ? L"\\" : path));
+ });
int row = 0;
std::vector remove;
@@ -249,25 +261,35 @@ void FileTreeModel::updateDirectories(
seen.insert(name);
if (item->areChildrenVisible()) {
- log::debug("{} still exists and is expanded", item->debugName());
+ trace([&]{ log::debug(
+ "{} still exists and is expanded", item->debugName());
+ });
// node is expanded
update(*item, *d, path);
if (flags & FillFlag::PruneDirectories) {
if (item->children().empty()) {
- log::debug("{} is now empty, will prune", item->debugName());
+ trace([&]{ 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());
+ trace([&]{ log::debug(
+ "{} still exists but is empty; pruning",
+ item->debugName());
+ });
+
remove.push_back(item.get());
} else if (item->isLoaded()) {
- log::debug(
+ trace([&]{ log::debug(
"{} still exists, is loaded, but is not expanded; unloading",
item->debugName());
+ });
// node is not expanded, unload
@@ -295,7 +317,7 @@ void FileTreeModel::updateDirectories(
}
} else {
// directory is gone
- log::debug("{} is gone, removing", item->debugName());
+ trace([&]{ log::debug("{} is gone, removing", item->debugName()); });
remove.push_back(item.get());
}
@@ -303,7 +325,10 @@ void FileTreeModel::updateDirectories(
}
if (!remove.empty()) {
- log::debug("{}: removing disappearing items", parentItem.debugName());
+ trace([&]{ log::debug(
+ "{}: removing disappearing items",
+ parentItem.debugName());
+ });
for (auto* toRemove : remove) {
const auto& cs = parentItem.children();
@@ -336,13 +361,14 @@ void FileTreeModel::updateDirectories(
const auto& dir = **itor;
if (!seen.contains(dir.getName())) {
- log::debug(
+ trace([&]{ 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");
+ trace([&]{ log::debug("has no files and pruning is set, skipping"); });
continue;
}
}
@@ -370,9 +396,10 @@ void FileTreeModel::updateDirectories(
const auto first = static_cast(insertPos);
const auto last = static_cast(insertPos);
- log::debug(
+ trace([&]{ log::debug(
"{}: inserting {} at {}",
parentItem.debugName(), child->debugName(), insertPos);
+ });
beginInsertRows(parentIndex, first, last);
parentItem.insert(std::move(child), insertPos);
@@ -387,9 +414,10 @@ void FileTreeModel::updateFiles(
FileTreeItem& parentItem, const std::wstring& path,
const MOShared::DirectoryEntry& parentEntry, FillFlags)
{
- log::debug(
+ trace([&]{ log::debug(
"updating files in {} from {}",
parentItem.debugName(), (path.empty() ? L"\\" : path));
+ });
std::set seen;
std::vector remove;
@@ -404,20 +432,22 @@ void FileTreeModel::updateFiles(
if (auto f=parentEntry.findFile(name)) {
if (shouldShowFile(*f)) {
// file still exists
- log::debug("{} still exists", item->debugName());
+ trace([&]{ log::debug("{} still exists", item->debugName()); });
seen.insert(name);
continue;
}
}
- log::debug("{} is gone", item->debugName());
+ trace([&]{ log::debug("{} is gone", item->debugName()); });
remove.push_back(item.get());
}
if (!remove.empty()) {
- log::debug("{}: removing disappearing items", parentItem.debugName());
+ trace([&]{ log::debug(
+ "{}: removing disappearing items", parentItem.debugName());
+ });
for (auto* toRemove : remove) {
const auto& cs = parentItem.children();
@@ -450,15 +480,19 @@ void FileTreeModel::updateFiles(
++firstFile;
}
- log::debug("{}: first file index is {}", parentItem.debugName(), firstFile);
+ trace([&]{ 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(
+ trace([&]{ log::debug(
"{}: new file {}",
parentItem.debugName(), QString::fromStdWString(file->getName()));
+ });
bool isArchive = false;
int originID = file->getOrigin(isArchive);
@@ -477,9 +511,10 @@ void FileTreeModel::updateFiles(
&parentItem, originID, path, file->getFullPath(), flags, file->getName(),
makeModName(*file, originID));
- log::debug(
+ trace([&]{ log::debug(
"{}: inserting {} at {}",
parentItem.debugName(), child->debugName(), insertPos);
+ });
QModelIndex parentIndex;
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 302dea7d..4ac95465 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -21,6 +21,7 @@ along with Mod Organizer. If not, see .
#include "windows_error.h"
#include "mainwindow.h"
#include "env.h"
+#include
#include
#include
@@ -320,6 +321,26 @@ void SetThisThreadName(const QString& s)
} // namespace MOShared
+TimeThis::TimeThis(QString what)
+ : m_what(std::move(what)), m_start(Clock::now())
+{
+}
+
+TimeThis::~TimeThis()
+{
+ using namespace std::chrono;
+
+ const auto end = Clock::now();
+ const auto d = duration_cast(end - m_start).count();
+
+ if (m_what.isEmpty()) {
+ MOBase::log::debug("{} ms", d);
+ } else {
+ MOBase::log::debug("{} {} ms", m_what, d);
+ }
+}
+
+
static bool g_exiting = false;
static bool g_canClose = false;
diff --git a/src/shared/util.h b/src/shared/util.h
index b6f898db..788d2444 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -52,6 +52,20 @@ void SetThisThreadName(const QString& s);
} // namespace MOShared
+class TimeThis
+{
+public:
+ TimeThis(QString what={});
+ ~TimeThis();
+
+private:
+ using Clock = std::chrono::high_resolution_clock;
+
+ QString m_what;
+ Clock::time_point m_start;
+};
+
+
enum class Exit
{
None = 0x00,
--
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/util.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 47d1826f55c66a039951d922dabab8b01324bebb Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Sun, 2 Feb 2020 17:07:51 -0500
Subject: hidden unique_ptr into FileTreeItem refactored pruning don't show
shell menu for directories added collapse all in context menu
---
src/filetree.cpp | 37 +++++++++++---
src/filetree.h | 2 +-
src/filetreeitem.cpp | 12 ++++-
src/filetreeitem.h | 15 ++++--
src/filetreemodel.cpp | 137 ++++++++++++++++++++++++++++++--------------------
src/filetreemodel.h | 8 +--
src/shared/util.cpp | 86 +++++++++++++++++++++++++------
src/shared/util.h | 1 +
8 files changed, 214 insertions(+), 84 deletions(-)
(limited to 'src/shared/util.cpp')
diff --git a/src/filetree.cpp b/src/filetree.cpp
index a826ed9a..41b10586 100644
--- a/src/filetree.cpp
+++ b/src/filetree.cpp
@@ -442,8 +442,11 @@ void FileTree::onContextMenu(const QPoint &pos)
const auto m = QApplication::keyboardModifiers();
if (m & Qt::ShiftModifier) {
- showShellMenu(pos);
- return;
+ // if no shell menu was available, continue on and show the regular
+ // context menu
+ if (showShellMenu(pos)) {
+ return;
+ }
}
QMenu menu;
@@ -481,13 +484,14 @@ QMainWindow* getMainWindow(QWidget* w)
return nullptr;
}
-void FileTree::showShellMenu(QPoint pos)
+bool FileTree::showShellMenu(QPoint pos)
{
auto* mw = getMainWindow(m_tree);
// menus by origin
std::map menus;
int totalFiles = 0;
+ bool hasDirectory = false;
for (auto&& index : m_tree->selectionModel()->selectedRows()) {
auto* item = m_model->itemFromIndex(index);
@@ -495,6 +499,16 @@ void FileTree::showShellMenu(QPoint pos)
continue;
}
+ if (item->isDirectory()) {
+ hasDirectory = true;
+
+ log::warn(
+ "directories do not have shell menus; '{}' selected",
+ item->filename());
+
+ continue;
+ }
+
auto itor = menus.find(item->originID());
if (itor == menus.end()) {
itor = menus.emplace(item->originID(), mw).first;
@@ -543,8 +557,13 @@ void FileTree::showShellMenu(QPoint pos)
}
if (menus.empty()) {
- log::warn("no menus to show");
- return;
+ // don't warn if a directory was selected, a warning has already been
+ // logged above
+ if (!hasDirectory) {
+ log::warn("no menus to show");
+ }
+
+ return false;
}
else if (menus.size() == 1) {
auto& menu = menus.begin()->second;
@@ -576,6 +595,8 @@ void FileTree::showShellMenu(QPoint pos)
mc.exec(m_tree->viewport()->mapToGlobal(pos));
}
+
+ return true;
}
void FileTree::addDirectoryMenus(QMenu&, FileTreeItem&)
@@ -720,7 +741,11 @@ void FileTree::addCommonMenus(QMenu& menu)
.hint(QObject::tr("Refreshes the list"))
.addTo(menu);
- MenuItem(QObject::tr("E&xpand All"))
+ MenuItem(QObject::tr("Ex&pand All"))
.callback([&]{ m_tree->expandAll(); })
.addTo(menu);
+
+ MenuItem(QObject::tr("&Collapse All"))
+ .callback([&]{ m_tree->collapseAll(); })
+ .addTo(menu);
}
diff --git a/src/filetree.h b/src/filetree.h
index 39c9d0c6..80704f7b 100644
--- a/src/filetree.h
+++ b/src/filetree.h
@@ -51,7 +51,7 @@ private:
void onExpandedChanged(const QModelIndex& index, bool expanded);
void onContextMenu(const QPoint &pos);
- void showShellMenu(QPoint pos);
+ bool showShellMenu(QPoint pos);
void addDirectoryMenus(QMenu& menu, FileTreeItem& item);
void addFileMenus(QMenu& menu, const MOShared::FileEntry& file, int originID);
diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp
index 6d42f2dc..da4ce701 100644
--- a/src/filetreeitem.cpp
+++ b/src/filetreeitem.cpp
@@ -58,7 +58,17 @@ FileTreeItem::FileTreeItem(
{
}
-void FileTreeItem::insert(std::unique_ptr child, std::size_t at)
+FileTreeItem::Ptr FileTreeItem::create(
+ FileTreeItem* parent, int originID,
+ std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags,
+ std::wstring file, std::wstring mod)
+{
+ return std::unique_ptr(new FileTreeItem(
+ parent, originID, std::move(dataRelativeParentPath), std::move(realPath),
+ flags, std::move(file), std::move(mod)));
+}
+
+void FileTreeItem::insert(FileTreeItem::Ptr child, std::size_t at)
{
if (at > m_children.size()) {
log::error(
diff --git a/src/filetreeitem.h b/src/filetreeitem.h
index c5418d43..8ef42289 100644
--- a/src/filetreeitem.h
+++ b/src/filetreeitem.h
@@ -9,7 +9,8 @@ class FileTreeItem
class Sorter;
public:
- using Children = std::vector>;
+ using Ptr = std::unique_ptr;
+ using Children = std::vector;
enum Flag
{
@@ -22,7 +23,7 @@ public:
Q_DECLARE_FLAGS(Flags, Flag);
- FileTreeItem(
+ static Ptr create(
FileTreeItem* parent, int originID,
std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags,
std::wstring file, std::wstring mod);
@@ -32,13 +33,13 @@ public:
FileTreeItem(FileTreeItem&&) = default;
FileTreeItem& operator=(FileTreeItem&&) = default;
- void add(std::unique_ptr child)
+ void add(Ptr child)
{
child->m_indexGuess = m_children.size();
m_children.push_back(std::move(child));
}
- void insert(std::unique_ptr child, std::size_t at);
+ void insert(Ptr child, std::size_t at);
template
void insert(Itor begin, Itor end, std::size_t at)
@@ -269,6 +270,12 @@ private:
bool m_expanded;
Children m_children;
+
+ FileTreeItem(
+ FileTreeItem* parent, int originID,
+ std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags,
+ std::wstring file, std::wstring mod);
+
void getFileType() const;
};
diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp
index 91e6198f..3890ad2e 100644
--- a/src/filetreemodel.cpp
+++ b/src/filetreemodel.cpp
@@ -65,7 +65,7 @@ public:
const auto parentIndex = m_model->indexFromItem(m_parentItem);
// make sure the number of items is the same as the size of this range
- Q_ASSERT(static_cast(toAdd.size()) == (last - m_first));
+ Q_ASSERT(static_cast(toAdd.size()) == (last - m_first + 1));
trace(log::debug("Range::add() {} to {}", m_first, last));
@@ -124,12 +124,24 @@ private:
};
+FileTreeItem* getItem(const QModelIndex& index)
+{
+ return static_cast(index.internalPointer());
+}
+
+void* makeInternalPointer(FileTreeItem* item)
+{
+ return item;
+}
+
+
FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) :
QAbstractItemModel(parent), m_core(core),
- m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""),
+ m_root(FileTreeItem::create(
+ nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L"")),
m_flags(NoFlags)
{
- m_root.setExpanded(true);
+ m_root->setExpanded(true);
connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); });
}
@@ -137,19 +149,19 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) :
void FileTreeModel::refresh()
{
TimeThis tt("FileTreeModel::refresh()");
- update(m_root, *m_core.directoryStructure(), L"");
+ update(*m_root, *m_core.directoryStructure(), L"");
}
void FileTreeModel::clear()
{
beginResetModel();
- m_root.clear();
+ m_root->clear();
endResetModel();
}
bool FileTreeModel::showArchives() const
{
- return (m_flags & Archives) && m_core.getArchiveParsing();
+ return (m_flags.testFlag(Archives) && m_core.getArchiveParsing());
}
QModelIndex FileTreeModel::index(
@@ -160,7 +172,7 @@ QModelIndex FileTreeModel::index(
return {};
}
- return createIndex(row, col, parentItem);
+ return createIndex(row, col, makeInternalPointer(parentItem));
}
log::error("FileTreeModel::index(): parentIndex has no internal pointer");
@@ -173,7 +185,7 @@ QModelIndex FileTreeModel::parent(const QModelIndex& index) const
return {};
}
- auto* parentItem = static_cast(index.internalPointer());
+ auto* parentItem = getItem(index);
if (!parentItem) {
log::error("FileTreeModel::parent(): no internal pointer");
return {};
@@ -341,7 +353,7 @@ void FileTreeModel::sort(int column, Qt::SortOrder order)
oldItems.push_back({itemFromIndex(index), index.column()});
}
- m_root.sort(column, order);
+ m_root->sort(column, order);
QModelIndexList newList;
newList.reserve(itemCount);
@@ -359,10 +371,10 @@ void FileTreeModel::sort(int column, Qt::SortOrder order)
FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const
{
if (!index.isValid()) {
- return &m_root;
+ return m_root.get();
}
- auto* parentItem = static_cast(index.internalPointer());
+ auto* parentItem = getItem(index);
if (!parentItem) {
log::error("FileTreeModel::itemFromIndex(): no internal pointer");
return nullptr;
@@ -395,7 +407,7 @@ QModelIndex FileTreeModel::indexFromItem(FileTreeItem& item, int col) const
return {};
}
- return createIndex(index, col, parent);
+ return createIndex(index, col, makeInternalPointer(parent));
}
void FileTreeModel::update(
@@ -477,35 +489,22 @@ void FileTreeModel::removeDisappearingDirectories(
if (item->areChildrenVisible()) {
// the item is currently expanded, update it
update(*item, *d, parentPath);
- } else if (item->isLoaded()) {
- // the item is loaded (previously expanded but now collapsed), mark it
- // as unloaded
- item->setLoaded(false);
}
- if ((m_flags & PruneDirectories)) {
- // this directory must be checked to see if it's empty so it can be
- // pruned
- bool prune = false;
-
- if (item->isLoaded() && item->children().empty()) {
- // item is loaded and has no children; prune it
- prune = true;
- } else {
- // item is not loaded, so children have to be checked manually
- if (!hasFilesAnywhere(*d)) {
- // item wouldn't have any children, prune it
- prune = true;
- }
+ if (shouldShowFolder(*d, item.get())) {
+ // folder should be left in the list
+ if (!item->areChildrenVisible() && item->isLoaded()) {
+ // the item is loaded (previously expanded but now collapsed), mark
+ // it as unloaded so it updates when next expanded
+ item->setLoaded(false);
}
+ } else {
+ // item wouldn't have any children, prune it
+ trace(log::debug("dir {} is empty and pruned", item->filename()));
- if (prune) {
- trace(log::debug("dir {} is empty and pruned", item->filename()));
-
- range.includeCurrent();
- currentRemoved = true;
- ++itor;
- }
+ range.includeCurrent();
+ currentRemoved = true;
+ ++itor;
}
if (!currentRemoved) {
@@ -536,7 +535,7 @@ bool FileTreeModel::addNewDirectories(
// keeps track of the contiguous directories that need to be added to
// avoid calling beginAddRows(), etc. for each item
Range range(this, parentItem);
- std::vector> toAdd;
+ std::vector toAdd;
bool added = false;
// for each directory on the filesystem
@@ -553,7 +552,7 @@ bool FileTreeModel::addNewDirectories(
range.add(std::move(toAdd));
toAdd.clear();
} else {
- if ((m_flags & PruneDirectories) && !hasFilesAnywhere(*d)) {
+ if (!shouldShowFolder(*d, nullptr)) {
// this is a new directory, but it doesn't contain anything interesting
trace(log::debug("new dir {}, empty and pruned", QString::fromStdWString(d->getName())));
@@ -656,7 +655,7 @@ bool FileTreeModel::addNewFiles(
{
// keeps track of the contiguous files that need to be added to
// avoid calling beginAddRows(), etc. for each item
- std::vector> toAdd;
+ std::vector toAdd;
Range range(this, parentItem, firstFileRow);
bool added = false;
@@ -705,11 +704,11 @@ bool FileTreeModel::addNewFiles(
return added;
}
-std::unique_ptr FileTreeModel::createDirectoryItem(
+FileTreeItem::Ptr FileTreeModel::createDirectoryItem(
FileTreeItem& parentItem, const std::wstring& parentPath,
const DirectoryEntry& d)
{
- auto item = std::make_unique(
+ auto item = FileTreeItem::create(
&parentItem, 0, parentPath, L"", FileTreeItem::Directory,
d.getName(), L"");
@@ -722,7 +721,7 @@ std::unique_ptr FileTreeModel::createDirectoryItem(
return item;
}
-std::unique_ptr FileTreeModel::createFileItem(
+FileTreeItem::Ptr FileTreeModel::createFileItem(
FileTreeItem& parentItem, const std::wstring& parentPath,
const FileEntry& file)
{
@@ -739,7 +738,7 @@ std::unique_ptr FileTreeModel::createFileItem(
flags |= FileTreeItem::Conflicted;
}
- auto item = std::make_unique(
+ auto item = FileTreeItem::create(
&parentItem, originID, parentPath, file.getFullPath(), flags,
file.getName(), makeModName(file, originID));
@@ -759,22 +758,54 @@ std::unique_ptr FileTreeModel::createFileItem(
bool FileTreeModel::shouldShowFile(const FileEntry& file) const
{
if (showConflictsOnly() && (file.getAlternatives().size() == 0)) {
+ // only conflicts should be shown, but this file is not conflicted
return false;
}
- if (!showArchives()) {
- bool isArchive = false;
- file.getOrigin(isArchive);
- return !isArchive;
+ if (!showArchives() && file.isFromArchive()) {
+ // files from archives shouldn't be shown, but this file is from an archive
+ return false;
}
return true;
}
-bool FileTreeModel::hasFilesAnywhere(const DirectoryEntry& dir) const
+bool FileTreeModel::shouldShowFolder(
+ const DirectoryEntry& dir, const FileTreeItem* item) const
{
+ bool shouldPrune = m_flags.testFlag(PruneDirectories);
+
+ if (m_core.settings().archiveParsing()) {
+ if (!m_flags.testFlag(Archives)) {
+ // archive parsing is enabled but the tree shouldn't show archives; this
+ // is a bit of a special case for folders because they have to be hidden
+ // regardless of the PruneDirectories flag if they only exist in archives
+ //
+ // note that this test is inaccurate: if a loose folder exists but is
+ // empty, and the same folder exists in an archive but is _not_ empty,
+ // then it's considered to exist _only_ in an archive and will be pruned
+ //
+ // if directories are ever made first-class so they can retain their
+ // origins, this test can be made more accurate
+ shouldPrune = true;
+ }
+ }
+
+ if (!shouldPrune) {
+ // always show folders regardless of their content
+ return true;
+ }
+
+ if (item) {
+ if (item->isLoaded() && item->children().empty()) {
+ // item is loaded and has no children; prune it
+ return false;
+ }
+ }
+
bool foundFile = false;
+ // check all files in this directory, return early if a file should be shown
dir.forEachFile([&](auto&& f) {
if (shouldShowFile(f)) {
foundFile = true;
@@ -791,11 +822,9 @@ bool FileTreeModel::hasFilesAnywhere(const DirectoryEntry& dir) const
return true;
}
- std::vector::const_iterator begin, end;
- dir.getSubDirectories(begin, end);
-
- for (auto itor=begin; itor!=end; ++itor) {
- if (hasFilesAnywhere(**itor)) {
+ // recurse into subdirectories
+ for (auto subdir : dir.getSubDirectories()) {
+ if (shouldShowFolder(*subdir, nullptr)) {
return true;
}
}
diff --git a/src/filetreemodel.h b/src/filetreemodel.h
index 23640ac5..03bae602 100644
--- a/src/filetreemodel.h
+++ b/src/filetreemodel.h
@@ -69,7 +69,7 @@ private:
using DirectoryIterator = std::vector::const_iterator;
OrganizerCore& m_core;
- mutable FileTreeItem m_root;
+ mutable FileTreeItem::Ptr m_root;
Flags m_flags;
mutable IconFetcher m_iconFetcher;
mutable std::vector m_iconPending;
@@ -117,11 +117,11 @@ private:
const std::unordered_set& seen);
- std::unique_ptr createDirectoryItem(
+ FileTreeItem::Ptr createDirectoryItem(
FileTreeItem& parentItem, const std::wstring& parentPath,
const MOShared::DirectoryEntry& d);
- std::unique_ptr createFileItem(
+ FileTreeItem::Ptr createFileItem(
FileTreeItem& parentItem, const std::wstring& parentPath,
const MOShared::FileEntry& file);
@@ -134,7 +134,7 @@ private:
void removePendingIcons(const QModelIndex& parent, int first, int last);
bool shouldShowFile(const MOShared::FileEntry& file) const;
- bool hasFilesAnywhere(const MOShared::DirectoryEntry& dir) const;
+ bool shouldShowFolder(const MOShared::DirectoryEntry& dir, const FileTreeItem* item) const;
QString makeTooltip(const FileTreeItem& item) const;
QVariant makeIcon(const FileTreeItem& item, const QModelIndex& index) const;
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
index 009aad70..aa4ad4b3 100644
--- a/src/shared/util.cpp
+++ b/src/shared/util.cpp
@@ -25,6 +25,8 @@ along with Mod Organizer. If not, see .
#include
#include
+using namespace MOBase;
+
namespace MOShared
{
@@ -199,7 +201,7 @@ std::wstring GetFileVersionString(const std::wstring &fileName)
}
}
-MOBase::VersionInfo createVersionInfo()
+VersionInfo createVersionInfo()
{
VS_FIXEDFILEINFO version = GetFileVersion(QApplication::applicationFilePath().toStdWString());
@@ -222,25 +224,25 @@ MOBase::VersionInfo createVersionInfo()
if (noLetters)
{
// Default to pre-alpha when release type is unspecified
- return MOBase::VersionInfo(version.dwFileVersionMS >> 16,
- version.dwFileVersionMS & 0xFFFF,
- version.dwFileVersionLS >> 16,
- version.dwFileVersionLS & 0xFFFF,
- MOBase::VersionInfo::RELEASE_PREALPHA);
+ return VersionInfo(version.dwFileVersionMS >> 16,
+ version.dwFileVersionMS & 0xFFFF,
+ version.dwFileVersionLS >> 16,
+ version.dwFileVersionLS & 0xFFFF,
+ VersionInfo::RELEASE_PREALPHA);
}
else
{
// Trust the string to make sense
- return MOBase::VersionInfo(versionString);
+ return VersionInfo(versionString);
}
}
else
{
// Non-pre-release builds just need their version numbers reading
- return MOBase::VersionInfo(version.dwFileVersionMS >> 16,
- version.dwFileVersionMS & 0xFFFF,
- version.dwFileVersionLS >> 16,
- version.dwFileVersionLS & 0xFFFF);
+ return VersionInfo(version.dwFileVersionMS >> 16,
+ version.dwFileVersionMS & 0xFFFF,
+ version.dwFileVersionLS >> 16,
+ version.dwFileVersionLS & 0xFFFF);
}
}
@@ -314,6 +316,62 @@ void SetThisThreadName(const QString& s)
}
}
+
+char shortcutChar(const QAction* a)
+{
+ const auto text = a->text();
+ char shortcut = 0;
+
+ for (int i=0; i= (text.size() - 1)) {
+ log::error("ampersand at the end");
+ return 0;
+ }
+
+ return text[i + 1].toLatin1();
+ }
+ }
+
+ log::error("action {} has no shortcut", text);
+ return 0;
+}
+
+void checkDuplicateShortcuts(const QMenu& m)
+{
+ const auto actions = m.actions();
+
+ for (int i=0; iisSeparator()) {
+ continue;
+ }
+
+ const char shortcut1 = shortcutChar(action1);
+ if (shortcut1 == 0) {
+ continue;
+ }
+
+ for (int j=i+1; jisSeparator()) {
+ continue;
+ }
+
+ const char shortcut2 = shortcutChar(action2);
+
+ if (shortcut1 == shortcut2) {
+ log::error(
+ "duplicate shortcut {} for {} and {}",
+ shortcut1, action1->text(), action2->text());
+
+ break;
+ }
+ }
+ }
+}
+
} // namespace MOShared
@@ -330,9 +388,9 @@ TimeThis::~TimeThis()
const auto d = duration_cast(end - m_start).count();
if (m_what.isEmpty()) {
- MOBase::log::debug("{} ms", d);
+ log::debug("{} ms", d);
} else {
- MOBase::log::debug("{} {} ms", m_what, d);
+ log::debug("{} {} ms", m_what, d);
}
}
@@ -358,7 +416,7 @@ bool ExitModOrganizer(ExitFlags e)
}
g_exiting = true;
- MOBase::Guard g([&]{ g_exiting = false; });
+ Guard g([&]{ g_exiting = false; });
if (!e.testFlag(Exit::Force)) {
if (auto* mw=findMainWindow()) {
diff --git a/src/shared/util.h b/src/shared/util.h
index 79cadf71..05522c2d 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -48,6 +48,7 @@ MOBase::VersionInfo createVersionInfo();
QString getUsvfsVersionString();
void SetThisThreadName(const QString& s);
+void checkDuplicateShortcuts(const QMenu& m);
} // namespace MOShared
--
cgit v1.3.1