diff options
| -rw-r--r-- | src/mainwindow.cpp | 29 | ||||
| -rw-r--r-- | src/mainwindow.h | 2 | ||||
| -rw-r--r-- | src/modinfo.h | 10 | ||||
| -rw-r--r-- | src/modinfoseparator.h | 2 | ||||
| -rw-r--r-- | src/modlist.cpp | 2 | ||||
| -rw-r--r-- | src/modlistbypriorityproxy.cpp | 236 | ||||
| -rw-r--r-- | src/modlistbypriorityproxy.h | 49 |
7 files changed, 178 insertions, 152 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ddde1d9b..be879dc6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -544,14 +544,22 @@ MainWindow::MainWindow(Settings &settings processUpdates(); ui->statusBar->updateNormalMessage(m_OrganizerCore); - - on_groupCombo_currentIndexChanged(0); } void MainWindow::setupModList() { - m_ModListSortProxy = m_OrganizerCore.createModListProxyModel(); + m_ModListByPriorityProxy = new ModListByPriorityProxy(m_OrganizerCore.currentProfile(), &m_OrganizerCore); + connect(ui->modList, SIGNAL(expanded(QModelIndex)), m_ModListByPriorityProxy, SLOT(expanded(QModelIndex))); + connect(ui->modList, SIGNAL(collapsed(QModelIndex)), m_ModListByPriorityProxy, SLOT(collapsed(QModelIndex))); + connect(m_ModListByPriorityProxy, SIGNAL(expandItem(QModelIndex)), this, SLOT(expandModList(QModelIndex))); + + m_ModListSortProxy = new ModListSortProxy(m_OrganizerCore.currentProfile(), &m_OrganizerCore); ui->modList->setModel(m_ModListSortProxy); + + m_ModListByPriorityProxy->setSourceModel(m_OrganizerCore.modList()); + m_ModListSortProxy->setSourceModel(m_ModListByPriorityProxy); + emit m_OrganizerCore.modList()->layoutChanged(); + ui->modList->sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder); @@ -1267,15 +1275,7 @@ void MainWindow::espFilterChanged(const QString &filter) void MainWindow::expandModList(const QModelIndex &index) { - QAbstractItemModel *model = ui->modList->model(); - - for (int i = 0; i < model->rowCount(); ++i) { - QModelIndex targetIdx = model->index(i, 0); - if (model->data(targetIdx).toString() == index.data().toString()) { - ui->modList->expand(targetIdx); - break; - } - } + ui->modList->expand(m_ModListSortProxy->mapFromSource(index)); } @@ -1679,6 +1679,7 @@ void MainWindow::activateSelectedProfile() m_OrganizerCore.setCurrentProfile(ui->profileBox->currentText()); m_ModListSortProxy->setProfile(m_OrganizerCore.currentProfile()); + m_ModListByPriorityProxy->setProfile(m_OrganizerCore.currentProfile()); m_SavesTab->refreshSaveList(); m_OrganizerCore.refresh(); @@ -5951,8 +5952,8 @@ void MainWindow::on_groupCombo_currentIndexChanged(int index) connect(ui->modList, SIGNAL(collapsed(QModelIndex)), newModel, SLOT(collapsed(QModelIndex))); connect(newModel, SIGNAL(expandItem(QModelIndex)), this, SLOT(expandModList(QModelIndex))); } else { - m_ModListSortProxy->setSourceModel(new ModListByPriorityProxy(m_OrganizerCore.modList(), this)); - // m_ModListSortProxy->setSourceModel(m_OrganizerCore.modList()); + m_ModListSortProxy->setSourceModel(m_ModListByPriorityProxy); + emit m_OrganizerCore.modList()->layoutChanged(); } modFilterActive(m_ModListSortProxy->isFilterActive()); } diff --git a/src/mainwindow.h b/src/mainwindow.h index f8910361..19a1b179 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -26,6 +26,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "imoinfo.h" #include "iuserinterface.h" #include "modinfo.h" +#include "modlistbypriorityproxy.h" #include "modlistsortproxy.h" #include "tutorialcontrol.h" #include "plugincontainer.h" //class PluginContainer; @@ -297,6 +298,7 @@ private: QStringList m_DefaultArchives; ModListSortProxy *m_ModListSortProxy; + ModListByPriorityProxy *m_ModListByPriorityProxy; PluginListSortProxy *m_PluginListSortProxy; diff --git a/src/modinfo.h b/src/modinfo.h index 42abe51e..d04f6657 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -379,6 +379,11 @@ public: // IModInterface implementations / Re-declaration virtual std::shared_ptr<const MOBase::IFileTree> fileTree() const = 0; /** + * @return true if this object represents a regular mod. + */ + virtual bool isRegular() const { return false; } + + /** * @return true if this object represents the overwrite mod. */ virtual bool isOverwrite() const { return false; } @@ -492,11 +497,6 @@ public: // Mutable operations: public: // Methods after this do not come from IModInterface: /** - * @return true if this mod is a regular mod, false otherwise. - */ - virtual bool isRegular() const { return false; } - - /** * @return true if this mod is empty, false otherwise. */ virtual bool isEmpty() const { return false; } diff --git a/src/modinfoseparator.h b/src/modinfoseparator.h index 88262f37..c7df7184 100644 --- a/src/modinfoseparator.h +++ b/src/modinfoseparator.h @@ -55,7 +55,7 @@ protected: private: ModInfoSeparator( - PluginContainer* pluginContainer, + PluginContainer* pluginContainer, const MOBase::IPluginGame* game, const QDir& path, MOShared::DirectoryEntry** directoryStructure); }; diff --git a/src/modlist.cpp b/src/modlist.cpp index c8056140..4191e2db 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1236,8 +1236,8 @@ void ModList::removeRowForce(int row, const QModelIndex &parent) m_Profile->cancelModlistWrite(); beginRemoveRows(parent, row, row); ModInfo::removeMod(row); - endRemoveRows(); m_Profile->refreshModStatus(); // removes the mod from the status list + endRemoveRows(); m_Profile->writeModlist(); // this ensures the modified list gets written back before new mods can be installed notifyModRemoved(modInfo->name()); diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp index 69c6ff88..77b7262a 100644 --- a/src/modlistbypriorityproxy.cpp +++ b/src/modlistbypriorityproxy.cpp @@ -1,12 +1,13 @@ #include "modlistbypriorityproxy.h" #include "modinfo.h" +#include "profile.h" #include "modlist.h" +#include "log.h" -ModListByPriorityProxy::ModListByPriorityProxy(ModList* modList, QObject* parent) : - QAbstractProxyModel(parent), m_ModList(modList) +ModListByPriorityProxy::ModListByPriorityProxy(Profile* profile, QObject* parent) : + QAbstractProxyModel(parent), m_Profile(profile) { - setSourceModel(modList); } ModListByPriorityProxy::~ModListByPriorityProxy() @@ -16,7 +17,14 @@ ModListByPriorityProxy::~ModListByPriorityProxy() void ModListByPriorityProxy::setSourceModel(QAbstractItemModel* model) { QAbstractProxyModel::setSourceModel(model); - // connect(sourceModel(), &QAbstractItemModel::layoutChanged, this, &ModListByPriorityProxy::buildTree); + + if (sourceModel()) { + m_CollapsedItems.clear(); + connect(sourceModel(), &QAbstractItemModel::layoutChanged, this, &ModListByPriorityProxy::buildTree, Qt::UniqueConnection); + connect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, [this]() { buildTree(); }, Qt::UniqueConnection); + connect(sourceModel(), &QAbstractItemModel::modelReset, this, &ModListByPriorityProxy::buildTree, Qt::UniqueConnection); + buildTree(); + } } void ModListByPriorityProxy::buildTree() @@ -25,73 +33,48 @@ void ModListByPriorityProxy::buildTree() beginResetModel(); - endResetModel(); - -} + // reset the root + m_Root = { }; + m_IndexToItem.clear(); -std::vector<ModListByPriorityProxy::ModInfoWithPriority> ModListByPriorityProxy::topLevelItems() const -{ - std::vector<ModListByPriorityProxy::ModInfoWithPriority> items; - bool separator = false; - for (auto& [priority, index] : m_ModList->m_Profile->getAllIndexesByPriority()) { + TreeItem* root = &m_Root; + for (auto& [priority, index] : m_Profile->getAllIndexesByPriority()) { ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + + TreeItem* item; + if (modInfo->isSeparator()) { - items.emplace_back(modInfo, priority); - separator = true; + m_Root.children.push_back(std::make_unique<TreeItem>(modInfo, index, &m_Root)); + item = m_Root.children.back().get(); + root = item; } - else if (modInfo->isOverwrite() || !separator) { - items.emplace_back(modInfo, priority); + else if (modInfo->isOverwrite()) { + m_Root.children.push_back(std::make_unique<TreeItem>(modInfo, index, &m_Root)); + item = m_Root.children.back().get(); } - } - - return items; -} - -std::vector<ModListByPriorityProxy::ModInfoWithPriority> ModListByPriorityProxy::childItems(int priority) const -{ - std::vector<ModListByPriorityProxy::ModInfoWithPriority> children; - for (auto& [p, index] : m_ModList->m_Profile->getAllIndexesByPriority()) { - if (p > priority) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - if (modInfo->isSeparator()) { - break; - } - children.emplace_back(modInfo, p); + else { + root->children.push_back(std::make_unique<TreeItem>(modInfo, index, root)); + item = root->children.back().get(); } - } - return children; -} + m_IndexToItem[index] = item; -std::optional<ModListByPriorityProxy::ModInfoWithPriority> ModListByPriorityProxy::separator(int priority) const -{ - // overwrites - if (priority == ULONG_MAX) { - return {}; } - auto& indexByPriority = m_ModList->m_Profile->getAllIndexesByPriority(); - - auto it = indexByPriority.find(priority); - if (it == std::end(indexByPriority)) { - return {}; - } + endResetModel(); - { - ModInfo::Ptr modInfo = ModInfo::getByIndex(it->second); - if (modInfo->isSeparator() || modInfo->isOverwrite()) { - return {}; - } - } + // restore expand-state + expandItems(QModelIndex()); +} - auto rit = std::reverse_iterator{ it }; - for (; rit != std::rend(indexByPriority); ++rit) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(rit->second); - if (modInfo->isSeparator()) { - return ModInfoWithPriority{ modInfo, rit->first }; +void ModListByPriorityProxy::expandItems(const QModelIndex& index) +{ + for (int row = 0; row < rowCount(index); row++) { + QModelIndex idx = this->index(row, 0, QModelIndex()); + if (!m_CollapsedItems.contains(idx.data(Qt::DisplayRole).toString())) { + emit expandItem(idx); } + expandItems(idx); } - - return {}; } QModelIndex ModListByPriorityProxy::mapFromSource(const QModelIndex& sourceIndex) const @@ -100,24 +83,8 @@ QModelIndex ModListByPriorityProxy::mapFromSource(const QModelIndex& sourceIndex return QModelIndex(); } - auto topItems = topLevelItems(); - ModInfo::Ptr modInfo = ModInfo::getByIndex(sourceIndex.row()); - - for (std::size_t i = 0; i < topItems.size(); ++i) { - if (topItems[i].mod == modInfo) { - return createIndex(i, sourceIndex.column(), modInfo.get()); - } - } - - auto sep = separator(m_ModList->priority(modInfo->name())); - auto children = childItems(sep->priority); - for (std::size_t i = 0; i < children.size(); ++i) { - if (children[i].mod == modInfo) { - return createIndex(i, sourceIndex.column(), modInfo.get()); - } - } - - return QModelIndex(); + auto* item = m_IndexToItem.at(sourceIndex.row()); + return createIndex(item->parent->childIndex(item), sourceIndex.column(), item); } QModelIndex ModListByPriorityProxy::mapToSource(const QModelIndex& proxyIndex) const @@ -125,32 +92,23 @@ QModelIndex ModListByPriorityProxy::mapToSource(const QModelIndex& proxyIndex) c if (!proxyIndex.isValid()) { return QModelIndex(); } - auto topItems = topLevelItems(); - ModInfo::Ptr modInfo; - if (proxyIndex.parent().isValid()) { - ModInfo::Ptr parentInfo = topItems[proxyIndex.parent().row()].mod; - modInfo = childItems(m_ModList->priority(parentInfo->name()))[proxyIndex.row()].mod; - } - else { - modInfo = topItems[proxyIndex.row()].mod; - } - return sourceModel()->index(ModInfo::getIndex(modInfo->name()), proxyIndex.column(), mapToSource(proxyIndex.parent())); + auto* item = static_cast<TreeItem*>(proxyIndex.internalPointer()); + return sourceModel()->index(item->index, proxyIndex.column()); } int ModListByPriorityProxy::rowCount(const QModelIndex& parent) const { - auto topItems = topLevelItems(); if (!parent.isValid()) { - return topItems.size(); + return m_Root.children.size(); } - ModInfo::Ptr modInfo = topItems[parent.row()].mod; - if (!modInfo->isSeparator()) { - return 0; + auto* item = static_cast<TreeItem*>(parent.internalPointer()); + + if (item->mod->isSeparator()) { + return item->children.size(); } - auto priority = m_ModList->priority(modInfo->name()); - return childItems(priority).size(); + return 0; } int ModListByPriorityProxy::columnCount(const QModelIndex& index) const @@ -161,54 +119,90 @@ int ModListByPriorityProxy::columnCount(const QModelIndex& index) const QModelIndex ModListByPriorityProxy::parent(const QModelIndex& child) const { - - auto topItems = topLevelItems(); - ModInfo::Ptr modInfo; - if (child.parent().isValid()) { - ModInfo::Ptr parentInfo = topItems[child.parent().row()].mod; - modInfo = childItems(m_ModList->priority(parentInfo->name()))[child.row()].mod; - } - else { - modInfo = topItems[child.row()].mod; + if (!child.isValid()) { + return QModelIndex(); } - auto sep = separator(m_ModList->priority(modInfo->name())); + auto* item = static_cast<TreeItem*>(child.internalPointer()); - if (!sep) { + if (!item->parent || item->parent == &m_Root) { return QModelIndex(); } - for (std::size_t i = 0; i < topItems.size(); ++i) { - if (topItems[i].mod == sep->mod) { - return createIndex(i, child.column(), sep->mod.get()); - } - } - - return QModelIndex(); + return createIndex(item->parent->parent->childIndex(item->parent), 0, item->parent); } bool ModListByPriorityProxy::hasChildren(const QModelIndex& parent) const { if (!parent.isValid()) { - return false; + return m_Root.children.size() > 0; } - ModInfo* modInfo = static_cast<ModInfo*>(parent.internalPointer()); - for (auto& item : topLevelItems()) { - if (modInfo == item.mod) { - return modInfo->isSeparator() && !childItems(item.priority).empty(); + auto* item = static_cast<TreeItem*>(parent.internalPointer()); + return item->children.size() > 0; +} + +bool ModListByPriorityProxy::setData(const QModelIndex& index, const QVariant& value, int role) +{ + // only care about the "name" column + if (index.column() == 0 && role == Qt::EditRole) { + QString oldValue = data(index, role).toString(); + if (m_CollapsedItems.contains(oldValue)) { + m_CollapsedItems.erase(oldValue); + m_CollapsedItems.insert(value.toString()); } } - return false; + return QAbstractProxyModel::setData(index, value, role); +} + + +Qt::ItemFlags ModListByPriorityProxy::flags(const QModelIndex& idx) const +{ + if (!idx.isValid()) { + return sourceModel()->flags(QModelIndex()); + } + + // we check the flags of the root node and if drop is not enabled, it + // means we are dragging files. + Qt::ItemFlags rootFlags = sourceModel()->flags(QModelIndex()); + if (!rootFlags.testFlag(Qt::ItemIsDropEnabled)) { + return sourceModel()->flags(mapToSource(idx)); + } + + auto flags = sourceModel()->flags(mapToSource(idx)); + auto* item = static_cast<TreeItem*>(idx.internalPointer()); + + if (item->mod->isSeparator()) { + flags |= Qt::ItemIsDropEnabled; + } + + return flags; } QModelIndex ModListByPriorityProxy::index(int row, int column, const QModelIndex& parent) const { - auto topItems = topLevelItems(); + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } + + const TreeItem* parentItem; if (!parent.isValid()) { - return createIndex(row, column, topItems[row].mod.get()); + parentItem = &m_Root; } else { - auto children = childItems(topItems[parent.row()].priority); - return createIndex(row, column, children[row].mod.get()); + parentItem = static_cast<TreeItem*>(parent.internalPointer()); + } + return createIndex(row, column, parentItem->children[row].get()); +} + +void ModListByPriorityProxy::expanded(const QModelIndex& index) +{ + auto it = m_CollapsedItems.find(index.data(Qt::DisplayRole).toString()); + if (it != m_CollapsedItems.end()) { + m_CollapsedItems.erase(it); } } + +void ModListByPriorityProxy::collapsed(const QModelIndex& index) +{ + m_CollapsedItems.insert(index.data(Qt::DisplayRole).toString()); +} diff --git a/src/modlistbypriorityproxy.h b/src/modlistbypriorityproxy.h index 6ec04b9b..d5f59f4c 100644 --- a/src/modlistbypriorityproxy.h +++ b/src/modlistbypriorityproxy.h @@ -2,6 +2,7 @@ #define MODLISBYPRIORITYPROXY_H #include <optional> +#include <set> #include <vector> #include <QAbstractProxyModel> @@ -14,43 +15,71 @@ #include "modinfo.h" class ModList; +class Profile; class ModListByPriorityProxy : public QAbstractProxyModel { Q_OBJECT public: - explicit ModListByPriorityProxy(ModList* modList, QObject* parent = nullptr); + explicit ModListByPriorityProxy(Profile* profile, QObject* parent = nullptr); ~ModListByPriorityProxy(); + void setProfile(Profile* profile) { m_Profile = profile; } + void setSourceModel(QAbstractItemModel* sourceModel) override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; + Qt::ItemFlags flags(const QModelIndex& idx) const override; QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& index) const override; bool hasChildren(const QModelIndex& parent) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; + QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override; QModelIndex mapToSource(const QModelIndex& proxyIndex) const override; +signals: + void expandItem(const QModelIndex& index); + +public slots: + + void expanded(const QModelIndex& index); + void collapsed(const QModelIndex& index); + private: void buildTree(); + void expandItems(const QModelIndex& index); - struct ModInfoWithPriority { - const ModInfo::Ptr mod; - const int priority; - }; + struct TreeItem { + ModInfo::Ptr mod; + unsigned int index; + std::vector<std::unique_ptr<TreeItem>> children; + TreeItem* parent; - std::vector<ModInfoWithPriority> topLevelItems() const; - std::vector<ModInfoWithPriority> childItems(int priority) const; - std::optional<ModInfoWithPriority> separator(int priority) const; + std::size_t childIndex(TreeItem* child) const { + for (std::size_t i = 0; i < children.size(); ++i) { + if (children[i].get() == child) { + return i; + } + } + return -1; + } -private: + TreeItem() : TreeItem(nullptr, -1) { } + TreeItem(ModInfo::Ptr mod, unsigned int index, TreeItem* parent = nullptr) : + mod(mod), index(index), parent(parent) { } + }; - ModList* m_ModList; + TreeItem m_Root; + std::map<unsigned int, TreeItem*> m_IndexToItem; + std::set<QString> m_CollapsedItems; +private: + Profile* m_Profile; }; #endif //GROUPINGPROXY_H |
