diff options
| author | Mikaƫl Capelle <capelle.mikael@gmail.com> | 2021-01-10 10:25:37 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-01-10 10:25:37 +0100 |
| commit | 80e44a9e3ade61695b4807a3a900d2866138ecac (patch) | |
| tree | 1ef9904664d8d34dba6692bbf5325aea8c199d08 /src/modlistbypriorityproxy.cpp | |
| parent | eaec140f7c823012c09536175d8917dddcacdb7c (diff) | |
| parent | 4a0ce804ea486744e5f6140a0ce4538d99e21ce3 (diff) | |
Merge pull request #1338 from Holt59/collapsible-separators
Collapsible separators (and refactoring).
Diffstat (limited to 'src/modlistbypriorityproxy.cpp')
| -rw-r--r-- | src/modlistbypriorityproxy.cpp | 322 |
1 files changed, 322 insertions, 0 deletions
diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp new file mode 100644 index 00000000..6f549d91 --- /dev/null +++ b/src/modlistbypriorityproxy.cpp @@ -0,0 +1,322 @@ +#include "modlistbypriorityproxy.h" + +#include "modinfo.h" +#include "profile.h" +#include "organizercore.h" +#include "modlist.h" +#include "modlistdropinfo.h" +#include "log.h" + +ModListByPriorityProxy::ModListByPriorityProxy(Profile* profile, OrganizerCore& core, QObject* parent) : + QAbstractProxyModel(parent), m_core(core), m_profile(profile) +{ +} + +ModListByPriorityProxy::~ModListByPriorityProxy() +{ +} + +void ModListByPriorityProxy::setSourceModel(QAbstractItemModel* model) +{ + if (sourceModel()) { + disconnect(sourceModel(), nullptr, this, nullptr); + } + + QAbstractProxyModel::setSourceModel(model); + + if (sourceModel()) { + connect(sourceModel(), &QAbstractItemModel::layoutChanged, this, &ModListByPriorityProxy::buildTree, Qt::UniqueConnection); + connect(sourceModel(), &QAbstractItemModel::rowsRemoved, this, &ModListByPriorityProxy::buildTree, Qt::UniqueConnection); + connect(sourceModel(), &QAbstractItemModel::modelReset, this, &ModListByPriorityProxy::buildTree, Qt::UniqueConnection); + connect(sourceModel(), &QAbstractItemModel::dataChanged, this, &ModListByPriorityProxy::modelDataChanged, Qt::UniqueConnection); + refresh(); + } +} + +void ModListByPriorityProxy::refresh() +{ + buildTree(); +} + +void ModListByPriorityProxy::setProfile(Profile* profile) +{ + m_profile = profile; +} + +void ModListByPriorityProxy::buildTree() +{ + if (!sourceModel()) return; + + beginResetModel(); + + // reset the root + m_Root = { }; + m_IndexToItem.clear(); + + TreeItem* root = &m_Root; + std::unique_ptr<TreeItem> overwrite; + std::vector<std::unique_ptr<TreeItem>> backups; + for (auto& [priority, index] : m_profile->getAllIndexesByPriority()) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + + TreeItem* item; + + if (modInfo->isSeparator()) { + 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()) { + // do not push here, because the overwrite is usually not at the right position + overwrite = std::make_unique<TreeItem>(modInfo, index, &m_Root); + item = overwrite.get(); + } + else if (modInfo->isBackup()) { + backups.push_back(std::make_unique<TreeItem>(modInfo, index, &m_Root)); + item = backups.back().get(); + } + else { + root->children.push_back(std::make_unique<TreeItem>(modInfo, index, root)); + item = root->children.back().get(); + } + + m_IndexToItem[index] = item; + } + + m_Root.children.insert(m_Root.children.begin(), + std::make_move_iterator(backups.begin()), std::make_move_iterator(backups.end())); + m_Root.children.push_back(std::move(overwrite)); + + endResetModel(); +} + +void ModListByPriorityProxy::modelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles) +{ + QModelIndex proxyTopLeft = mapFromSource(topLeft); + if (!proxyTopLeft.isValid()) { + return; + } + + if (topLeft == bottomRight) { + emit dataChanged(proxyTopLeft, proxyTopLeft); + } + else { + QModelIndex proxyBottomRight = mapFromSource(bottomRight); + emit dataChanged(proxyTopLeft, proxyBottomRight); + } +} + +QModelIndex ModListByPriorityProxy::mapFromSource(const QModelIndex& sourceIndex) const +{ + if (!sourceIndex.isValid()) { + 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 +{ + if (!proxyIndex.isValid()) { + return QModelIndex(); + } + auto* item = static_cast<TreeItem*>(proxyIndex.internalPointer()); + + return sourceModel()->index(item->index, proxyIndex.column()); +} + +int ModListByPriorityProxy::rowCount(const QModelIndex& parent) const +{ + if (!parent.isValid()) { + return m_Root.children.size(); + } + + auto* item = static_cast<TreeItem*>(parent.internalPointer()); + + if (item->mod->isSeparator()) { + return item->children.size(); + } + + return 0; +} + +int ModListByPriorityProxy::columnCount(const QModelIndex& index) const +{ + return sourceModel()->columnCount(mapToSource(index)); +} + +QModelIndex ModListByPriorityProxy::parent(const QModelIndex& child) const +{ + if (!child.isValid()) { + return QModelIndex(); + } + + auto* item = static_cast<TreeItem*>(child.internalPointer()); + + if (!item->parent || item->parent == &m_Root) { + return QModelIndex(); + } + + return createIndex(item->parent->parent->childIndex(item->parent), child.column(), item->parent); +} + +bool ModListByPriorityProxy::hasChildren(const QModelIndex& parent) const +{ + if (!parent.isValid()) { + return m_Root.children.size() > 0; + } + 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 QAbstractProxyModel::setData(index, value, role); +} + +bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const +{ + ModListDropInfo dropInfo(data, m_core); + + if (!dropInfo.isValid() || dropInfo.isLocalFileDrop()) { + return QAbstractProxyModel::canDropMimeData(data, action, row, column, parent); + } + + if (dropInfo.isModDrop()) { + + bool hasSeparator = false; + unsigned int firstRowIndex = -1; + + int firstRowPriority = INT_MAX; + for (auto sourceRow : dropInfo.rows()) { + hasSeparator = hasSeparator || ModInfo::getByIndex(sourceRow)->isSeparator(); + if (m_profile->getModPriority(sourceRow) < firstRowPriority) { + firstRowIndex = sourceRow; + firstRowPriority = m_profile->getModPriority(sourceRow); + } + } + + bool firstRowSeparator = firstRowIndex != -1 && ModInfo::getByIndex(firstRowIndex)->isSeparator(); + + // row = -1 and invalid parent means we're dropping onto an item, we don't want to drop + // separators onto items or items into their own separator + if (row == -1 && parent.isValid()) { + auto* parentItem = static_cast<TreeItem*>(parent.internalPointer()); + if (hasSeparator) { + return !parentItem->mod->isSeparator(); + } + + for (auto row : dropInfo.rows()) { + auto it = m_IndexToItem.find(row); + if (it != m_IndexToItem.end() && it->second->parent == parentItem) { + return false; + } + } + } + + // first row is a separator, we can drop it anywhere + if (firstRowSeparator) { + return QAbstractProxyModel::canDropMimeData(data, action, row, column, parent); + } + } + + // top-level drop is disabled unless it's before the first separator + if (!parent.isValid() && row >= 0) { + + // the row may be outside of the children list if we insert at the end + if (row >= m_Root.children.size()) { + return false; + } + + // if the previous row is a collapsed separator, disable dropping + if (row > 0 && m_Root.children[row - 1]->mod->isSeparator()) { + // we cannot use the name of the mod directly because it does not exactly + // match the display value (e.g. for separators) + QString display = sourceModel()->index(m_Root.children[row - 1]->index, ModList::COL_NAME).data(Qt::DisplayRole).toString(); + if (m_CollapsedItems.contains(display) + && (m_Root.children[row]->mod->isSeparator() || m_Root.children[row]->mod->isOverwrite())) { + return false; + } + } + } + + return QAbstractProxyModel::canDropMimeData(data, action, row, column, parent); +} + +bool ModListByPriorityProxy::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) +{ + // we need to fix the source model row + ModListDropInfo dropInfo(data, m_core); + int sourceRow = -1; + + if (dropInfo.isLocalFileDrop()) { + if (parent.isValid()) { + sourceRow = static_cast<TreeItem*>(parent.internalPointer())->index; + } + } + else { + if (row >= 0) { + if (!parent.isValid()) { + if (row < m_Root.children.size()) { + sourceRow = m_Root.children[row]->index; + if (row > 0 + && m_Root.children[row - 1]->mod->isSeparator() + && !m_Root.children[row - 1]->children.empty() + && m_dropPosition == ModListView::DropPosition::BelowItem) { + sourceRow = m_Root.children[row - 1]->children[0]->index; + } + } + else { + sourceRow = ModInfo::getNumMods(); + } + } + else { + auto* item = static_cast<TreeItem*>(parent.internalPointer()); + + if (row < item->children.size()) { + sourceRow = item->children[row]->index; + } + else if (parent.row() + 1 < m_Root.children.size()) { + sourceRow = m_Root.children[parent.row() + 1]->index; + } + } + } + else if (parent.isValid()) { + // this is a drop in a separator + sourceRow = m_Root.children[parent.row() + 1]->index; + } + } + + return sourceModel()->dropMimeData(data, action, sourceRow, column, QModelIndex()); +} + +QModelIndex ModListByPriorityProxy::index(int row, int column, const QModelIndex& parent) const +{ + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } + + const TreeItem* parentItem; + if (!parent.isValid()) { + parentItem = &m_Root; + } + else { + parentItem = static_cast<TreeItem*>(parent.internalPointer()); + } + + return createIndex(row, column, parentItem->children[row].get()); +} + +void ModListByPriorityProxy::onDropEnter(const QMimeData*, ModListView::DropPosition dropPosition) +{ + m_dropPosition = dropPosition; +} |
