From 7e748d15c9d4fdf42b753f58e033899c5753b899 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 10 Dec 2019 10:48:09 -0500 Subject: split data tab removed a bunch of undefined functions in MainWindow --- src/datatab.h | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/datatab.h (limited to 'src/datatab.h') diff --git a/src/datatab.h b/src/datatab.h new file mode 100644 index 00000000..27d36936 --- /dev/null +++ b/src/datatab.h @@ -0,0 +1,83 @@ +#include "modinfodialogfwd.h" +#include "modinfo.h" +#include +#include +#include + +namespace Ui { class MainWindow; } +class OrganizerCore; +class Settings; +class PluginContainer; + +namespace MOShared { class DirectoryEntry; } + +class DataTab : public QObject +{ + Q_OBJECT; + +public: + DataTab( + OrganizerCore& core, PluginContainer& pc, + QWidget* parent, Ui::MainWindow* ui); + + void saveState(Settings& s) const; + void restoreState(const Settings& s); + void activated(); + + void refreshDataTreeKeepExpandedNodes(); + void refreshDataTree(); + +signals: + void executablesChanged(); + void originModified(int originID); + void displayModInformation(ModInfo::Ptr m, unsigned int i, ModInfoTabIDs tab); + +private: + struct DataTabUi + { + QPushButton* refresh; + QTreeWidget* tree; + QCheckBox* conflicts; + QCheckBox* archives; + }; + + OrganizerCore& m_core; + PluginContainer& m_pluginContainer; + bool m_archives; + QWidget* m_parent; + DataTabUi ui; + std::vector m_removeLater; + + void onRefresh(); + void onItemExpanded(QTreeWidgetItem* item); + void onItemActivated(QTreeWidgetItem* item, int col); + void onContextMenu(const QPoint &pos); + void onConflicts(); + void onArchives(); + + void updateTo( + QTreeWidgetItem *subTree, const std::wstring &directorySoFar, + const MOShared::DirectoryEntry &directoryEntry, bool conflictsOnly, + QIcon *fileIcon, QIcon *folderIcon); + + QTreeWidgetItem* singleSelection(); + + void openSelection(); + void open(QTreeWidgetItem* item); + + void runSelectionHooked(); + void runHooked(QTreeWidgetItem* item); + + void previewSelection(); + void preview(QTreeWidgetItem* item); + + void addAsExecutable(); + void openOriginInExplorer(); + void openModInfo(); + void hideFile(); + void unhideFile(); + void writeDataToFile(); + void writeDataToFile( + QFile &file, const QString &directory, + const MOShared::DirectoryEntry &directoryEntry); +}; -- cgit v1.3.1 From a4624f239fe5d152ec8797c1a3f8c2b2aeaef1ba Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 10 Dec 2019 13:09:44 -0500 Subject: split FileTreeModel, initial implementation, some stuff is broken --- src/CMakeLists.txt | 3 + src/datatab.cpp | 173 ++++------------------- src/datatab.h | 4 +- src/filetree.cpp | 396 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/filetree.h | 110 +++++++++++++++ src/mainwindow.ui | 14 +- 6 files changed, 540 insertions(+), 160 deletions(-) create mode 100644 src/filetree.cpp create mode 100644 src/filetree.h (limited to 'src/datatab.h') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0658da31..ad791e79 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -145,6 +145,7 @@ SET(organizer_SRCS lootdialog.cpp filterlist.cpp datatab.cpp + filetree.cpp shared/windows_error.cpp shared/error_report.cpp @@ -270,6 +271,7 @@ SET(organizer_HDRS json.h filterlist.h datatab.h + filetree.h shared/windows_error.h shared/error_report.h @@ -401,6 +403,7 @@ set(loot set(mainwindow datatab + filetree filterlist mainwindow statusbar diff --git a/src/datatab.cpp b/src/datatab.cpp index fb42a904..bc0ff455 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -4,6 +4,7 @@ #include "organizercore.h" #include "directoryentry.h" #include "messagedialog.h" +#include "filetree.h" #include #include @@ -18,21 +19,24 @@ DataTab::DataTab( OrganizerCore& core, PluginContainer& pc, QWidget* parent, Ui::MainWindow* mwui) : m_core(core), m_pluginContainer(pc), m_archives(false), m_parent(parent), + m_model(new FileTreeModel(core)), ui{ mwui->btnRefreshData, mwui->dataTree, mwui->conflictsCheckBox, mwui->showArchiveDataCheckBox} { + ui.tree->setModel(m_model); + connect( ui.refresh, &QPushButton::clicked, [&]{ onRefresh(); }); - connect( - ui.tree, &QTreeWidget::itemExpanded, - [&](auto* item){ onItemExpanded(item); }); - - connect( - ui.tree, &QTreeWidget::itemActivated, - [&](auto* item, int col){ onItemActivated(item, col); }); + //connect( + // ui.tree, &QTreeWidget::itemExpanded, + // [&](auto* item){ onItemExpanded(item); }); + // + //connect( + // ui.tree, &QTreeWidget::itemActivated, + // [&](auto* item, int col){ onItemActivated(item, col); }); connect( ui.tree, &QTreeWidget::customContextMenuRequested, @@ -64,12 +68,13 @@ void DataTab::activated() QTreeWidgetItem* DataTab::singleSelection() { - const auto sel = ui.tree->selectedItems(); - if (sel.count() != 1) { - return nullptr; - } - - return sel[0]; + //const auto sel = ui.tree->selectedItems(); + //if (sel.count() != 1) { + // return nullptr; + //} + // + //return sel[0]; + return nullptr; } void DataTab::openSelection() @@ -144,21 +149,16 @@ void DataTab::onRefresh() void DataTab::refreshDataTree() { - QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); - QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); - 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); + m_model->refresh(); + ui.tree->expand(m_model->index(0, 0)); } void DataTab::refreshDataTreeKeepExpandedNodes() { - QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); + //m_model->refreshKeepExpandedNodes(); + m_model->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); @@ -185,7 +185,7 @@ void DataTab::refreshDataTreeKeepExpandedNodes() current->setExpanded(true); } ++it2; - } + }*/ } void DataTab::updateTo( @@ -193,127 +193,6 @@ void DataTab::updateTo( const DirectoryEntry &directoryEntry, bool conflictsOnly, QIcon *fileIcon, QIcon *folderIcon) { - bool isDirectory = true; - //QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); - //QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); - - std::wostringstream temp; - temp << directorySoFar << "\\" << directoryEntry.getName(); - { - std::vector::const_iterator current, end; - directoryEntry.getSubDirectories(current, end); - for (; current != end; ++current) { - QString pathName = QString::fromStdWString((*current)->getName()); - QStringList columns(pathName); - columns.append(""); - if (!(*current)->isEmpty()) { - QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - directoryChild->setData(0, Qt::DecorationRole, *folderIcon); - directoryChild->setData(0, Qt::UserRole + 3, isDirectory); - - if (conflictsOnly || !m_archives) { - updateTo(directoryChild, temp.str(), **current, conflictsOnly, fileIcon, folderIcon); - if (directoryChild->childCount() != 0) { - subTree->addChild(directoryChild); - } - else { - delete directoryChild; - } - } - else { - QTreeWidgetItem *onDemandLoad = new QTreeWidgetItem(QStringList()); - onDemandLoad->setData(0, Qt::UserRole + 0, "__loaded_on_demand__"); - onDemandLoad->setData(0, Qt::UserRole + 1, QString::fromStdWString(temp.str())); - onDemandLoad->setData(0, Qt::UserRole + 2, conflictsOnly); - directoryChild->addChild(onDemandLoad); - subTree->addChild(directoryChild); - } - } - else { - QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - directoryChild->setData(0, Qt::DecorationRole, *folderIcon); - directoryChild->setData(0, Qt::UserRole + 3, isDirectory); - subTree->addChild(directoryChild); - } - } - } - - - isDirectory = false; - { - for (const FileEntry::Ptr current : directoryEntry.getFiles()) { - if (conflictsOnly && (current->getAlternatives().size() == 0)) { - continue; - } - - bool isArchive = false; - int originID = current->getOrigin(isArchive); - if (!m_archives && isArchive) { - continue; - } - - QString fileName = QString::fromStdWString(current->getName()); - QStringList columns(fileName); - FilesOrigin origin = m_core.directoryStructure()->getOriginByID(originID); - - QString source; - const unsigned int modIndex = ModInfo::getIndex( - QString::fromStdWString(origin.getName())); - - if (modIndex == UINT_MAX) { - source = UnmanagedModName(); - } else { - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - source = modInfo->name(); - } - - std::pair archive = current->getArchive(); - if (archive.first.length() != 0) { - source.append(" (").append(QString::fromStdWString(archive.first)).append(")"); - } - columns.append(source); - QTreeWidgetItem *fileChild = new QTreeWidgetItem(columns); - if (isArchive) { - QFont font = fileChild->font(0); - font.setItalic(true); - fileChild->setFont(0, font); - fileChild->setFont(1, font); - } else if (fileName.endsWith(ModInfo::s_HiddenExt)) { - QFont font = fileChild->font(0); - font.setStrikeOut(true); - fileChild->setFont(0, font); - fileChild->setFont(1, font); - } - fileChild->setData(0, Qt::UserRole, QString::fromStdWString(current->getFullPath())); - fileChild->setData(0, Qt::DecorationRole, *fileIcon); - fileChild->setData(0, Qt::UserRole + 3, isDirectory); - fileChild->setData(0, Qt::UserRole + 1, isArchive); - fileChild->setData(1, Qt::UserRole, source); - fileChild->setData(1, Qt::UserRole + 1, originID); - - std::vector>> alternatives = current->getAlternatives(); - - if (!alternatives.empty()) { - std::wostringstream altString; - altString << tr("Also in:
").toStdWString(); - for (std::vector>>::iterator altIter = alternatives.begin(); - altIter != alternatives.end(); ++altIter) { - if (altIter != alternatives.begin()) { - altString << " , "; - } - altString << "" << m_core.directoryStructure()->getOriginByID(altIter->first).getName() << ""; - } - fileChild->setToolTip(1, QString("%1").arg(QString::fromStdWString(altString.str()))); - fileChild->setForeground(1, QBrush(Qt::red)); - } else { - fileChild->setToolTip(1, tr("No conflict")); - } - subTree->addChild(fileChild); - } - } - - - //subTree->sortChildren(0, Qt::AscendingOrder); } void DataTab::onItemExpanded(QTreeWidgetItem* item) @@ -369,7 +248,7 @@ void DataTab::onItemActivated(QTreeWidgetItem *item, int column) void DataTab::onContextMenu(const QPoint &pos) { - auto* item = ui.tree->itemAt(pos.x(), pos.y()); + QTreeWidgetItem* item = nullptr;//ui.tree->itemAt(pos.x(), pos.y()); QMenu menu; if ((item != nullptr) && (item->childCount() == 0) diff --git a/src/datatab.h b/src/datatab.h index 27d36936..8754c12d 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -8,6 +8,7 @@ namespace Ui { class MainWindow; } class OrganizerCore; class Settings; class PluginContainer; +class FileTreeModel; namespace MOShared { class DirectoryEntry; } @@ -36,7 +37,7 @@ private: struct DataTabUi { QPushButton* refresh; - QTreeWidget* tree; + QTreeView* tree; QCheckBox* conflicts; QCheckBox* archives; }; @@ -45,6 +46,7 @@ private: PluginContainer& m_pluginContainer; bool m_archives; QWidget* m_parent; + FileTreeModel* m_model; DataTabUi ui; std::vector m_removeLater; diff --git a/src/filetree.cpp b/src/filetree.cpp new file mode 100644 index 00000000..cfc73de0 --- /dev/null +++ b/src/filetree.cpp @@ -0,0 +1,396 @@ +#include "filetree.h" +#include "organizercore.h" + +using namespace MOShared; + +// in mainwindow.cpp +QString UnmanagedModName(); + + +FileTreeItem::FileTreeItem() + : m_flags(NoFlags), m_loaded(false) +{ +} + +FileTreeItem::FileTreeItem( + FileTreeItem* parent, + std::wstring virtualParentPath, std::wstring realPath, Flags flags, + std::wstring file, std::wstring mod) : + m_parent(parent), + m_virtualParentPath(QString::fromStdWString(virtualParentPath)), + m_realPath(QString::fromStdWString(realPath)), + m_flags(flags), + m_file(QString::fromStdWString(file)), + m_mod(QString::fromStdWString(mod)), + m_loaded(false) +{ +} + +void FileTreeItem::add(std::unique_ptr child) +{ + m_children.push_back(std::move(child)); +} + +const std::vector>& FileTreeItem::children() const +{ + return m_children; +} + +FileTreeItem* FileTreeItem::parent() +{ + return m_parent; +} + +const QString& FileTreeItem::filename() const +{ + return m_file; +} + +const QString& FileTreeItem::mod() const +{ + return m_mod; +} + +bool FileTreeItem::isFromArchive() const +{ + return (m_flags & FromArchive); +} + +bool FileTreeItem::isHidden() const +{ + return m_file.endsWith(ModInfo::s_HiddenExt); +} + +bool FileTreeItem::isConflicted() const +{ + return (m_flags & Conflicted); +} + +QFileIconProvider::IconType FileTreeItem::icon() const +{ + if (m_flags & Directory) { + return QFileIconProvider::Folder; + } else { + return QFileIconProvider::File; + } +} + +void FileTreeItem::setLoaded(bool b) +{ + m_loaded = b; +} + + + +FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) + : QAbstractItemModel(parent), m_core(core), m_flags(NoFlags) +{ + QFileIconProvider provider; + m_fileIcon = provider.icon(QFileIconProvider::File); + m_directoryIcon = provider.icon(QFileIconProvider::Folder); +} + +void FileTreeModel::setFlags(Flags f) +{ + m_flags = f; +} + +bool FileTreeModel::showConflicts() const +{ + return (m_flags & Conflicts); +} + +bool FileTreeModel::showArchives() const +{ + return (m_flags & Archives); +} + +void FileTreeModel::refresh() +{ + m_root = {nullptr, L"", L"", FileTreeItem::Directory, L"Data", L""}; + + fill(m_root, *m_core.directoryStructure(), L""); + m_root.setLoaded(true); +} + +void FileTreeModel::fill( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath) +{ + const std::wstring path = parentPath + L"\\" + parentEntry.getName(); + bool isDirectory = true; + + + { + std::vector::const_iterator begin, end; + parentEntry.getSubDirectories(begin, end); + fillDirectories(parentItem, path, begin, end); + } + + { + fillFiles(parentItem, path, parentEntry.getFiles()); + } +} + +void FileTreeModel::fillDirectories( + FileTreeItem& parentItem, const std::wstring& path, + DirectoryIterator begin, DirectoryIterator end) +{ + const bool isDirectory = true; + + for (auto itor=begin; itor!=end; ++itor) { + const auto& dir = **itor; + + auto child = std::make_unique( + &parentItem, path, L"", FileTreeItem::Directory, dir.getName(), L""); + + if (dir.isEmpty()) { + child->setLoaded(true); + } else if (showConflicts() || !showArchives()) { + fill(*child, dir, path); + } + + parentItem.add(std::move(child)); + } +} + +void FileTreeModel::fillFiles( + FileTreeItem& parentItem, const std::wstring& path, + const std::vector& files) +{ + const bool isDirectory = false; + + for (auto&& file : files) { + if (showConflicts() && (file->getAlternatives().size() == 0)) { + continue; + } + + bool isArchive = false; + int originID = file->getOrigin(isArchive); + if (!showArchives() && isArchive) { + continue; + } + + FileTreeItem::Flags flags = FileTreeItem::NoFlags; + + if (isArchive) { + flags |= FileTreeItem::FromArchive; + } + + if (!file->getAlternatives().empty()) { + flags |= FileTreeItem::Conflicted; + } + + parentItem.add(std::make_unique( + &parentItem, path, file->getFullPath(), flags, file->getName(), + makeModName(*file, originID))); + } +} + +std::wstring FileTreeModel::makeModName(const FileEntry& file, int originID) const +{ + const auto origin = m_core.directoryStructure()->getOriginByID(originID); + return origin.getName(); + + //const auto index = ModInfo::getIndex(QString::fromStdWString(origin.getName())); + //if (index == UINT_MAX) { + // return UnmanagedModName(); + //} + // + //std::wstring name = ModInfo::getByIndex(index)->name(); + // + //std::pair archive = file.getArchive(); + //if (archive.first.length() != 0) { + // name += L" (" + archive.first + L")"; + //} + // + //return name; +} + +FileTreeItem* FileTreeModel::itemFromIndex(const QModelIndex& index) const +{ + auto* data = index.internalPointer(); + if (!data) { + return nullptr; + } + + return static_cast(data); +} + +QModelIndex FileTreeModel::index(int row, int col, const QModelIndex& parentIndex) const +{ + FileTreeItem* parent = nullptr; + + if (!parentIndex.isValid()) { + parent = &m_root; + } else { + parent = itemFromIndex(parentIndex); + } + + if (!parent) { + return {}; + } + + if (static_cast(row) >= parent->children().size()) { + return {}; + } + + if (col >= columnCount({})) { + return {}; + } + + auto* item = parent->children()[static_cast(row)].get(); + return createIndex(row, col, item); +} + +QModelIndex FileTreeModel::parent(const QModelIndex& index) const +{ + if (!index.isValid()) { + return {}; + } + + auto* item = itemFromIndex(index); + if (!item) { + return {}; + } + + auto* parent = item->parent(); + if (!parent) { + return {}; + } + + int row = 0; + for (auto&& child : parent->children()) { + if (child.get() == item) { + return createIndex(row, 0, parent); + } + + ++row; + } + + return {}; +} + +int FileTreeModel::rowCount(const QModelIndex& parent) const +{ + if (!parent.isValid()) { + return static_cast(m_root.children().size()); + } else { + if (auto* item=itemFromIndex(parent)) { + return static_cast(item->children().size()); + } + } + + return 0; +} + +int FileTreeModel::columnCount(const QModelIndex&) const +{ + return 2; +} + +QVariant FileTreeModel::data(const QModelIndex& index, int role) const +{ + switch (role) + { + case Qt::DisplayRole: + { + if (auto* item=itemFromIndex(index)) { + if (index.column() == 0) { + return item->filename(); + } else if (index.column() == 1) { + return item->mod(); + } + } + + break; + } + + case Qt::FontRole: + { + if (auto* item=itemFromIndex(index)) { + QFont f; + + if (item->isFromArchive()) { + f.setItalic(true); + } else if (item->isHidden()) { + f.setStrikeOut(true); + } + + return f; + } + + break; + } + + case Qt::ToolTipRole: + { + /* + const auto alternatives = file->getAlternatives(); + + if (!alternatives.empty()) { + std::wostringstream altString; + altString << tr("Also in:
").toStdWString(); + for (std::vector>>::iterator altIter = alternatives.begin(); + altIter != alternatives.end(); ++altIter) { + if (altIter != alternatives.begin()) { + altString << " , "; + } + altString << "" << m_core.directoryStructure()->getOriginByID(altIter->first).getName() << ""; + } + fileChild->setToolTip(1, QString("%1").arg(QString::fromStdWString(altString.str()))); + fileChild->setForeground(1, QBrush(Qt::red)); + } else { + fileChild->setToolTip(1, tr("No conflict")); + } + */ + + break; + } + + case Qt::ForegroundRole: + { + if (index.column() == 1) { + if (auto* item=itemFromIndex(index)) { + if (item->isConflicted()) { + return QBrush(Qt::red); + } + } + } + + break; + } + + case Qt::DecorationRole: + { + if (index.column() == 0) { + if (auto* item=itemFromIndex(index)) { + const auto iconType = item->icon(); + + if (iconType == QFileIconProvider::File) { + return m_fileIcon; + } else if (iconType == QFileIconProvider::Folder) { + return m_directoryIcon; + } + } + } + + break; + } + } + + return {}; +} + +QVariant FileTreeModel::headerData(int i, Qt::Orientation ori, int role) const +{ + if (role == Qt::DisplayRole) { + if (i == 0) { + return tr("File"); + } else if (i == 1) { + return tr("Mod"); + } + } + + return {}; +} diff --git a/src/filetree.h b/src/filetree.h new file mode 100644 index 00000000..a9eac64e --- /dev/null +++ b/src/filetree.h @@ -0,0 +1,110 @@ +#include "directoryentry.h" +#include +#include + +class OrganizerCore; + +class FileTreeItem +{ +public: + enum Flag + { + NoFlags = 0x00, + Directory = 0x01, + FromArchive = 0x02, + Conflicted = 0x04 + }; + + Q_DECLARE_FLAGS(Flags, Flag); + + FileTreeItem(); + FileTreeItem( + FileTreeItem* parent, + std::wstring virtualParentPath, std::wstring realPath, Flags flags, + std::wstring file, std::wstring mod); + + FileTreeItem(const FileTreeItem&) = delete; + FileTreeItem& operator=(const FileTreeItem&) = delete; + FileTreeItem(FileTreeItem&&) = default; + FileTreeItem& operator=(FileTreeItem&&) = default; + + void add(std::unique_ptr child); + const std::vector>& children() const; + + FileTreeItem* parent(); + const QString& filename() const; + const QString& mod() const; + bool isFromArchive() const; + bool isHidden() const; + bool isConflicted() const;; + QFileIconProvider::IconType icon() const; + + void setLoaded(bool b); + +private: + FileTreeItem* m_parent; + QString m_virtualParentPath; + QString m_realPath; + Flags m_flags; + QString m_file; + QString m_mod; + bool m_loaded; + std::vector> m_children; +}; + + +class FileTreeModel : public QAbstractItemModel +{ + Q_OBJECT; + +public: + enum Flag + { + NoFlags = 0x00, + Conflicts = 0x01, + Archives = 0x02 + }; + + Q_DECLARE_FLAGS(Flags, Flag); + + FileTreeModel(OrganizerCore& core, QObject* parent=nullptr); + + void setFlags(Flags f); + void refresh(); + + QModelIndex index(int row, int col, const QModelIndex& parent={}) const override; + QModelIndex parent(const QModelIndex& index) const override; + int rowCount(const QModelIndex& parent={}) const override; + int columnCount(const QModelIndex& parent={}) const override; + QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const override; + QVariant headerData(int i, Qt::Orientation ori, int role=Qt::DisplayRole) const override; + +private: + using DirectoryIterator = std::vector::const_iterator; + OrganizerCore& m_core; + mutable FileTreeItem m_root; + Flags m_flags; + QIcon m_fileIcon, m_directoryIcon; + + bool showConflicts() const; + bool showArchives() const; + + void fill( + FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, + const std::wstring& parentPath); + + void fillDirectories( + FileTreeItem& parentItem, const std::wstring& path, + DirectoryIterator begin, DirectoryIterator end); + + void fillFiles( + FileTreeItem& parentItem, const std::wstring& path, + const std::vector& files); + + std::wstring makeModName(const MOShared::FileEntry& file, int originID) const; + + FileTreeItem* itemFromIndex(const QModelIndex& index) const; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeModel::Flags); +Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeItem::Flags); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index d0daafc0..b27122ef 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1075,7 +1075,7 @@ p, li { white-space: pre-wrap; } - + Qt::CustomContextMenu @@ -1088,19 +1088,9 @@ p, li { white-space: pre-wrap; } true - + 400 - - - File - - - - - Mod - - -- cgit v1.3.1 From c5f98f9756ed9ba5b2241fa11215d99c8bf52461 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 12 Dec 2019 18:57:36 -0500 Subject: now prunes empty directories handles archives and conflicts checkboxes --- src/datatab.cpp | 22 ++++++-- src/datatab.h | 2 +- src/filetree.cpp | 124 +++++++++++++++++++++++++++++++------------- src/filetree.h | 16 ++++-- src/shared/directoryentry.h | 14 +++++ 5 files changed, 134 insertions(+), 44 deletions(-) (limited to 'src/datatab.h') diff --git a/src/datatab.cpp b/src/datatab.cpp index bc0ff455..b351923c 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -18,7 +18,7 @@ QString UnmanagedModName(); DataTab::DataTab( OrganizerCore& core, PluginContainer& pc, QWidget* parent, Ui::MainWindow* mwui) : - m_core(core), m_pluginContainer(pc), m_archives(false), m_parent(parent), + m_core(core), m_pluginContainer(pc), m_parent(parent), m_model(new FileTreeModel(core)), ui{ mwui->btnRefreshData, mwui->dataTree, @@ -150,7 +150,6 @@ void DataTab::onRefresh() void DataTab::refreshDataTree() { m_model->refresh(); - ui.tree->expand(m_model->index(0, 0)); } void DataTab::refreshDataTreeKeepExpandedNodes() @@ -343,12 +342,27 @@ void DataTab::onContextMenu(const QPoint &pos) void DataTab::onConflicts() { - refreshDataTreeKeepExpandedNodes(); + updateOptions(); } void DataTab::onArchives() { - m_archives = (m_core.getArchiveParsing() && ui.archives->isChecked()); + updateOptions(); +} + +void DataTab::updateOptions() +{ + FileTreeModel::Flags flags = FileTreeModel::NoFlags; + + if (ui.conflicts->isChecked()) { + flags |= FileTreeModel::Conflicts; + } + + if (ui.archives->isChecked()) { + flags |= FileTreeModel::Archives; + } + + m_model->setFlags(flags); refreshDataTree(); } diff --git a/src/datatab.h b/src/datatab.h index 8754c12d..de38cc8f 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -44,7 +44,6 @@ private: OrganizerCore& m_core; PluginContainer& m_pluginContainer; - bool m_archives; QWidget* m_parent; FileTreeModel* m_model; DataTabUi ui; @@ -56,6 +55,7 @@ private: void onContextMenu(const QPoint &pos); void onConflicts(); void onArchives(); + void updateOptions(); void updateTo( QTreeWidgetItem *subTree, const std::wstring &directorySoFar, diff --git a/src/filetree.cpp b/src/filetree.cpp index f43d07a2..cf40e01c 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -16,17 +16,16 @@ FileTreeItem::FileTreeItem() FileTreeItem::FileTreeItem( FileTreeItem* parent, int originID, - std::wstring virtualParentPath, std::wstring realPath, Flags flags, + std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, std::wstring file, std::wstring mod) : m_parent(parent), m_originID(originID), - m_virtualParentPath(QString::fromStdWString(virtualParentPath)), + m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), m_realPath(QString::fromStdWString(realPath)), m_flags(flags), m_file(QString::fromStdWString(file)), m_mod(QString::fromStdWString(mod)), m_loaded(false) { - Q_ASSERT(!m_mod.isEmpty()); } void FileTreeItem::add(std::unique_ptr child) @@ -56,27 +55,20 @@ const QString& FileTreeItem::virtualParentPath() const QString FileTreeItem::virtualPath() const { - if (m_virtualParentPath.isEmpty()) { - return m_file; - } else { - return m_virtualParentPath + "\\" + m_file; - } -} + QString s = "Data\\"; -QString FileTreeItem::dataRelativeParentPath() const -{ - if (m_virtualParentPath == "data") { - return ""; + if (!m_virtualParentPath.isEmpty()) { + s += m_virtualParentPath + "\\"; } - static const QString prefix = "data\\"; + s += m_file; - auto path = m_virtualParentPath; - if (path.startsWith(prefix)) { - path = path.mid(prefix.size()); - } + return s; +} - return path; +QString FileTreeItem::dataRelativeParentPath() const +{ + return m_virtualParentPath; } QString FileTreeItem::dataRelativeFilePath() const @@ -396,13 +388,13 @@ bool FileTreeModel::showConflicts() const bool FileTreeModel::showArchives() const { - return (m_flags & Archives); + return (m_flags & Archives) && m_core.getArchiveParsing(); } void FileTreeModel::refresh() { beginResetModel(); - m_root = {nullptr, 0, L"", L"", FileTreeItem::Directory, L"Data", L""}; + m_root = {nullptr, 0, L"", L"", FileTreeItem::Directory, L"Data", L""}; fill(m_root, *m_core.directoryStructure(), L""); endResetModel(); } @@ -437,27 +429,87 @@ void FileTreeModel::fill( FileTreeItem& parentItem, const MOShared::DirectoryEntry& parentEntry, const std::wstring& parentPath) { - const std::wstring path = - parentPath + - (parentPath.empty() ? L"" : L"\\") + - parentEntry.getName(); + std::wstring path = parentPath; + + if (!parentEntry.isTopLevel()) { + if (!path.empty()) { + path += L"\\"; + } + + path += parentEntry.getName(); + } + + const auto flags = FillFlag::PruneDirectories; std::vector::const_iterator begin, end; parentEntry.getSubDirectories(begin, end); - fillDirectories(parentItem, path, begin, end); + fillDirectories(parentItem, path, begin, end, flags); - fillFiles(parentItem, path, parentEntry.getFiles()); + fillFiles(parentItem, path, parentEntry.getFiles(), flags); parentItem.setLoaded(true); } +bool FileTreeModel::shouldShowFile(const FileEntry& file) const +{ + if (showConflicts() && (file.getAlternatives().size() == 0)) { + return false; + } + + bool isArchive = false; + int originID = file.getOrigin(isArchive); + if (!showArchives() && isArchive) { + return false; + } + + return true; +} + +bool FileTreeModel::hasFilesAnywhere(const DirectoryEntry& dir) const +{ + bool foundFile = false; + + dir.forEachFile([&](auto&& f) { + if (shouldShowFile(f)) { + foundFile = true; + + // stop + return false; + } + + // continue + return true; + }); + + if (foundFile) { + return true; + } + + std::vector::const_iterator begin, end; + dir.getSubDirectories(begin, end); + + for (auto itor=begin; itor!=end; ++itor) { + if (hasFilesAnywhere(**itor)) { + return true; + } + } + + return false; +} + void FileTreeModel::fillDirectories( FileTreeItem& parentItem, const std::wstring& path, - DirectoryIterator begin, DirectoryIterator end) + DirectoryIterator begin, DirectoryIterator end, FillFlags flags) { for (auto itor=begin; itor!=end; ++itor) { const auto& dir = **itor; + if (flags & FillFlag::PruneDirectories) { + if (!hasFilesAnywhere(dir)) { + continue; + } + } + auto child = std::make_unique( &parentItem, 0, path, L"", FileTreeItem::Directory, dir.getName(), L""); @@ -471,18 +523,15 @@ void FileTreeModel::fillDirectories( void FileTreeModel::fillFiles( FileTreeItem& parentItem, const std::wstring& path, - const std::vector& files) + const std::vector& files, FillFlags) { for (auto&& file : files) { - if (showConflicts() && (file->getAlternatives().size() == 0)) { + if (!shouldShowFile(*file)) { continue; } bool isArchive = false; int originID = file->getOrigin(isArchive); - if (!showArchives() && isArchive) { - continue; - } FileTreeItem::Flags flags = FileTreeItem::NoFlags; @@ -549,9 +598,12 @@ QModelIndex FileTreeModel::index( ensureLoaded(parent); if (static_cast(row) >= parent->children().size()) { - log::error( - "FileTreeModel::index(): row {} is out of range for {}", - row, parent->debugName()); + // don't warn if the tree hasn't been refreshed yet + if (!m_root.children().empty()) { + log::error( + "FileTreeModel::index(): row {} is out of range for {}", + row, parent->debugName()); + } return {}; } diff --git a/src/filetree.h b/src/filetree.h index 13299bb3..4fbd5a2b 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -20,7 +20,7 @@ public: FileTreeItem(); FileTreeItem( FileTreeItem* parent, int originID, - std::wstring virtualParentPath, std::wstring realPath, Flags flags, + std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, std::wstring file, std::wstring mod); FileTreeItem(const FileTreeItem&) = delete; @@ -98,6 +98,14 @@ public: Qt::ItemFlags flags(const QModelIndex& index) const override; private: + enum class FillFlag + { + None = 0x00, + PruneDirectories = 0x01 + }; + + Q_DECLARE_FLAGS(FillFlags, FillFlag); + class IconFetcher; using DirectoryIterator = std::vector::const_iterator; @@ -117,11 +125,11 @@ private: void fillDirectories( FileTreeItem& parentItem, const std::wstring& path, - DirectoryIterator begin, DirectoryIterator end); + DirectoryIterator begin, DirectoryIterator end, FillFlags flags); void fillFiles( FileTreeItem& parentItem, const std::wstring& path, - const std::vector& files); + const std::vector& files, FillFlags flags); std::wstring makeModName(const MOShared::FileEntry& file, int originID) const; @@ -129,6 +137,8 @@ private: void ensureLoaded(FileTreeItem* item) const; void updatePendingIcons(); + 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; }; diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 785c3ff6..0e6b20f0 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -217,8 +217,10 @@ public: 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(); } const DirectoryEntry *getParent() const { return m_Parent; } @@ -247,6 +249,18 @@ public: begin = m_SubDirectories.begin(); end = m_SubDirectories.end(); } + template + void forEachFile(F&& f) const + { + for (auto&& p : m_Files) { + if (auto file=m_FileRegister->getFile(p.second)) { + if (!f(*file)) { + break; + } + } + } + } + DirectoryEntry *findSubDirectory(const std::wstring &name) const; DirectoryEntry *findSubDirectoryRecursive(const std::wstring &path); -- cgit v1.3.1 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/datatab.cpp | 189 +------------------------- src/datatab.h | 5 +- src/filetree.cpp | 300 ++++++++++++++++++++++++++++++++++++++++++ src/filetree.h | 46 ++++++- src/iconfetcher.cpp | 156 ++++++++++++++++++++++ src/main.cpp | 2 - src/modinfodialog.cpp | 3 +- src/modinfodialogfwd.h | 2 +- src/shared/directoryentry.cpp | 2 +- src/shared/directoryentry.h | 2 +- src/spawn.cpp | 9 ++ src/spawn.h | 2 + 12 files changed, 523 insertions(+), 195 deletions(-) create mode 100644 src/iconfetcher.cpp (limited to 'src/datatab.h') diff --git a/src/datatab.cpp b/src/datatab.cpp index b351923c..c8c7bbfa 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -19,29 +19,20 @@ DataTab::DataTab( OrganizerCore& core, PluginContainer& pc, QWidget* parent, Ui::MainWindow* mwui) : m_core(core), m_pluginContainer(pc), m_parent(parent), - m_model(new FileTreeModel(core)), ui{ mwui->btnRefreshData, mwui->dataTree, mwui->conflictsCheckBox, mwui->showArchiveDataCheckBox} { - ui.tree->setModel(m_model); + m_filetree.reset(new FileTree(core, m_pluginContainer, ui.tree)); connect( ui.refresh, &QPushButton::clicked, [&]{ onRefresh(); }); - //connect( - // ui.tree, &QTreeWidget::itemExpanded, - // [&](auto* item){ onItemExpanded(item); }); - // //connect( // ui.tree, &QTreeWidget::itemActivated, // [&](auto* item, int col){ onItemActivated(item, col); }); - connect( - ui.tree, &QTreeWidget::customContextMenuRequested, - [&](auto pos){ onContextMenu(pos); }); - connect( ui.conflicts, &QCheckBox::toggled, [&]{ onConflicts(); }); @@ -79,28 +70,10 @@ QTreeWidgetItem* DataTab::singleSelection() void DataTab::openSelection() { - if (auto* item=singleSelection()) { - open(item); - } } void DataTab::open(QTreeWidgetItem* item) { - const auto isArchive = item->data(0, Qt::UserRole + 1).toBool(); - const auto isDirectory = item->data(0, Qt::UserRole + 3).toBool(); - - if (isArchive || isDirectory) { - return; - } - - const QString path = item->data(0, Qt::UserRole).toString(); - const QFileInfo targetInfo(path); - - m_core.processRunner() - .setFromFile(m_parent, targetInfo) - .setHooked(false) - .setWaitForCompletion(ProcessRunner::Refresh) - .run(); } void DataTab::runSelectionHooked() @@ -112,21 +85,6 @@ void DataTab::runSelectionHooked() void DataTab::runHooked(QTreeWidgetItem* item) { - const auto isArchive = item->data(0, Qt::UserRole + 1).toBool(); - const auto isDirectory = item->data(0, Qt::UserRole + 3).toBool(); - - if (isArchive || isDirectory) { - return; - } - - const QString path = item->data(0, Qt::UserRole).toString(); - const QFileInfo targetInfo(path); - - m_core.processRunner() - .setFromFile(m_parent, targetInfo) - .setHooked(true) - .setWaitForCompletion(ProcessRunner::Refresh) - .run(); } void DataTab::previewSelection() @@ -138,8 +96,6 @@ void DataTab::previewSelection() void DataTab::preview(QTreeWidgetItem* item) { - QString fileName = QDir::fromNativeSeparators(item->data(0, Qt::UserRole).toString()); - m_core.previewFileWithAlternatives(m_parent, fileName); } void DataTab::onRefresh() @@ -149,13 +105,13 @@ void DataTab::onRefresh() void DataTab::refreshDataTree() { - m_model->refresh(); + m_filetree->refresh(); } void DataTab::refreshDataTreeKeepExpandedNodes() { //m_model->refreshKeepExpandedNodes(); - m_model->refresh(); // temp + m_filetree->refresh(); // temp /*QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); @@ -245,101 +201,6 @@ void DataTab::onItemActivated(QTreeWidgetItem *item, int column) } } -void DataTab::onContextMenu(const QPoint &pos) -{ - QTreeWidgetItem* item = nullptr;//ui.tree->itemAt(pos.x(), pos.y()); - - QMenu menu; - if ((item != nullptr) && (item->childCount() == 0) - && (item->data(0, Qt::UserRole + 3).toBool() != true)) { - QString fileName = item->text(0); - const auto isArchive = item->data(0, Qt::UserRole + 1).toBool(); - const auto isDirectory = item->data(0, Qt::UserRole + 3).toBool(); - - QAction* open = nullptr; - QAction* runHooked = nullptr; - QAction* preview = nullptr; - - if (canRunFile(isArchive, fileName)) { - open = new QAction(tr("&Execute"), ui.tree); - runHooked = new QAction(tr("Execute with &VFS"), ui.tree); - } else if (canOpenFile(isArchive, fileName)) { - open = new QAction(tr("&Open"), ui.tree); - runHooked = new QAction(tr("Open with &VFS"), ui.tree); - } - - if (m_pluginContainer.previewGenerator().previewSupported(QFileInfo(fileName).suffix())) { - preview = new QAction(tr("Preview"), ui.tree); - } - - if (open) { - connect(open, &QAction::triggered, [&]{ openSelection(); }); - } - - if (runHooked) { - connect(runHooked, &QAction::triggered, [&]{ runSelectionHooked(); }); - } - - if (preview) { - connect(preview, &QAction::triggered, [&]{ previewSelection(); }); - } - - if (open && preview) { - if (m_core.settings().interface().doubleClicksOpenPreviews()) { - menu.addAction(preview); - menu.addAction(open); - } else { - menu.addAction(open); - menu.addAction(preview); - } - } else { - if (open) { - menu.addAction(open); - } - - if (preview) { - menu.addAction(preview); - } - } - - if (runHooked) { - menu.addAction(runHooked); - } - - menu.addAction(tr("&Add as Executable"), [&]{ addAsExecutable(); }); - - if (!isArchive && !isDirectory) { - menu.addAction("Open Origin in Explorer", [&]{ openOriginInExplorer(); }); - } - - menu.addAction("Open Mod Info", [&]{ openModInfo(); }); - - menu.addSeparator(); - - // offer to hide/unhide file, but not for files from archives - if (!isArchive) { - if (item->text(0).endsWith(ModInfo::s_HiddenExt)) { - menu.addAction(tr("Un-Hide"), [&]{ unhideFile(); }); - } else { - menu.addAction(tr("Hide"), [&]{ hideFile(); }); - } - } - - if (open || preview || runHooked) { - // bold the first option - auto* top = menu.actions()[0]; - auto f = top->font(); - f.setBold(true); - top->setFont(f); - } - } - - menu.addAction(tr("Write To File..."), [&]{ writeDataToFile(); }); - menu.addAction(tr("Refresh"), [&]{ onRefresh(); }); - - menu.exec(ui.tree->viewport()->mapToGlobal(pos)); -} - void DataTab::onConflicts() { updateOptions(); @@ -362,54 +223,12 @@ void DataTab::updateOptions() flags |= FileTreeModel::Archives; } - m_model->setFlags(flags); + m_filetree->setFlags(flags); refreshDataTree(); } void DataTab::addAsExecutable() { - auto* item = singleSelection(); - if (!item) { - return; - } - - const QFileInfo target(item->data(0, Qt::UserRole).toString()); - const auto fec = spawn::getFileExecutionContext(m_parent, target); - - switch (fec.type) - { - case spawn::FileExecutionTypes::Executable: - { - const QString name = QInputDialog::getText( - m_parent, tr("Enter Name"), - tr("Enter a name for the executable"), - QLineEdit::Normal, - target.completeBaseName()); - - if (!name.isEmpty()) { - //Note: If this already exists, you'll lose custom settings - m_core.executablesList()->setExecutable(Executable() - .title(name) - .binaryInfo(fec.binary) - .arguments(fec.arguments) - .workingDirectory(target.absolutePath())); - - emit executablesChanged(); - } - - break; - } - - case spawn::FileExecutionTypes::Other: // fall-through - default: - { - QMessageBox::information( - m_parent, tr("Not an executable"), - tr("This is not a recognized executable.")); - - break; - } - } } void DataTab::openOriginInExplorer() diff --git a/src/datatab.h b/src/datatab.h index de38cc8f..f92d713e 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -8,7 +8,7 @@ namespace Ui { class MainWindow; } class OrganizerCore; class Settings; class PluginContainer; -class FileTreeModel; +class FileTree; namespace MOShared { class DirectoryEntry; } @@ -45,14 +45,13 @@ private: OrganizerCore& m_core; PluginContainer& m_pluginContainer; QWidget* m_parent; - FileTreeModel* m_model; DataTabUi ui; + std::unique_ptr m_filetree; std::vector m_removeLater; void onRefresh(); void onItemExpanded(QTreeWidgetItem* item); void onItemActivated(QTreeWidgetItem* item, int col); - void onContextMenu(const QPoint &pos); void onConflicts(); void onArchives(); void updateOptions(); diff --git a/src/filetree.cpp b/src/filetree.cpp index f99ae8cd..16542734 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -1,5 +1,6 @@ #include "filetree.h" #include "organizercore.h" +#include "modinfodialogfwd.h" #include using namespace MOShared; @@ -9,6 +10,33 @@ using namespace MOBase; QString UnmanagedModName(); +bool canPreviewFile(const PluginContainer& pc, const FileEntry& file) +{ + return canPreviewFile( + pc, file.isFromArchive(), QString::fromStdWString(file.getName())); +} + +bool canRunFile(const FileEntry& file) +{ + return canRunFile(file.isFromArchive(), QString::fromStdWString(file.getName())); +} + +bool canOpenFile(const FileEntry& file) +{ + return canOpenFile(file.isFromArchive(), QString::fromStdWString(file.getName())); +} + +bool isHidden(const FileEntry& file) +{ + return (QString::fromStdWString(file.getName()).endsWith(ModInfo::s_HiddenExt)); +} + +bool canExploreFile(const FileEntry& file); +bool canHideFile(const FileEntry& file); +bool canUnhideFile(const FileEntry& file); + + + FileTreeItem::FileTreeItem() : m_flags(NoFlags), m_loaded(false) { @@ -674,3 +702,275 @@ Qt::ItemFlags FileTreeModel::flags(const QModelIndex& index) const return f; } + + +FileTree::FileTree(OrganizerCore& core, PluginContainer& pc, QTreeView* tree) + : m_core(core), m_plugins(pc), m_tree(tree), m_model(new FileTreeModel(core)) +{ + m_tree->setModel(m_model); + + QObject::connect( + m_tree, &QTreeWidget::customContextMenuRequested, + [&](auto pos){ onContextMenu(pos); }); +} + +void FileTree::setFlags(FileTreeModel::Flags flags) +{ + m_model->setFlags(flags); +} + +void FileTree::refresh() +{ + m_model->refresh(); +} + +FileTreeItem* FileTree::singleSelection() +{ + const auto sel = m_tree->selectionModel()->selectedRows(); + if (sel.size() == 1) { + return m_model->itemFromIndex(sel[0]); + } + + return nullptr; +} + +void FileTree::open() +{ + if (auto* item=singleSelection()) { + if (item->isFromArchive() || item->isDirectory()) { + return; + } + + const QString path = item->realPath(); + const QFileInfo targetInfo(path); + + m_core.processRunner() + .setFromFile(m_tree->window(), targetInfo) + .setHooked(false) + .setWaitForCompletion(ProcessRunner::Refresh) + .run(); + } +} + +void FileTree::openHooked() +{ + if (auto* item=singleSelection()) { + if (item->isFromArchive() || item->isDirectory()) { + return; + } + + const QString path = item->realPath(); + const QFileInfo targetInfo(path); + + m_core.processRunner() + .setFromFile(m_tree->window(), targetInfo) + .setHooked(true) + .setWaitForCompletion(ProcessRunner::Refresh) + .run(); + } +} + +void FileTree::preview() +{ + if (auto* item=singleSelection()) { + const QString path = item->dataRelativeFilePath(); + m_core.previewFileWithAlternatives(m_tree->window(), path); + } +} + +void FileTree::addAsExecutable() +{ + auto* item = singleSelection(); + if (!item) { + return; + } + + const QString path = item->realPath(); + const QFileInfo target(path); + const auto fec = spawn::getFileExecutionContext(m_tree->window(), target); + + switch (fec.type) + { + case spawn::FileExecutionTypes::Executable: + { + const QString name = QInputDialog::getText( + m_tree->window(), QObject::tr("Enter Name"), + QObject::tr("Enter a name for the executable"), + QLineEdit::Normal, + target.completeBaseName()); + + if (!name.isEmpty()) { + //Note: If this already exists, you'll lose custom settings + m_core.executablesList()->setExecutable(Executable() + .title(name) + .binaryInfo(fec.binary) + .arguments(fec.arguments) + .workingDirectory(target.absolutePath())); + + // todo + //emit executablesChanged(); + } + + break; + } + + case spawn::FileExecutionTypes::Other: // fall-through + default: + { + QMessageBox::information( + m_tree->window(), QObject::tr("Not an executable"), + QObject::tr("This is not a recognized executable.")); + + break; + } + } +} + +void FileTree::exploreOrigin() +{ +} + +void FileTree::openModInfo() +{ +} + +void FileTree::toggleVisibility() +{ +} + +void FileTree::hide() +{ +} + +void FileTree::unhide() +{ +} + +void FileTree::dumpToFile() +{ +} + +void FileTree::onContextMenu(const QPoint &pos) +{ + QMenu menu; + + if (auto* item=singleSelection()) { + if (item->isDirectory()) { + addDirectoryMenus(menu, *item); + } else { + const auto file = m_core.directoryStructure()->searchFile( + item->dataRelativeFilePath().toStdWString(), nullptr); + + if (file) { + addFileMenus(menu, *file, item->originID()); + } + } + } + + addCommonMenus(menu); + + menu.exec(m_tree->viewport()->mapToGlobal(pos)); +} + +void FileTree::addDirectoryMenus(QMenu&, FileTreeItem&) +{ + // noop +} + +void FileTree::addFileMenus(QMenu& menu, const FileEntry& file, int originID) +{ + using namespace spawn; + + addOpenMenus(menu, file); + + menu.addSeparator(); + + const QFileInfo target(QString::fromStdWString(file.getFullPath())); + if (getFileExecutionType(target) == FileExecutionTypes::Executable) { + menu.addAction(QObject::tr("&Add as Executable"), [&]{ addAsExecutable(); }); + } + + if (!file.isFromArchive()) { + menu.addAction(QObject::tr("Open Origin in Explorer"), [&]{ exploreOrigin(); }); + } + + if (originID != 0) { + menu.addAction(QObject::tr("Open Mod Info"), [&]{ openModInfo(); }); + } + + // offer to hide/unhide file, but not for files from archives + if (!file.isFromArchive()) { + if (isHidden(file)) { + menu.addAction(QObject::tr("Un-Hide"), [&]{ hide(); }); + } else { + menu.addAction(QObject::tr("Hide"), [&]{ unhide(); }); + } + } +} + +void FileTree::addOpenMenus(QMenu& menu, const MOShared::FileEntry& file) +{ + QAction* openMenu = nullptr; + QAction* openHookedMenu = nullptr; + + if (canRunFile(file)) { + openMenu = new QAction(QObject::tr("&Execute"), m_tree); + openHookedMenu = new QAction(QObject::tr("Execute with &VFS"), m_tree); + } else if (canOpenFile(file)) { + openMenu = new QAction(QObject::tr("&Open"), m_tree); + openHookedMenu = new QAction(QObject::tr("Open with &VFS"), m_tree); + } + + QAction* previewMenu = nullptr; + if (canPreviewFile(m_plugins, file)) { + previewMenu = new QAction(QObject::tr("Preview"), m_tree); + } + + if (openMenu && previewMenu) { + if (m_core.settings().interface().doubleClicksOpenPreviews()) { + menu.addAction(previewMenu); + menu.addAction(openMenu); + } else { + menu.addAction(openMenu); + menu.addAction(previewMenu); + } + } else { + if (openMenu) { + menu.addAction(openMenu); + } + + if (previewMenu) { + menu.addAction(previewMenu); + } + } + + if (openHookedMenu) { + menu.addAction(openHookedMenu); + } + + + if (openMenu) { + QObject::connect(openMenu, &QAction::triggered, [&]{ open(); }); + } + + if (openHookedMenu) { + QObject::connect(openHookedMenu, &QAction::triggered, [&]{ openHooked(); }); + } + + if (previewMenu) { + QObject::connect(previewMenu, &QAction::triggered, [&]{ preview(); }); + } + + + // bold the first option + auto* top = menu.actions()[0]; + auto f = top->font(); + f.setBold(true); + top->setFont(f); +} + +void FileTree::addCommonMenus(QMenu& menu) +{ + menu.addAction(QObject::tr("Write To File..."), [&]{ dumpToFile(); }); + menu.addAction(QObject::tr("Refresh"), [&]{ refresh(); }); +} diff --git a/src/filetree.h b/src/filetree.h index 108c5843..41592d82 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -1,8 +1,12 @@ +#ifndef MODORGANIZER_FILETREE_INCLUDED +#define MODORGANIZER_FILETREE_INCLUDED + #include "directoryentry.h" #include "iconfetcher.h" #include class OrganizerCore; +class PluginContainer; class FileTreeItem { @@ -96,6 +100,7 @@ public: QVariant data(const QModelIndex& index, int role=Qt::DisplayRole) const override; QVariant headerData(int i, Qt::Orientation ori, int role=Qt::DisplayRole) const override; Qt::ItemFlags flags(const QModelIndex& index) const override; + FileTreeItem* itemFromIndex(const QModelIndex& index) const; private: enum class FillFlag @@ -131,7 +136,6 @@ private: std::wstring makeModName(const MOShared::FileEntry& file, int originID) const; - FileTreeItem* itemFromIndex(const QModelIndex& index) const; void ensureLoaded(FileTreeItem* item) const; void updatePendingIcons(); @@ -143,3 +147,43 @@ private: Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeModel::Flags); Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeItem::Flags); + + +class FileTree +{ +public: + FileTree(OrganizerCore& core, PluginContainer& pc, QTreeView* tree); + + void setFlags(FileTreeModel::Flags flags); + void refresh(); + + void open(); + void openHooked(); + void preview(); + + void addAsExecutable(); + void exploreOrigin(); + void openModInfo(); + + void toggleVisibility(); + void hide(); + void unhide(); + + void dumpToFile(); + +private: + OrganizerCore& m_core; + PluginContainer& m_plugins; + QTreeView* m_tree; + FileTreeModel* m_model; + + FileTreeItem* singleSelection(); + 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); + void addCommonMenus(QMenu& menu); +}; + +#endif // MODORGANIZER_FILETREE_INCLUDED diff --git a/src/iconfetcher.cpp b/src/iconfetcher.cpp new file mode 100644 index 00000000..9f8e348a --- /dev/null +++ b/src/iconfetcher.cpp @@ -0,0 +1,156 @@ +#include "iconfetcher.h" + +void IconFetcher::Waiter::wait() +{ + std::unique_lock lock(m_wakeUpMutex); + m_wakeUp.wait(lock, [&]{ return m_queueAvailable; }); + m_queueAvailable = false; +} + +void IconFetcher::Waiter::wakeUp() +{ + { + std::scoped_lock lock(m_wakeUpMutex); + m_queueAvailable = true; + } + + m_wakeUp.notify_one(); +} + + +IconFetcher::IconFetcher() + : m_iconSize(GetSystemMetrics(SM_CXSMICON)), m_stop(false) +{ + m_quickCache.file = getPixmapIcon(QFileIconProvider::File); + m_quickCache.directory = getPixmapIcon(QFileIconProvider::Folder); + + m_thread = std::thread([&]{ threadFun(); }); +} + +IconFetcher::~IconFetcher() +{ + stop(); + m_thread.join(); +} + +void IconFetcher::stop() +{ + m_stop = true; + m_waiter.wakeUp(); +} + +QVariant IconFetcher::icon(const QString& path) const +{ + if (hasOwnIcon(path)) { + return fileIcon(path); + } else { + const auto dot = path.lastIndexOf("."); + + if (dot == -1) { + // no extension + return m_quickCache.file; + } + + return extensionIcon(path.midRef(dot)); + } +} + +QPixmap IconFetcher::genericFileIcon() const +{ + return m_quickCache.file; +} + +QPixmap IconFetcher::genericDirectoryIcon() const +{ + return m_quickCache.directory; +} + +bool IconFetcher::hasOwnIcon(const QString& path) const +{ + static const QString exe = ".exe"; + static const QString lnk = ".lnk"; + static const QString ico = ".ico"; + + return + path.endsWith(exe, Qt::CaseInsensitive) || + path.endsWith(lnk, Qt::CaseInsensitive) || + path.endsWith(ico, Qt::CaseInsensitive); +} + +void IconFetcher::threadFun() +{ + while (!m_stop) { + m_waiter.wait(); + if (m_stop) { + break; + } + + checkCache(m_extensionCache); + checkCache(m_fileCache); + } +} + +void IconFetcher::checkCache(Cache& cache) +{ + std::set queue; + + { + std::scoped_lock lock(cache.queueMutex); + queue = std::move(cache.queue); + cache.queue.clear(); + } + + if (queue.empty()) { + return; + } + + std::map map; + for (auto&& ext : queue) { + map.emplace(std::move(ext), getPixmapIcon(ext)); + } + + { + std::scoped_lock lock(cache.mapMutex); + for (auto&& p : map) { + cache.map.insert(std::move(p)); + } + } +} + +void IconFetcher::queue(Cache& cache, QString path) const +{ + { + std::scoped_lock lock(cache.queueMutex); + cache.queue.insert(std::move(path)); + } + + m_waiter.wakeUp(); +} + +QVariant IconFetcher::fileIcon(const QString& path) const +{ + { + std::scoped_lock lock(m_fileCache.mapMutex); + auto itor = m_fileCache.map.find(path); + if (itor != m_fileCache.map.end()) { + return itor->second; + } + } + + queue(m_fileCache, path); + return {}; +} + +QVariant IconFetcher::extensionIcon(const QStringRef& ext) const +{ + { + std::scoped_lock lock(m_extensionCache.mapMutex); + auto itor = m_extensionCache.map.find(ext); + if (itor != m_extensionCache.map.end()) { + return itor->second; + } + } + + queue(m_extensionCache, ext.toString()); + return {}; +} diff --git a/src/main.cpp b/src/main.cpp index 29d2d02c..2f4c80a1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -635,8 +635,6 @@ int runApplication(MOApplication &application, SingleInstance &instance, } } - Q_ASSERT(!edition.isEmpty()); - game->setGameVariant(edition); log::info( diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 302175aa..b0a4248e 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -51,7 +51,8 @@ int naturalCompare(const QString& a, const QString& b) } bool canPreviewFile( - PluginContainer& pluginContainer, bool isArchive, const QString& filename) + const PluginContainer& pluginContainer, + bool isArchive, const QString& filename) { if (isArchive) { return false; diff --git a/src/modinfodialogfwd.h b/src/modinfodialogfwd.h index 1086e740..c758573c 100644 --- a/src/modinfodialogfwd.h +++ b/src/modinfodialogfwd.h @@ -23,7 +23,7 @@ enum class ModInfoTabIDs class PluginContainer; -bool canPreviewFile(PluginContainer& pluginContainer, bool isArchive, const QString& filename); +bool canPreviewFile(const PluginContainer& pluginContainer, bool isArchive, const QString& filename); bool canRunFile(bool isArchive, const QString& filename); bool canOpenFile(bool isArchive, const QString& filename); bool canExploreFile(bool isArchive, const QString& filename); 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; diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 0e6b20f0..8766f0c6 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -78,7 +78,7 @@ public: 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""); + bool isFromArchive(std::wstring archiveName = L"") const; std::wstring getFullPath() const; std::wstring getRelativePath() const; DirectoryEntry *getParent() { return m_Parent; } diff --git a/src/spawn.cpp b/src/spawn.cpp index 33bdbc05..df6fa379 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -924,6 +924,15 @@ QFileInfo getCmdPath() return systemDirectory + "cmd.exe"; } +FileExecutionTypes getFileExecutionType(const QFileInfo& target) +{ + if (isExeFile(target) || isBatchFile(target) || isJavaFile(target)) { + return FileExecutionTypes::Executable; + } + + return FileExecutionTypes::Other; +} + FileExecutionContext getFileExecutionContext( QWidget* parent, const QFileInfo& target) { diff --git a/src/spawn.h b/src/spawn.h index a615b5ff..0628781e 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -85,6 +85,8 @@ QString findJavaInstallation(const QString& jarFile); FileExecutionContext getFileExecutionContext( QWidget* parent, const QFileInfo& target); +FileExecutionTypes getFileExecutionType(const QFileInfo& target); + } // namespace -- cgit v1.3.1 From 219bd59a21430f830136927564e18dfc25421927 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 18 Dec 2019 20:08:30 -0500 Subject: removed dead stuff --- src/datatab.cpp | 106 ++------------------------------------------------------ src/datatab.h | 27 --------------- 2 files changed, 2 insertions(+), 131 deletions(-) (limited to 'src/datatab.h') diff --git a/src/datatab.cpp b/src/datatab.cpp index 489f0554..7e60ca0f 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -29,10 +29,6 @@ DataTab::DataTab( ui.refresh, &QPushButton::clicked, [&]{ onRefresh(); }); - //connect( - // ui.tree, &QTreeWidget::itemActivated, - // [&](auto* item, int col){ onItemActivated(item, col); }); - connect( ui.conflicts, &QCheckBox::toggled, [&]{ onConflicts(); }); @@ -69,47 +65,6 @@ void DataTab::activated() refreshDataTreeKeepExpandedNodes(); } -QTreeWidgetItem* DataTab::singleSelection() -{ - //const auto sel = ui.tree->selectedItems(); - //if (sel.count() != 1) { - // return nullptr; - //} - // - //return sel[0]; - return nullptr; -} - -void DataTab::openSelection() -{ -} - -void DataTab::open(QTreeWidgetItem* item) -{ -} - -void DataTab::runSelectionHooked() -{ - if (auto* item=singleSelection()) { - runHooked(item); - } -} - -void DataTab::runHooked(QTreeWidgetItem* item) -{ -} - -void DataTab::previewSelection() -{ - if (auto* item=singleSelection()) { - preview(item); - } -} - -void DataTab::preview(QTreeWidgetItem* item) -{ -} - void DataTab::onRefresh() { m_core.refreshDirectoryStructure(); @@ -155,16 +110,9 @@ void DataTab::refreshDataTreeKeepExpandedNodes() }*/ } -void DataTab::updateTo( - QTreeWidgetItem *subTree, const std::wstring &directorySoFar, - const DirectoryEntry &directoryEntry, bool conflictsOnly, - QIcon *fileIcon, QIcon *folderIcon) -{ -} - void DataTab::onItemExpanded(QTreeWidgetItem* item) { - if ((item->childCount() == 1) && (item->child(0)->data(0, Qt::UserRole).toString() == "__loaded_on_demand__")) { + /*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(); @@ -189,28 +137,7 @@ void DataTab::onItemExpanded(QTreeWidgetItem* item) } m_removeLater.clear(); }); - } -} - -void DataTab::onItemActivated(QTreeWidgetItem *item, int column) -{ - const auto isArchive = item->data(0, Qt::UserRole + 1).toBool(); - const auto isDirectory = item->data(0, Qt::UserRole + 3).toBool(); - - if (isArchive || isDirectory) { - return; - } - - const QString path = item->data(0, Qt::UserRole).toString(); - const QFileInfo targetInfo(path); - - const auto tryPreview = m_core.settings().interface().doubleClicksOpenPreviews(); - - if (tryPreview && m_pluginContainer.previewGenerator().previewSupported(targetInfo.suffix())) { - emit preview(item); - } else { - emit open(item); - } + }*/ } void DataTab::onConflicts() @@ -238,32 +165,3 @@ void DataTab::updateOptions() m_filetree->setFlags(flags); refreshDataTree(); } - -void DataTab::addAsExecutable() -{ -} - -void DataTab::openOriginInExplorer() -{ -} - -void DataTab::openModInfo() -{ -} - -void DataTab::hideFile() -{ -} - -void DataTab::unhideFile() -{ -} - -void DataTab::writeDataToFile( - QFile &file, const QString &directory, const DirectoryEntry &directoryEntry) -{ -} - -void DataTab::writeDataToFile() -{ -} diff --git a/src/datatab.h b/src/datatab.h index f92d713e..fd93514c 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -51,34 +51,7 @@ private: void onRefresh(); void onItemExpanded(QTreeWidgetItem* item); - void onItemActivated(QTreeWidgetItem* item, int col); void onConflicts(); void onArchives(); void updateOptions(); - - void updateTo( - QTreeWidgetItem *subTree, const std::wstring &directorySoFar, - const MOShared::DirectoryEntry &directoryEntry, bool conflictsOnly, - QIcon *fileIcon, QIcon *folderIcon); - - QTreeWidgetItem* singleSelection(); - - void openSelection(); - void open(QTreeWidgetItem* item); - - void runSelectionHooked(); - void runHooked(QTreeWidgetItem* item); - - void previewSelection(); - void preview(QTreeWidgetItem* item); - - void addAsExecutable(); - void openOriginInExplorer(); - void openModInfo(); - void hideFile(); - void unhideFile(); - void writeDataToFile(); - void writeDataToFile( - QFile &file, const QString &directory, - const MOShared::DirectoryEntry &directoryEntry); }; -- 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/datatab.h') diff --git a/src/datatab.cpp b/src/datatab.cpp index af457b33..6b403f66 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -59,7 +59,7 @@ void DataTab::saveState(Settings& s) const void DataTab::restoreState(const Settings& s) { s.geometry().restoreState(ui.tree->header()); - + // prior to 2.3, the list was not sortable, and this remembered in the // widget state, for whatever reason ui.tree->setSortingEnabled(true); @@ -67,7 +67,7 @@ void DataTab::restoreState(const Settings& s) void DataTab::activated() { - //refreshDataTreeKeepExpandedNodes(); + updateTree(); } void DataTab::onRefresh() @@ -79,76 +79,11 @@ void DataTab::onRefresh() m_core.refreshDirectoryStructure(); } -void DataTab::refreshDataTree() +void DataTab::updateTree() { m_filetree->refresh(); } -void DataTab::refreshDataTreeKeepExpandedNodes() -{ - //m_model->refreshKeepExpandedNodes(); - m_filetree->refresh(); // temp - - /*QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); - QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); - QStringList expandedNodes; - QTreeWidgetItemIterator it1(ui.tree, QTreeWidgetItemIterator::NotHidden | QTreeWidgetItemIterator::HasChildren); - while (*it1) { - QTreeWidgetItem *current = (*it1); - if (current->isExpanded() && !(current->text(0)=="data")) { - expandedNodes.append(current->text(0)+"/"+current->parent()->text(0)); - } - ++it1; - } - - ui.tree->clear(); - QStringList columns("data"); - columns.append(""); - QTreeWidgetItem *subTree = new QTreeWidgetItem(columns); - subTree->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); - updateTo(subTree, L"", *m_core.directoryStructure(), ui.conflicts->isChecked(), &fileIcon, &folderIcon); - ui.tree->insertTopLevelItem(0, subTree); - subTree->setExpanded(true); - QTreeWidgetItemIterator it2(ui.tree, QTreeWidgetItemIterator::HasChildren); - while (*it2) { - QTreeWidgetItem *current = (*it2); - if (!(current->text(0)=="data") && expandedNodes.contains(current->text(0)+"/"+current->parent()->text(0))) { - current->setExpanded(true); - } - ++it2; - }*/ -} - -void DataTab::onItemExpanded(QTreeWidgetItem* item) -{ - /*if ((item->childCount() == 1) && (item->child(0)->data(0, Qt::UserRole).toString() == "__loaded_on_demand__")) { - // read the data we need from the sub-item, then dispose of it - QTreeWidgetItem *onDemandDataItem = item->child(0); - const QString path = onDemandDataItem->data(0, Qt::UserRole + 1).toString(); - std::wstring wspath = path.toStdWString(); - bool conflictsOnly = onDemandDataItem->data(0, Qt::UserRole + 2).toBool(); - - std::wstring virtualPath = (wspath + L"\\").substr(6) + item->text(0).toStdWString(); - DirectoryEntry *dir = m_core.directoryStructure()->findSubDirectoryRecursive(virtualPath); - if (dir != nullptr) { - QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); - QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); - updateTo(item, wspath, *dir, conflictsOnly, &fileIcon, &folderIcon); - } else { - log::warn("failed to update view of {}", path); - } - - m_removeLater.push_back(item); - - QTimer::singleShot(5, [this]{ - for (QTreeWidgetItem *item : m_removeLater) { - item->removeChild(item->child(0)); - } - m_removeLater.clear(); - }); - }*/ -} - void DataTab::onConflicts() { updateOptions(); @@ -172,5 +107,5 @@ void DataTab::updateOptions() } m_filetree->model()->setFlags(flags); - refreshDataTree(); + updateTree(); } diff --git a/src/datatab.h b/src/datatab.h index fd93514c..4545dc5a 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -25,8 +25,7 @@ public: void restoreState(const Settings& s); void activated(); - void refreshDataTreeKeepExpandedNodes(); - void refreshDataTree(); + void updateTree(); signals: void executablesChanged(); diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index a506f0f0..6d42f2dc 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -4,9 +4,38 @@ #include "util.h" #include "modinfodialogfwd.h" #include +#include using namespace MOBase; using namespace MOShared; +namespace fs = std::filesystem; + +const QString& directoryFileType() +{ + static QString name; + + if (name.isEmpty()) { + const DWORD flags = SHGFI_TYPENAME; + SHFILEINFOW sfi = {}; + + // "." for the current directory, which should always exist + const auto r = SHGetFileInfoW(L".", 0, &sfi, sizeof(sfi), flags); + + if (!r) { + const auto e = GetLastError(); + + log::error( + "SHGetFileInfoW failed for folder file type, {}", + formatSystemMessage(e)); + + name = "File folder"; + } else { + name = QString::fromWCharArray(sfi.szTypeName); + } + } + + return name; +} FileTreeItem::FileTreeItem( @@ -16,6 +45,7 @@ FileTreeItem::FileTreeItem( m_parent(parent), m_indexGuess(NoIndexGuess), m_originID(originID), m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), + m_wsRealPath(realPath), m_realPath(QString::fromStdWString(realPath)), m_flags(flags), m_wsFile(file), @@ -94,13 +124,19 @@ public: return naturalCompare(a->m_mod, b->m_mod); case FileTreeModel::FileType: - return naturalCompare(a->meta().type, b->meta().type); + return naturalCompare( + a->fileType().value_or(QString()), + b->fileType().value_or(QString())); case FileTreeModel::FileSize: - return threeWayCompare(a->meta().size, b->meta().size); + return threeWayCompare( + a->fileSize().value_or(0), + b->fileSize().value_or(0)); case FileTreeModel::LastModified: - return threeWayCompare(a->meta().lastModified, b->meta().lastModified); + return threeWayCompare( + a->lastModified().value_or(QDateTime()), + b->lastModified().value_or(QDateTime())); default: return 0; @@ -166,34 +202,88 @@ QFont FileTreeItem::font() const return f; } -const FileTreeItem::Meta& FileTreeItem::meta() const +std::optional FileTreeItem::fileSize() const { - if (!m_meta) { - QFileInfo fi(m_realPath); - - SHFILEINFOW sfi = {}; - const auto r = SHGetFileInfoW( - m_realPath.toStdWString().c_str(), 0, &sfi, sizeof(sfi), - SHGFI_TYPENAME); + if (m_fileSize.empty()) { + std::error_code ec; + const auto size = fs::file_size(fs::path(m_wsRealPath), ec); - if (!r) { - const auto e = GetLastError(); + if (ec) { + log::error("can't get file size for '{}', {}", m_realPath, ec.message()); + m_fileSize.fail(); + } else { + m_fileSize.set(size); + } + } - log::error( - "SHGetFileInfoW failed for '{}', {}", - m_realPath, e); + return m_fileSize.value; +} - sfi = {}; +std::optional FileTreeItem::lastModified() const +{ + if (m_lastModified.empty()) { + if (m_realPath.isEmpty()) { + // this is a virtual directory + m_lastModified.set({}); + } else if (isFromArchive()) { + // can't get last modified date for files in archives + m_lastModified.set({}); + } else { + // looks like a regular file on the filesystem + const QFileInfo fi(m_realPath); + const auto d = fi.lastModified(); + + if (!d.isValid()) { + log::error("can't get last modified date for '{}'", m_realPath); + m_lastModified.fail(); + } else { + m_lastModified.set(d); + } } + } + + return m_lastModified.value; +} - m_meta = { - static_cast(fi.size()), - fi.lastModified(), - QString::fromWCharArray(sfi.szTypeName) - }; +std::optional FileTreeItem::fileType() const +{ + if (m_fileType.empty()) { + getFileType(); } - return *m_meta; + return m_fileType.value; +} + +void FileTreeItem::getFileType() const +{ + if (isDirectory()) { + m_fileType.set(directoryFileType()); + return; + } + + DWORD flags = SHGFI_TYPENAME; + + if (isFromArchive()) { + // files from archives are not on the filesystem; this flag forces + // SHGetFileInfoW() to only work with the filename + flags |= SHGFI_USEFILEATTRIBUTES; + } + + SHFILEINFOW sfi = {}; + const auto r = SHGetFileInfoW( + m_wsRealPath.c_str(), 0, &sfi, sizeof(sfi), flags); + + if (!r) { + const auto e = GetLastError(); + + log::error( + "SHGetFileInfoW failed for '{}', {}", + m_realPath, formatSystemMessage(e)); + + m_fileType.fail(); + } else { + m_fileType.set(QString::fromWCharArray(sfi.szTypeName)); + } } QFileIconProvider::IconType FileTreeItem::icon() const diff --git a/src/filetreeitem.h b/src/filetreeitem.h index fc27b0c0..c5418d43 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -19,13 +19,6 @@ public: Conflicted = 0x04 }; - struct Meta - { - uint64_t size; - QDateTime lastModified; - QString type; - }; - Q_DECLARE_FLAGS(Flags, Flag); @@ -136,7 +129,24 @@ public: QFont font() const; - const Meta& meta() const; + std::optional fileSize() const; + std::optional lastModified() const; + std::optional fileType() const; + + std::optional compressedFileSize() const + { + return m_compressedFileSize; + } + + void setFileSize(uint64_t size) + { + m_fileSize.set(size); + } + + void setCompressedFileSize(uint64_t compressedSize) + { + m_compressedFileSize = compressedSize; + } const QString& realPath() const { @@ -210,6 +220,30 @@ public: QString debugName() const; private: + template + struct Cached + { + std::optional value; + bool failed = false; + + bool empty() const + { + return !failed && !value; + } + + void set(T t) + { + value = std::move(t); + failed = false; + } + + void fail() + { + value = {}; + failed = true; + } + }; + static constexpr std::size_t NoIndexGuess = std::numeric_limits::max(); @@ -218,17 +252,24 @@ private: const int m_originID; const QString m_virtualParentPath; + const std::wstring m_wsRealPath; const QString m_realPath; const Flags m_flags; const std::wstring m_wsFile, m_wsLcFile; const MOShared::DirectoryEntry::FileKey m_key; const QString m_file; const QString m_mod; - mutable std::optional m_meta; + + mutable Cached m_fileSize; + mutable Cached m_lastModified; + mutable Cached m_fileType; + mutable std::optional m_compressedFileSize; bool m_loaded; bool m_expanded; Children m_children; + + void getFileType() const; }; #endif // MODORGANIZER_FILETREEITEM_INCLUDED diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index aa053eeb..cb7e94fa 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -127,7 +127,7 @@ private: FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L""), - m_flags(NoFlags), m_isRefreshing(false) + m_flags(NoFlags) { m_root.setExpanded(true); @@ -136,9 +136,6 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : void FileTreeModel::refresh() { - m_isRefreshing = true; - Guard g([&]{ m_isRefreshing = false; }); - TimeThis tt("FileTreeModel::refresh()"); update(m_root, *m_core.directoryStructure(), L""); } @@ -695,6 +692,14 @@ std::unique_ptr FileTreeModel::createFileItem( &parentItem, originID, parentPath, file.getFullPath(), flags, file.getName(), makeModName(file, originID)); + if (file.getFileSize() != FileEntry::NoFileSize) { + item->setFileSize(file.getFileSize()); + } + + if (file.getCompressedFileSize() != FileEntry::NoFileSize) { + item->setCompressedFileSize(file.getCompressedFileSize()); + } + item->setLoaded(true); return item; @@ -716,7 +721,7 @@ QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const case FileType: { - return item->meta().type; + return item->fileType().value_or(QString()); } case FileSize: @@ -724,13 +729,29 @@ QVariant FileTreeModel::displayData(const FileTreeItem* item, int column) const if (item->isDirectory()) { return {}; } else { - return localizedByteSize(item->meta().size); + QString fs; + + if (auto n=item->fileSize()) { + fs = localizedByteSize(*n); + } + + if (auto n=item->compressedFileSize()) { + return QString("%1 (%2)").arg(fs).arg(localizedByteSize(*n)); + } else { + return fs; + } } } case LastModified: { - return item->meta().lastModified.toString(Qt::SystemLocaleDate); + if (auto d=item->lastModified()) { + if (d->isValid()) { + return d->toString(Qt::SystemLocaleDate); + } + } + + return {}; } default: diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 38f611f9..0fbcf521 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -81,7 +81,6 @@ private: mutable IconFetcher m_iconFetcher; mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; - bool m_isRefreshing; Sort m_sort; bool showConflicts() const diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c71a877d..bf0dc61f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2364,7 +2364,7 @@ void MainWindow::directory_refreshed() //Some better check for the current tab is needed. if (ui->tabWidget->currentIndex() == 2) { - m_DataTab->refreshDataTreeKeepExpandedNodes(); + m_DataTab->updateTree(); } } diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 4181098c..92b6c2bf 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -167,13 +167,15 @@ private: FileEntry::FileEntry() : m_Index(UINT_MAX), m_Name(), m_Origin(-1), m_Parent(nullptr), + m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize), m_LastAccessed(time(nullptr)) { } FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) : - m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), - m_Parent(parent), m_LastAccessed(time(nullptr)) + m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent), + m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize), + m_LastAccessed(time(nullptr)) { } @@ -1020,7 +1022,7 @@ void DirectoryEntry::removeFiles(const std::set &indices) removeFilesFromList(indices); } -void DirectoryEntry::insert( +FileEntry::Ptr DirectoryEntry::insert( const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) { @@ -1036,6 +1038,8 @@ void DirectoryEntry::insert( file->addOrigin(origin.getID(), fileTime, archive, order); origin.addFile(file->getIndex()); + + return file; } void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset) @@ -1085,7 +1089,16 @@ void DirectoryEntry::addFiles( // add files for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) { BSA::File::Ptr file = archiveFolder->getFile(fileIdx); - insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order); + + auto f = insert(ToWString(file->getName(), true), origin, fileTime, archiveName, order); + + if (f) { + if (file->getUncompressedFileSize() > 0) { + f->setFileSize(file->getFileSize(), file->getUncompressedFileSize()); + } else { + f->setFileSize(file->getFileSize(), FileEntry::NoFileSize); + } + } } // recurse into subdirectories diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 6102c88f..7e45493d 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -61,6 +61,9 @@ class FileRegister; class FileEntry { public: + static constexpr uint64_t NoFileSize = + std::numeric_limits::max(); + typedef unsigned int Index; typedef boost::shared_ptr Ptr; @@ -150,6 +153,22 @@ public: return m_FileTime; } + void setFileSize(uint64_t size, uint64_t compressedSize) + { + m_FileSize = size; + m_CompressedFileSize = compressedSize; + } + + uint64_t getFileSize() const + { + return m_FileSize; + } + + uint64_t getCompressedFileSize() const + { + return m_CompressedFileSize; + } + private: Index m_Index; std::wstring m_Name; @@ -158,6 +177,7 @@ private: AlternativesVector m_Alternatives; DirectoryEntry *m_Parent; mutable FILETIME m_FileTime; + uint64_t m_FileSize, m_CompressedFileSize; time_t m_LastAccessed; @@ -480,7 +500,7 @@ private: DirectoryEntry(const DirectoryEntry &reference); - void insert( + FileEntry::Ptr insert( const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order); -- cgit v1.3.1 From 34b022a2297b869c7be306e3f6bf8e124adaf1a7 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 5 Feb 2020 16:21:41 -0500 Subject: fixed warning when comparing empty strings filter in data tab fixed some bad iterators when removing rows fixed empty directories not being marked as such when refreshing always sort directories first even when reversing the sort order fixed children not being sorted fixed errors about file sizes for directories remember state of checkboxes in data tab --- src/datatab.cpp | 28 ++++++++++++++++++-- src/datatab.h | 2 ++ src/filetree.cpp | 24 ++++++++++++++--- src/filetree.h | 3 +++ src/filetreeitem.cpp | 20 ++++++++++++--- src/filetreemodel.cpp | 71 ++++++++++++++++++++++++++++++++++++--------------- src/filetreemodel.h | 3 +++ src/mainwindow.cpp | 22 +++++++--------- src/mainwindow.h | 2 -- src/mainwindow.ui | 13 +++++++--- src/modinfodialog.cpp | 12 +++++++++ 11 files changed, 152 insertions(+), 48 deletions(-) (limited to 'src/datatab.h') diff --git a/src/datatab.cpp b/src/datatab.cpp index 3923ba4e..74fa35ce 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -21,10 +21,21 @@ DataTab::DataTab( QWidget* parent, Ui::MainWindow* mwui) : m_core(core), m_pluginContainer(pc), m_parent(parent), ui{ - mwui->btnRefreshData, mwui->dataTree, - mwui->conflictsCheckBox, mwui->showArchiveDataCheckBox} + mwui->dataTabRefresh, mwui->dataTree, + mwui->dataTabShowOnlyConflicts, mwui->dataTabShowFromArchives} { m_filetree.reset(new FileTree(core, m_pluginContainer, ui.tree)); + m_filter.setUseSourceSort(true); + m_filter.setEdit(mwui->dataTabFilter); + m_filter.setList(mwui->dataTree); + + if (auto* m=m_filter.proxyModel()) { + m->setDynamicSortFilter(false); + } + + connect( + &m_filter, &FilterWidget::aboutToChange, + [&]{ m_filetree->ensureFullyLoaded(); }); connect( ui.refresh, &QPushButton::clicked, @@ -54,6 +65,8 @@ DataTab::DataTab( void DataTab::saveState(Settings& s) const { s.geometry().saveState(ui.tree->header()); + s.widgets().saveChecked(ui.conflicts); + s.widgets().saveChecked(ui.archives); } void DataTab::restoreState(const Settings& s) @@ -63,6 +76,9 @@ void DataTab::restoreState(const Settings& s) // prior to 2.3, the list was not sortable, and this remembered in the // widget state, for whatever reason ui.tree->setSortingEnabled(true); + + s.widgets().restoreChecked(ui.conflicts); + s.widgets().restoreChecked(ui.archives); } void DataTab::activated() @@ -82,6 +98,14 @@ void DataTab::onRefresh() void DataTab::updateTree() { m_filetree->refresh(); + + if (!m_filter.empty()) { + m_filetree->ensureFullyLoaded(); + + if (auto* m=m_filter.proxyModel()) { + m->invalidate(); + } + } } void DataTab::onConflicts() diff --git a/src/datatab.h b/src/datatab.h index 4545dc5a..7e8eb2b9 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -1,5 +1,6 @@ #include "modinfodialogfwd.h" #include "modinfo.h" +#include #include #include #include @@ -47,6 +48,7 @@ private: DataTabUi ui; std::unique_ptr m_filetree; std::vector m_removeLater; + MOBase::FilterWidget m_filter; void onRefresh(); void onItemExpanded(QTreeWidgetItem* item); diff --git a/src/filetree.cpp b/src/filetree.cpp index bf3a9a7a..ae1fddfe 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -150,11 +150,16 @@ void FileTree::clear() m_model->clear(); } +void FileTree::ensureFullyLoaded() +{ + m_model->ensureFullyLoaded(); +} + FileTreeItem* FileTree::singleSelection() { const auto sel = m_tree->selectionModel()->selectedRows(); if (sel.size() == 1) { - return m_model->itemFromIndex(sel[0]); + return m_model->itemFromIndex(proxiedIndex(sel[0])); } return nullptr; @@ -357,7 +362,7 @@ void FileTree::dumpToFile() const QString file = QFileDialog::getSaveFileName(m_tree->window()); if (file.isEmpty()) { - log::debug("user cancelled"); + log::debug("user canceled"); return; } @@ -432,7 +437,7 @@ void FileTree::dumpToFile( void FileTree::onExpandedChanged(const QModelIndex& index, bool expanded) { - if (auto* item=m_model->itemFromIndex(index)) { + if (auto* item=m_model->itemFromIndex(proxiedIndex(index))) { item->setExpanded(expanded); } } @@ -494,7 +499,7 @@ bool FileTree::showShellMenu(QPoint pos) bool warnOnEmpty = true; for (auto&& index : m_tree->selectionModel()->selectedRows()) { - auto* item = m_model->itemFromIndex(index); + auto* item = m_model->itemFromIndex(proxiedIndex(index)); if (!item) { continue; } @@ -759,3 +764,14 @@ void FileTree::addCommonMenus(QMenu& menu) .callback([&]{ m_tree->collapseAll(); }) .addTo(menu); } + +QModelIndex FileTree::proxiedIndex(const QModelIndex& index) +{ + auto* model = m_tree->model(); + + if (auto* proxy=dynamic_cast(model)) { + return proxy->mapToSource(index); + } else { + return index; + } +} diff --git a/src/filetree.h b/src/filetree.h index 80704f7b..855a1751 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -21,6 +21,7 @@ public: FileTreeModel* model(); void refresh(); void clear(); + void ensureFullyLoaded(); void open(); void openHooked(); @@ -60,6 +61,8 @@ private: void toggleVisibility(bool b); + QModelIndex proxiedIndex(const QModelIndex& index); + void dumpToFile( QFile& out, const QString& parentPath, const MOShared::DirectoryEntry& entry) const; diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index bb07568a..3332fb7d 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -10,6 +10,8 @@ using namespace MOBase; using namespace MOShared; namespace fs = std::filesystem; +constexpr bool AlwaysSortDirectoriesFirst = true; + const QString& directoryFileType() { static QString name; @@ -179,9 +181,17 @@ void FileTreeItem::sort(int column, Qt::SortOrder order) int r = 0; if (a->isDirectory() && !b->isDirectory()) { - r = -1; + if constexpr (AlwaysSortDirectoriesFirst) { + return true; + } else { + r = -1; + } } else if (!a->isDirectory() && b->isDirectory()) { - r = 1; + if constexpr (AlwaysSortDirectoriesFirst) { + return false; + } else { + r = 1; + } } else { r = FileTreeItem::Sorter::compare(column, a.get(), b.get()); } @@ -192,6 +202,10 @@ void FileTreeItem::sort(int column, Qt::SortOrder order) return (r > 0); } }); + + for (auto& child : m_children) { + child->sort(column, order); + } } QString FileTreeItem::virtualPath() const @@ -232,7 +246,7 @@ QFont FileTreeItem::font() const std::optional FileTreeItem::fileSize() const { - if (m_fileSize.empty()) { + if (m_fileSize.empty() && !m_isDirectory) { std::error_code ec; const auto size = fs::file_size(fs::path(m_wsRealPath), ec); diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index aa87edbf..912e2007 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -87,31 +87,32 @@ public: // FileTreeItem::Children::const_iterator remove() { - if (m_first == -1) { - // nothing to remove - return m_parentItem.children().begin() + m_current + 1; - } + if (m_first >= 0) { + const auto last = m_current - 1; + const auto parentIndex = m_model->indexFromItem(m_parentItem); - const auto last = m_current - 1; - const auto parentIndex = m_model->indexFromItem(m_parentItem); + trace(log::debug("Range::remove() {} to {}", m_first, last)); - trace(log::debug("Range::remove() {} to {}", m_first, last)); + m_model->beginRemoveRows(parentIndex, m_first, last); - m_model->beginRemoveRows(parentIndex, m_first, last); + m_parentItem.remove( + static_cast(m_first), + static_cast(last - m_first + 1)); - m_parentItem.remove( - static_cast(m_first), - static_cast(last - m_first + 1)); + m_model->endRemoveRows(); - m_model->endRemoveRows(); + m_model->removePendingIcons(parentIndex, m_first, last); - m_model->removePendingIcons(parentIndex, m_first, last); + // adjust current row to account for those that were just removed + m_current -= (m_current - m_first); - // adjust current row to account for those that were just removed - m_current -= (m_current - m_first); + // reset + m_first = -1; + } - // reset - m_first = -1; + if (m_current >= m_parentItem.children().size()) { + return m_parentItem.children().end(); + } return m_parentItem.children().begin() + m_current + 1; } @@ -138,7 +139,7 @@ void* makeInternalPointer(FileTreeItem* item) FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : QAbstractItemModel(parent), m_core(core), m_root(FileTreeItem::createDirectory(nullptr, L"", L"")), - m_flags(NoFlags) + m_flags(NoFlags), m_fullyLoaded(false) { m_root->setExpanded(true); @@ -148,16 +149,40 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : void FileTreeModel::refresh() { TimeThis tt("FileTreeModel::refresh()"); + + m_fullyLoaded = false; update(*m_root, *m_core.directoryStructure(), L""); } void FileTreeModel::clear() { + m_fullyLoaded = false; + beginResetModel(); m_root->clear(); endResetModel(); } +void FileTreeModel::recursiveFetchMore(const QModelIndex& m) +{ + if (canFetchMore(m)) { + fetchMore(m); + } + + for (int i=0; iareChildrenVisible() && item->isLoaded()) { - // the item is loaded (previously expanded but now collapsed), mark - // it as unloaded so it updates when next expanded - item->setLoaded(false); + if (!d->isEmpty()) { + // the item is loaded (previously expanded but now collapsed) and + // has children, mark it as unloaded so it updates when next + // expanded + item->setLoaded(false); + } } } else { // item wouldn't have any children, prune it @@ -694,6 +722,7 @@ bool FileTreeModel::addNewFiles( } else { // this is a new file, but it shouldn't be shown trace(log::debug("new file {}, not shown", QString::fromStdWString(file->getName()))); + return true; } } diff --git a/src/filetreemodel.h b/src/filetreemodel.h index ffd46fac..3e846a0f 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -43,6 +43,7 @@ public: void refresh(); void clear(); + void ensureFullyLoaded(); QModelIndex index(int row, int col, const QModelIndex& parent={}) const override; QModelIndex parent(const QModelIndex& index) const override; @@ -75,6 +76,7 @@ private: mutable std::vector m_iconPending; mutable QTimer m_iconPendingTimer; Sort m_sort; + bool m_fullyLoaded; bool showConflictsOnly() const { @@ -141,6 +143,7 @@ private: QVariant makeIcon(const FileTreeItem& item, const QModelIndex& index) const; QModelIndex indexFromItem(FileTreeItem& item, int col=0) const; + void recursiveFetchMore(const QModelIndex& m); }; Q_DECLARE_OPERATORS_FOR_FLAGS(FileTreeModel::Flags); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index bf0dc61f..439775f8 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -455,15 +455,13 @@ MainWindow::MainWindow(Settings &settings if (m_OrganizerCore.getArchiveParsing()) { - ui->showArchiveDataCheckBox->setCheckState(Qt::Checked); - ui->showArchiveDataCheckBox->setEnabled(true); - m_showArchiveData = true; + ui->dataTabShowFromArchives->setCheckState(Qt::Checked); + ui->dataTabShowFromArchives->setEnabled(true); } else { - ui->showArchiveDataCheckBox->setCheckState(Qt::Unchecked); - ui->showArchiveDataCheckBox->setEnabled(false); - m_showArchiveData = false; + ui->dataTabShowFromArchives->setCheckState(Qt::Unchecked); + ui->dataTabShowFromArchives->setEnabled(false); } QApplication::instance()->installEventFilter(this); @@ -1193,7 +1191,7 @@ void MainWindow::downloadFilterChanged(const QString &filter) void MainWindow::expandModList(const QModelIndex &index) { QAbstractItemModel *model = ui->modList->model(); -#pragma message("why is this so complicated? mapping the index doesn't work, probably a bug in QtGroupingProxy?") + for (int i = 0; i < model->rowCount(); ++i) { QModelIndex targetIdx = model->index(i, 0); if (model->data(targetIdx).toString() == index.data().toString()) { @@ -4949,15 +4947,13 @@ void MainWindow::on_actionSettings_triggered() m_OrganizerCore.setArchiveParsing(state); if (!state) { - ui->showArchiveDataCheckBox->setCheckState(Qt::Unchecked); - ui->showArchiveDataCheckBox->setEnabled(false); - m_showArchiveData = false; + ui->dataTabShowFromArchives->setCheckState(Qt::Unchecked); + ui->dataTabShowFromArchives->setEnabled(false); } else { - ui->showArchiveDataCheckBox->setCheckState(Qt::Checked); - ui->showArchiveDataCheckBox->setEnabled(true); - m_showArchiveData = true; + ui->dataTabShowFromArchives->setCheckState(Qt::Checked); + ui->dataTabShowFromArchives->setEnabled(true); } m_OrganizerCore.refreshModList(); m_OrganizerCore.refreshDirectoryStructure(); diff --git a/src/mainwindow.h b/src/mainwindow.h index d50705e3..608bfa64 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -349,8 +349,6 @@ private: bool m_DidUpdateMasterList; - bool m_showArchiveData{ true }; - MOBase::DelayedFileWriter m_ArchiveListWriter; QAction* m_LinkToolbar; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index f0887f56..8bd22ff1 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1058,7 +1058,7 @@ p, li { white-space: pre-wrap; } - + refresh data-directory overview @@ -1088,7 +1088,7 @@ p, li { white-space: pre-wrap; } - + Filters the above list so that only conflicts are displayed. @@ -1101,7 +1101,7 @@ p, li { white-space: pre-wrap; } - + Filters the above list so that files from archives are not shown @@ -1147,6 +1147,13 @@ p, li { white-space: pre-wrap; } + + + + + + + diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index b0a4248e..fb093529 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -47,6 +47,18 @@ int naturalCompare(const QString& a, const QString& b) return c; }(); + + // todo: remove this once the fix is released + // see https://bugreports.qt.io/projects/QTBUG/issues/QTBUG-81673 + // and https://codereview.qt-project.org/c/qt/qtbase/+/287966/5/src/corelib/text/qcollator_win.cpp + if (!a.size()) { + return b.size() ? -1 : 0; + } + if (!b.size()) { + return +1; + } + + return c.compare(a, b); } -- cgit v1.3.1 From 1c8c1bb89b6d2103e146a54ae9f0dfdd50f7b835 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 7 Feb 2020 11:40:26 -0500 Subject: fixed data tab not refreshing after "restarting" MO larger default size for filename column fixed sorting in descending order by default --- src/datatab.cpp | 8 ++++++-- src/datatab.h | 1 + src/filetree.cpp | 2 ++ src/mainwindow.cpp | 7 +------ 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src/datatab.h') diff --git a/src/datatab.cpp b/src/datatab.cpp index db9151f8..d8ce9c3a 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -22,7 +22,8 @@ DataTab::DataTab( m_core(core), m_pluginContainer(pc), m_parent(parent), ui{ mwui->dataTabRefresh, mwui->dataTree, - mwui->dataTabShowOnlyConflicts, mwui->dataTabShowFromArchives} + mwui->dataTabShowOnlyConflicts, mwui->dataTabShowFromArchives}, + m_firstActivation(true) { m_filetree.reset(new FileTree(core, m_pluginContainer, ui.tree)); m_filter.setEdit(mwui->dataTabFilter); @@ -84,7 +85,10 @@ void DataTab::restoreState(const Settings& s) void DataTab::activated() { - updateTree(); + if (m_firstActivation) { + m_firstActivation = false; + updateTree(); + } } void DataTab::onRefresh() diff --git a/src/datatab.h b/src/datatab.h index 7e8eb2b9..1e006898 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -49,6 +49,7 @@ private: std::unique_ptr m_filetree; std::vector m_removeLater; MOBase::FilterWidget m_filter; + bool m_firstActivation; void onRefresh(); void onItemExpanded(QTreeWidgetItem* item); diff --git a/src/filetree.cpp b/src/filetree.cpp index af6ef70b..937ffe4c 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -120,7 +120,9 @@ private: FileTree::FileTree(OrganizerCore& core, PluginContainer& pc, QTreeView* tree) : m_core(core), m_plugins(pc), m_tree(tree), m_model(new FileTreeModel(core)) { + m_tree->sortByColumn(0, Qt::AscendingOrder); m_tree->setModel(m_model); + m_tree->header()->resizeSection(0, 200); connect( m_tree, &QTreeView::customContextMenuRequested, diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f89176eb..b34d7e8a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2169,12 +2169,7 @@ void MainWindow::on_tabWidget_currentChanged(int index) } else if (index == 1) { m_OrganizerCore.refreshBSAList(); } else if (index == 2) { - static bool first = true; - - if (first) { - m_DataTab->activated(); - first = false; - } + m_DataTab->activated(); } else if (index == 3) { refreshSaveList(); } -- cgit v1.3.1 From d0c16e60d734fe2f72c387552559bf125554fb2d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 7 Feb 2020 14:02:00 -0500 Subject: just disable recursive filtering when fully loading switching models clears persistent indexes and collapses everything --- src/datatab.cpp | 13 +++++++++++-- src/datatab.h | 1 + src/filetree.cpp | 26 +++++--------------------- src/filetree.h | 2 ++ src/filetreemodel.h | 6 ++++++ 5 files changed, 25 insertions(+), 23 deletions(-) (limited to 'src/datatab.h') diff --git a/src/datatab.cpp b/src/datatab.cpp index d8ce9c3a..f263c2c6 100644 --- a/src/datatab.cpp +++ b/src/datatab.cpp @@ -37,7 +37,7 @@ DataTab::DataTab( connect( &m_filter, &FilterWidget::aboutToChange, - [&]{ m_filetree->ensureFullyLoaded(); }); + [&]{ ensureFullyLoaded(); }); connect( ui.refresh, &QPushButton::clicked, @@ -107,7 +107,7 @@ void DataTab::updateTree() m_filetree->refresh(); if (!m_filter.empty()) { - m_filetree->ensureFullyLoaded(); + ensureFullyLoaded(); if (auto* m=m_filter.proxyModel()) { m->invalidate(); @@ -115,6 +115,15 @@ void DataTab::updateTree() } } +void DataTab::ensureFullyLoaded() +{ + if (!m_filetree->fullyLoaded()) { + m_filter.proxyModel()->setRecursiveFilteringEnabled(false); + m_filetree->ensureFullyLoaded(); + m_filter.proxyModel()->setRecursiveFilteringEnabled(true); + } +} + void DataTab::onConflicts() { updateOptions(); diff --git a/src/datatab.h b/src/datatab.h index 1e006898..a0f29a5f 100644 --- a/src/datatab.h +++ b/src/datatab.h @@ -56,4 +56,5 @@ private: void onConflicts(); void onArchives(); void updateOptions(); + void ensureFullyLoaded(); }; diff --git a/src/filetree.cpp b/src/filetree.cpp index 937ffe4c..19f2555a 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -157,30 +157,14 @@ void FileTree::clear() m_model->clear(); } -void FileTree::ensureFullyLoaded() +bool FileTree::fullyLoaded() const { - QAbstractProxyModel* proxy = nullptr; - - if (m_tree->model() != m_model) { - // looks like there's a proxy on the tree, disable it - proxy = dynamic_cast(m_tree->model()); - - m_tree->setModel(m_model); - - if (proxy) { - if (proxy->sourceModel() != m_model) - DebugBreak(); - - proxy->setSourceModel(nullptr); - } - } + return m_model->fullyLoaded(); +} +void FileTree::ensureFullyLoaded() +{ m_model->ensureFullyLoaded(); - - if (proxy) { - proxy->setSourceModel(m_model); - m_tree->setModel(proxy); - } } FileTreeItem* FileTree::singleSelection() diff --git a/src/filetree.h b/src/filetree.h index 3c3ffc07..c1458179 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -21,6 +21,8 @@ public: FileTreeModel* model(); void refresh(); void clear(); + + bool fullyLoaded() const; void ensureFullyLoaded(); void open(FileTreeItem* item=nullptr); diff --git a/src/filetreemodel.h b/src/filetreemodel.h index dfee597f..fbc7ec44 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -50,6 +50,12 @@ public: void refresh(); void clear(); + + bool fullyLoaded() const + { + return m_fullyLoaded; + } + void ensureFullyLoaded(); bool enabled() const; -- cgit v1.3.1