From 1e54b4b57682b544bca0bb9d563fdb3d747073f2 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 24 Jun 2019 14:50:55 -0400 Subject: switched all conflict lists to QTreeView with custom model --- src/modinfodialog.ui | 56 +-- src/modinfodialogconflicts.cpp | 766 ++++++++++++++++++++++++----------------- src/modinfodialogconflicts.h | 37 +- 3 files changed, 471 insertions(+), 388 deletions(-) (limited to 'src') diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 5defcd57..cdf4c80f 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -406,7 +406,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - 1 + 0 @@ -421,7 +421,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e 100 - + 0 @@ -473,7 +473,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -489,25 +489,6 @@ text-align: left; true - - 2 - - - 200 - - - 365 - - - - File - - - - - Overwritten Mods - - @@ -546,7 +527,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -562,25 +543,6 @@ text-align: left; true - - 2 - - - 200 - - - 365 - - - - File - - - - - Providing Mod - - @@ -619,7 +581,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -635,14 +597,6 @@ text-align: left; true - - 1 - - - - File - - diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 55fbf2e5..d1384d7a 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -10,7 +10,7 @@ using namespace MOBase; // if there are more than 50 selected items in the conflict tree, don't bother // checking whether menu items apply to them, just show all of them -const int max_scan_for_context_menu = 50; +const std::size_t max_small_selection = 50; class ElideLeftDelegate : public QStyledItemDelegate @@ -27,60 +27,62 @@ protected: }; -class ConflictItem : public QTreeWidgetItem +class ConflictItem { public: - static const int FILENAME_USERROLE = Qt::UserRole + 1; - static const int ALT_ORIGIN_USERROLE = Qt::UserRole + 2; - static const int ARCHIVE_USERROLE = Qt::UserRole + 3; - static const int INDEX_USERROLE = Qt::UserRole + 4; - static const int HAS_ALTS_USERROLE = Qt::UserRole + 5; - ConflictItem( - QStringList columns, FileEntry::Index index, const QString& fileName, - bool hasAltOrigins, const QString& altOrigin, bool archive) - : QTreeWidgetItem(columns) + QString before, QString relativeName, QString after, + FileEntry::Index index, QString fileName, + bool hasAltOrigins, QString altOrigin, bool archive) : + m_before(std::move(before)), + m_relativeName(std::move(relativeName)), + m_after(std::move(after)), + m_index(index), + m_fileName(std::move(fileName)), + m_hasAltOrigins(hasAltOrigins), + m_altOrigin(std::move(altOrigin)), + m_isArchive(archive) { - setData(0, FILENAME_USERROLE, fileName); - setData(0, ALT_ORIGIN_USERROLE, altOrigin); - setData(0, ARCHIVE_USERROLE, archive); - setData(0, INDEX_USERROLE, index); - setData(0, HAS_ALTS_USERROLE, hasAltOrigins); - - if (archive) { - QFont f = font(0); - f.setItalic(true); - - for (int i=0; i); - return data(0, INDEX_USERROLE).toUInt(); + return m_index; } bool canHide() const @@ -103,21 +105,276 @@ public: return canPreviewFile(pluginContainer, isArchive(), fileName()); } - bool operator<(const QTreeWidgetItem& other) const +private: + QString m_before; + QString m_relativeName; + QString m_after; + FileEntry::Index m_index; + QString m_fileName; + bool m_hasAltOrigins; + QString m_altOrigin; + bool m_isArchive; +}; + + +class ConflictListModel : public QAbstractItemModel +{ +public: + struct Column { - const int column = treeWidget()->sortColumn(); + QString caption; + const QString& (ConflictItem::*getText)() const; + }; - if (column >= columnCount() || column >= other.columnCount()) { - // shouldn't happen - qWarning().nospace() << "ConflictItem::operator<() mistmatch in column count"; - return false; + ConflictListModel(QTreeView* tree, std::vector columns) + : m_tree(tree), m_columns(std::move(columns)) + { + m_tree->setModel(this); + } + + void clear() + { + m_items.clear(); + } + + void reserve(std::size_t s) + { + m_items.reserve(s); + } + + QModelIndex index(int row, int col, const QModelIndex& ={}) const override + { + return createIndex(row, col); + } + + QModelIndex parent(const QModelIndex&) const override + { + return {}; + } + + int rowCount(const QModelIndex& parent={}) const override + { + if (parent.isValid()) { + return 0; + } + + return static_cast(m_items.size()); + } + + int columnCount(const QModelIndex& ={}) const override + { + return static_cast(m_columns.size()); + } + + QVariant data(const QModelIndex& index, int role) const override + { + if (role == Qt::DisplayRole || role == Qt::FontRole) { + const auto row = index.row(); + if (row < 0) { + return {}; + } + + const auto i = static_cast(row); + if (i >= m_items.size()) { + return {}; + } + + const auto col = index.column(); + if (col < 0) { + return {}; + } + + const auto c = static_cast(col); + if (c >= m_columns.size()) { + return {}; + } + + const auto& item = m_items[i]; + + if (role == Qt::DisplayRole) { + return (item.*m_columns[c].getText)(); + } else if (role == Qt::FontRole) { + if (item.isArchive()) { + QFont f = m_tree->font(); + f.setItalic(true); + return f; + } + } + } + + return {}; + } + + QVariant headerData(int col, Qt::Orientation, int role) const + { + if (role == Qt::DisplayRole) { + if (col < 0) { + return {}; + } + + const auto i = static_cast(col); + if (i >= m_columns.size()) { + return {}; + } + + return m_columns[i].caption; } - return (naturalCompare(text(column), other.text(column)) < 0); + return {}; + } + + void sort(int colIndex, Qt::SortOrder order=Qt::AscendingOrder) + { + if (colIndex < 0) { + return; + } + + const auto c = static_cast(colIndex); + if (c >= m_columns.size()) { + return; + } + + const auto& col = m_columns[c]; + + // avoids branching on sort order while sorting + auto sortAsc = [&](const auto& a, const auto& b) { + return (naturalCompare((a.*col.getText)(), (b.*col.getText)()) < 0); + }; + + auto sortDesc = [&](const auto& a, const auto& b) { + return (naturalCompare((a.*col.getText)(), (b.*col.getText)()) > 0); + }; + + if (order == Qt::AscendingOrder) { + std::sort(m_items.begin(), m_items.end(), sortAsc); + } else { + std::sort(m_items.begin(), m_items.end(), sortDesc); + } + + emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); + } + + void add(ConflictItem item) + { + m_items.emplace_back(std::move(item)); + } + + void finished() + { + beginResetModel(); + endResetModel(); + } + + const ConflictItem* getItem(std::size_t row) const + { + if (row >= m_items.size()) { + return nullptr; + } + + return &m_items[row]; + } + +private: + QTreeView* m_tree; + std::vector m_columns; + std::vector m_items; +}; + + +class OverwriteConflictListModel : public ConflictListModel +{ +public: + OverwriteConflictListModel(QTreeView* tree) + : ConflictListModel(tree, { + {tr("File"), &ConflictItem::relativeName}, + {tr("Overwritten Mods"), &ConflictItem::before} + }) + { + } +}; + + +class OverwrittenConflictListModel : public ConflictListModel +{ +public: + OverwrittenConflictListModel(QTreeView* tree) + : ConflictListModel(tree, { + {tr("File"), &ConflictItem::relativeName}, + {tr("Providing Mod"), &ConflictItem::after} + }) + { + } +}; + + +class NoConflictListModel : public ConflictListModel +{ +public: + NoConflictListModel(QTreeView* tree) + : ConflictListModel(tree, { + {tr("File"), &ConflictItem::relativeName} + }) + { + } +}; + + +class AdvancedConflictListModel : public ConflictListModel +{ +public: + AdvancedConflictListModel(QTreeView* tree) + : ConflictListModel(tree, { + {tr("Overwrites"), &ConflictItem::before}, + {tr("File"), &ConflictItem::relativeName}, + {tr("Overwritten By"), &ConflictItem::after} + }) + { } }; +std::size_t smallSelectionSize(const QTreeView* tree) +{ + const std::size_t too_many = std::numeric_limits::max(); + + std::size_t n = 0; + const auto* sel = tree->selectionModel(); + + for (const auto& range : sel->selection()) { + n += range.height(); + + if (n >= max_small_selection) { + return too_many; + } + } + + return n; +} + +template +void for_each_in_selection(QTreeView* tree, F&& f) +{ + const auto* sel = tree->selectionModel(); + const auto* model = dynamic_cast(tree->model()); + + if (!model) { + qCritical() << "tree doesn't have a ConflictListModel"; + return; + } + + for (const auto& range : sel->selection()) { + // ranges are inclusive + for (int row=range.top(); row<=range.bottom(); ++row) { + if (auto* item=model->getItem(static_cast(row))) { + if (!f(item)) { + return; + } + } + } + } +} + + ConflictsTab::ConflictsTab( OrganizerCore& oc, PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, int id) : @@ -169,52 +426,55 @@ bool ConflictsTab::canHandleUnmanaged() const return true; } -void ConflictsTab::changeItemsVisibility( - const QList& items, bool visible) +void ConflictsTab::changeItemsVisibility(QTreeView* tree, bool visible) { bool changed = false; bool stop = false; - qDebug().nospace() + const auto n = smallSelectionSize(tree); + + qDebug().nospace().noquote() << (visible ? "unhiding" : "hiding") << " " - << items.size() << " conflict files"; + << (n > max_small_selection ? "a lot of" : QString("%1").arg(n)) + << " conflict files"; QFlags flags = (visible ? FileRenamer::UNHIDE : FileRenamer::HIDE); - if (items.size() > 1) { + if (n > 1) { flags |= FileRenamer::MULTIPLE; } FileRenamer renamer(parentWidget(), flags); - for (const auto* item : items) { - if (stop) { - break; - } + auto* model = dynamic_cast(tree->model()); + if (!model) { + qCritical() << "list doesn't have a ConflictListModel"; + return; + } - const auto* ci = dynamic_cast(item); - if (!ci) { - continue; + for_each_in_selection(tree, [&](const ConflictItem* item) { + if (stop) { + return false; } auto result = FileRenamer::RESULT_CANCEL; if (visible) { - if (!ci->canUnhide()) { - qDebug().nospace() << "cannot unhide " << item->text(0) << ", skipping"; - continue; + if (!item->canUnhide()) { + qDebug().nospace() << "cannot unhide " << item->relativeName() << ", skipping"; + return true; } - result = unhideFile(renamer, ci->fileName()); + result = unhideFile(renamer, item->fileName()); } else { - if (!ci->canHide()) { - qDebug().nospace() << "cannot hide " << item->text(0) << ", skipping"; - continue; + if (!item->canHide()) { + qDebug().nospace() << "cannot hide " << item->relativeName() << ", skipping"; + return true; } - result = hideFile(renamer, ci->fileName()); + result = hideFile(renamer, item->fileName()); } switch (result) { @@ -235,7 +495,9 @@ void ConflictsTab::changeItemsVisibility( break; } } - } + + return true; + }); qDebug().nospace() << (visible ? "unhiding" : "hiding") << " conflict files done"; @@ -250,38 +512,36 @@ void ConflictsTab::changeItemsVisibility( } } -void ConflictsTab::openItems(const QList& items) +void ConflictsTab::openItems(QTreeView* tree) { // the menu item is only shown for a single selection, but handle all of them // in case this changes - for (auto* item : items) { - if (auto* ci=dynamic_cast(item)) { - core().executeFileVirtualized(parentWidget(), ci->fileName()); - } - } + for_each_in_selection(tree, [&](const ConflictItem* item) { + core().executeFileVirtualized(parentWidget(), item->fileName()); + return true; + }); } -void ConflictsTab::previewItems(const QList& items) +void ConflictsTab::previewItems(QTreeView* tree) { // the menu item is only shown for a single selection, but handle all of them // in case this changes - for (auto* item : items) { - if (auto* ci=dynamic_cast(item)) { - core().previewFileWithAlternatives(parentWidget(), ci->fileName()); - } - } + for_each_in_selection(tree, [&](const ConflictItem* item) { + core().previewFileWithAlternatives(parentWidget(), item->fileName()); + return true; + }); } -void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) +void ConflictsTab::showContextMenu(const QPoint &pos, QTreeView* tree) { - auto actions = createMenuActions(tree->selectedItems()); + auto actions = createMenuActions(tree); QMenu menu; // open if (actions.open) { connect(actions.open, &QAction::triggered, [&]{ - openItems(tree->selectedItems()); + openItems(tree); }); menu.addAction(actions.open); @@ -290,7 +550,7 @@ void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) // preview if (actions.preview) { connect(actions.preview, &QAction::triggered, [&]{ - previewItems(tree->selectedItems()); + previewItems(tree); }); menu.addAction(actions.preview); @@ -299,7 +559,7 @@ void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) // hide if (actions.hide) { connect(actions.hide, &QAction::triggered, [&]{ - changeItemsVisibility(tree->selectedItems(), false); + changeItemsVisibility(tree, false); }); menu.addAction(actions.hide); @@ -308,7 +568,7 @@ void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) // unhide if (actions.unhide) { connect(actions.unhide, &QAction::triggered, [&]{ - changeItemsVisibility(tree->selectedItems(), true); + changeItemsVisibility(tree, true); }); menu.addAction(actions.unhide); @@ -332,10 +592,9 @@ void ConflictsTab::showContextMenu(const QPoint &pos, QTreeWidget* tree) } } -ConflictsTab::Actions ConflictsTab::createMenuActions( - const QList& selection) +ConflictsTab::Actions ConflictsTab::createMenuActions(QTreeView* tree) { - if (selection.empty()) { + if (tree->selectionModel()->selection().isEmpty()) { return {}; } @@ -345,18 +604,28 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( bool enablePreview = true; bool enableGoto = true; - if (selection.size() == 1) { + const auto n = smallSelectionSize(tree); + + const auto* model = dynamic_cast(tree->model()); + if (!model) { + qCritical() << "tree doesn't have a ConflictListModel"; + return {}; + } + + if (n == 1) { // this is a single selection - const auto* ci = dynamic_cast(selection[0]); - if (!ci) { + const auto* item = model->getItem(static_cast( + tree->selectionModel()->selectedRows()[0].row())); + + if (!item) { return {}; } - enableHide = ci->canHide(); - enableUnhide = ci->canUnhide(); - enableOpen = ci->canOpen(); - enablePreview = ci->canPreview(plugin()); - enableGoto = ci->hasAlts(); + enableHide = item->canHide(); + enableUnhide = item->canUnhide(); + enableOpen = item->canOpen(); + enablePreview = item->canPreview(plugin()); + enableGoto = item->hasAlts(); } else { // this is a multiple selection, don't show open/preview so users don't open @@ -367,28 +636,28 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( // don't bother with this on multiple selection, at least for now enableGoto = false; - if (selection.size() < max_scan_for_context_menu) { + if (n <= max_small_selection) { // if the number of selected items is low, checking them to accurately // show the menu items is worth it enableHide = false; enableUnhide = false; - for (const auto* item : selection) { - if (const auto* ci=dynamic_cast(item)) { - if (ci->canHide()) { - enableHide = true; - } + for_each_in_selection(tree, [&](const ConflictItem* item) { + if (item->canHide()) { + enableHide = true; + } - if (ci->canUnhide()) { - enableUnhide = true; - } + if (item->canUnhide()) { + enableUnhide = true; + } - if (enableHide && enableUnhide && enableGoto) { - // found all, no need to check more - break; - } + if (enableHide && enableUnhide && enableGoto) { + // found all, no need to check more + return false; } - } + + return true; + }); } } @@ -411,22 +680,19 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( actions.gotoMenu = new QMenu(tr("Go to..."), parentWidget()); actions.gotoMenu->setEnabled(enableGoto); - if (enableGoto) { - actions.gotoActions = createGotoActions(selection); + if (enableGoto && n == 1) { + const auto* item = model->getItem(static_cast( + tree->selectionModel()->selectedRows()[0].row())); + + actions.gotoActions = createGotoActions(item); } return actions; } -std::vector ConflictsTab::createGotoActions( - const QList& selection) +std::vector ConflictsTab::createGotoActions(const ConflictItem* item) { - if (!origin() || selection.size() != 1) { - return {}; - } - - const auto* item = dynamic_cast(selection[0]); - if (!item) { + if (!origin()) { return {}; } @@ -468,39 +734,42 @@ std::vector ConflictsTab::createGotoActions( GeneralConflictsTab::GeneralConflictsTab( - ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) - : m_tab(tab), ui(pui), m_core(oc) + ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) : + m_tab(tab), ui(pui), m_core(oc), + m_overwriteModel(new OverwriteConflictListModel(ui->overwriteTree)), + m_overwrittenModel(new OverwrittenConflictListModel(ui->overwrittenTree)), + m_noConflictModel(new NoConflictListModel(ui->noConflictTree)) { m_expanders.overwrite.set(ui->overwriteExpander, ui->overwriteTree, true); m_expanders.overwritten.set(ui->overwrittenExpander, ui->overwrittenTree, true); m_expanders.nonconflict.set(ui->noConflictExpander, ui->noConflictTree); QObject::connect( - ui->overwriteTree, &QTreeWidget::itemDoubleClicked, - [&](auto* item, int col){ onOverwriteActivated(item, col); }); + ui->overwriteTree, &QTreeView::doubleClicked, + [&](auto&& item){ onOverwriteActivated(item); }); QObject::connect( - ui->overwrittenTree, &QTreeWidget::itemDoubleClicked, - [&](auto* item, int col){ onOverwrittenActivated(item, col); }); + ui->overwrittenTree, &QTreeView::doubleClicked, + [&](auto&& item){ onOverwrittenActivated(item); }); QObject::connect( - ui->overwriteTree, &QTreeWidget::customContextMenuRequested, + ui->overwriteTree, &QTreeView::customContextMenuRequested, [&](const QPoint& p){ m_tab->showContextMenu(p, ui->overwriteTree); }); QObject::connect( - ui->overwrittenTree, &QTreeWidget::customContextMenuRequested, + ui->overwrittenTree, &QTreeView::customContextMenuRequested, [&](const QPoint& p){ m_tab->showContextMenu(p, ui->overwrittenTree); }); QObject::connect( - ui->noConflictTree, &QTreeWidget::customContextMenuRequested, + ui->noConflictTree, &QTreeView::customContextMenuRequested, [&](const QPoint& p){ m_tab->showContextMenu(p, ui->noConflictTree); }); } void GeneralConflictsTab::clear() { - ui->overwriteTree->clear(); - ui->overwrittenTree->clear(); - ui->noConflictTree->clear(); + m_overwriteModel->clear(); + m_overwrittenModel->clear(); + m_noConflictModel->clear(); ui->overwriteCount->display(0); ui->overwrittenCount->display(0); @@ -568,10 +837,6 @@ bool GeneralConflictsTab::update() int numOverwrite = 0; int numOverwritten = 0; - QList overwriteItems; - QList overwrittenItems; - QList noConflictItems; - if (m_tab->origin() != nullptr) { const auto rootPath = m_tab->mod()->absolutePath(); @@ -585,19 +850,19 @@ bool GeneralConflictsTab::update() if (fileOrigin == m_tab->origin()->getID()) { if (!alternatives.empty()) { - overwriteItems.append(createOverwriteItem( - file->getIndex(), archive, fileName, relativeName, alternatives)); + m_overwriteModel->add(createOverwriteItem( + file->getIndex(), archive, fileName, relativeName, alternatives)); ++numOverwrite; } else { // otherwise, put the file in the noconflict tree - noConflictItems.append(createNoConflictItem( + m_noConflictModel->add(createNoConflictItem( file->getIndex(), archive, fileName, relativeName)); ++numNonConflicting; } } else { - overwrittenItems.append(createOverwrittenItem( + m_overwrittenModel->add(createOverwrittenItem( file->getIndex(), fileOrigin, archive, fileName, relativeName)); ++numOverwritten; @@ -605,9 +870,9 @@ bool GeneralConflictsTab::update() } } - ui->overwriteTree->addTopLevelItems(overwriteItems); - ui->overwrittenTree->addTopLevelItems(overwrittenItems); - ui->noConflictTree->addTopLevelItems(noConflictItems); + m_overwriteModel->finished(); + m_overwrittenModel->finished(); + m_noConflictModel->finished(); ui->overwriteCount->display(numOverwrite); ui->overwrittenCount->display(numOverwritten); @@ -616,7 +881,7 @@ bool GeneralConflictsTab::update() return (numOverwrite > 0 || numOverwritten > 0); } -QTreeWidgetItem* GeneralConflictsTab::createOverwriteItem( +ConflictItem GeneralConflictsTab::createOverwriteItem( FileEntry::Index index, bool archive, const QString& fileName, const QString& relativeName, const FileEntry::AlternativesVector& alternatives) @@ -638,17 +903,17 @@ QTreeWidgetItem* GeneralConflictsTab::createOverwriteItem( const auto origin = ToQString(ds.getOriginByID(alternatives.back().first).getName()); - return new ConflictItem(fields, index, fileName, true, origin, archive); + return {altString, relativeName, "", index, fileName, true, origin, archive}; } -QTreeWidgetItem* GeneralConflictsTab::createNoConflictItem( +ConflictItem GeneralConflictsTab::createNoConflictItem( FileEntry::Index index, bool archive, const QString& fileName, const QString& relativeName) { - return new ConflictItem({relativeName}, index, fileName, false, "", archive); + return {"", relativeName, "", index, fileName, false, "", archive}; } -QTreeWidgetItem* GeneralConflictsTab::createOverwrittenItem( +ConflictItem GeneralConflictsTab::createOverwrittenItem( FileEntry::Index index, int fileOrigin, bool archive, const QString& fileName, const QString& relativeName) { @@ -659,193 +924,53 @@ QTreeWidgetItem* GeneralConflictsTab::createOverwrittenItem( QStringList fields(relativeName); fields.append(ToQString(realOrigin.getName())); - return new ConflictItem( - fields, index, fileName, true, ToQString(realOrigin.getName()), archive); + return { + "", relativeName, ToQString(realOrigin.getName()), + index, fileName, true, ToQString(realOrigin.getName()), archive}; } -void GeneralConflictsTab::onOverwriteActivated(QTreeWidgetItem *item, int) +void GeneralConflictsTab::onOverwriteActivated(const QModelIndex& index) { - if (auto* ci=dynamic_cast(item)) { - const auto origin = ci->altOrigin(); - - if (!origin.isEmpty()) { - emit modOpen(origin); - } + auto* model = dynamic_cast(ui->overwriteTree->model()); + if (!model) { + return; } -} -void GeneralConflictsTab::onOverwrittenActivated(QTreeWidgetItem *item, int) -{ - if (const auto* ci=dynamic_cast(item)) { - const auto origin = ci->altOrigin(); + auto* item = model->getItem(static_cast(index.row())); + if (!item) { + return; + } - if (!origin.isEmpty()) { - emit modOpen(origin); - } + const auto origin = item->altOrigin(); + if (!origin.isEmpty()) { + emit modOpen(origin); } } - -class AdvancedListModel : public QAbstractItemModel +void GeneralConflictsTab::onOverwrittenActivated(const QModelIndex& index) { -public: - struct Item - { - QString before, relativeName, after; - FileEntry::Index index; - QString fileName; - bool hasAltOrigins; - QString altOrigin; - bool archive; - }; - - void clear() - { - m_items.clear(); - } - - void reserve(std::size_t s) - { - m_items.reserve(s); - } - - QModelIndex index(int row, int col, const QModelIndex& ={}) const override - { - return createIndex(row, col); - } - - QModelIndex parent(const QModelIndex&) const override - { - return {}; - } - - int rowCount(const QModelIndex& parent={}) const override - { - if (parent.isValid()) { - return 0; - } - - return static_cast(m_items.size()); - } - - int columnCount(const QModelIndex& ={}) const override - { - return 3; - } - - QVariant data(const QModelIndex& index, int role) const override - { - if (role == Qt::DisplayRole) { - const auto row = index.row(); - if (row < 0) { - return {}; - } - - const auto i = static_cast(row); - if (i >= m_items.size()) { - return {}; - } - - const auto& item = m_items[i]; - - if (index.column() == 0) { - return item.before; - } else if (index.column() == 1) { - return item.relativeName; - } else if (index.column() == 2) { - return item.after; - } - } - - return {}; - } - - QVariant headerData(int col, Qt::Orientation, int role) const - { - if (role == Qt::DisplayRole) { - if (col == 0) { - return tr("Overwrites"); - } else if (col == 1) { - return tr("File"); - } else if (col == 2) { - return tr("Overwritten by"); - } - } - - return {}; - } - - void sort(int col, Qt::SortOrder order=Qt::AscendingOrder) - { - // avoids branching on column/sort order while sorting - auto sortBeforeAsc = [](const auto& a, const auto& b) { - return (naturalCompare(a.before, b.before) < 0); - }; - - auto sortBeforeDesc = [](const auto& a, const auto& b) { - return (naturalCompare(a.before, b.before) > 0); - }; - - auto sortFileAsc = [](const auto& a, const auto& b) { - return (naturalCompare(a.relativeName, b.relativeName) < 0); - }; - - auto sortFileDesc = [](const auto& a, const auto& b) { - return (naturalCompare(a.relativeName, b.relativeName) > 0); - }; - - auto sortAfterAsc = [](const auto& a, const auto& b) { - return (naturalCompare(a.after, b.after) < 0); - }; - - auto sortAfterDesc = [](const auto& a, const auto& b) { - return (naturalCompare(a.after, b.after) > 0); - }; - - if (col == 0) { - if (order == Qt::AscendingOrder) { - std::sort(m_items.begin(), m_items.end(), sortBeforeAsc); - } else { - std::sort(m_items.begin(), m_items.end(), sortBeforeDesc); - } - } else if (col == 1) { - if (order == Qt::AscendingOrder) { - std::sort(m_items.begin(), m_items.end(), sortFileAsc); - } else { - std::sort(m_items.begin(), m_items.end(), sortFileDesc); - } - } else if (col == 2) { - if (order == Qt::AscendingOrder) { - std::sort(m_items.begin(), m_items.end(), sortAfterAsc); - } else { - std::sort(m_items.begin(), m_items.end(), sortAfterDesc); - } - } - - emit layoutChanged({}, QAbstractItemModel::VerticalSortHint); + auto* model = dynamic_cast(ui->overwrittenTree->model()); + if (!model) { + return; } - void add(Item item) - { - m_items.emplace_back(std::move(item)); + auto* item = model->getItem(static_cast(index.row())); + if (!item) { + return; } - void finished() - { - emit dataChanged(index(0, 0), index(0, rowCount())); + const auto origin = item->altOrigin(); + if (!origin.isEmpty()) { + emit modOpen(origin); } - -private: - std::vector m_items; -}; +} AdvancedConflictsTab::AdvancedConflictsTab( - ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) - : m_tab(tab), ui(pui), m_core(oc), m_model(new AdvancedListModel) + ConflictsTab* tab, Ui::ModInfoDialog* pui, OrganizerCore& oc) : + m_tab(tab), ui(pui), m_core(oc), + m_model(new AdvancedConflictListModel(ui->conflictsAdvancedList)) { - ui->conflictsAdvancedList->setModel(m_model); - // left-elide the overwrites column so that the nearest are visible ui->conflictsAdvancedList->setItemDelegateForColumn( 0, new ElideLeftDelegate(ui->conflictsAdvancedList)); @@ -868,9 +993,9 @@ AdvancedConflictsTab::AdvancedConflictsTab( ui->conflictsAdvancedShowNearest, &QRadioButton::clicked, [&]{ update(); }); - //QObject::connect( - // ui->conflictsAdvancedList, &QTreeView::customContextMenuRequested, - // [&](const QPoint& p){ m_tab->showContextMenu(p, ui->conflictsAdvancedList); }); + QObject::connect( + ui->conflictsAdvancedList, &QTreeView::customContextMenuRequested, + [&](const QPoint& p){ m_tab->showContextMenu(p, ui->conflictsAdvancedList); }); m_filter.set(ui->conflictsAdvancedFilter); m_filter.changed = [&]{ update(); }; @@ -938,16 +1063,20 @@ void AdvancedConflictsTab::update() const int fileOrigin = file->getOrigin(archive); const auto& alternatives = file->getAlternatives(); - addItem( + auto item = createItem( file->getIndex(), fileOrigin, archive, fileName, relativeName, alternatives); + + if (item) { + m_model->add(std::move(*item)); + } } m_model->finished(); } } -void AdvancedConflictsTab::addItem( +std::optional AdvancedConflictsTab::createItem( FileEntry::Index index, int fileOrigin, bool archive, const QString& fileName, const QString& relativeName, const MOShared::FileEntry::AlternativesVector& alternatives) @@ -1040,7 +1169,7 @@ void AdvancedConflictsTab::addItem( // if both before and after are empty, it means this file has no conflicts // at all, only display it if the user wants it if (!hasAlts) { - return; + return {}; } } @@ -1052,9 +1181,8 @@ void AdvancedConflictsTab::addItem( }); if (!matched) { - return; + return {}; } - m_model->add( - {before, relativeName, after, index, fileName, hasAlts, "", archive}); + return ConflictItem{before, relativeName, after, index, fileName, hasAlts, "", archive}; } diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index f499d1a4..f0c0c589 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -6,10 +6,12 @@ #include "filterwidget.h" #include "directoryentry.h" #include +#include class ConflictsTab; class OrganizerCore; class ConflictItem; +class ConflictListModel; class GeneralConflictsTab : public QObject { @@ -39,21 +41,25 @@ private: OrganizerCore& m_core; Expanders m_expanders; - QTreeWidgetItem* createOverwriteItem( + ConflictListModel* m_overwriteModel; + ConflictListModel* m_overwrittenModel; + ConflictListModel* m_noConflictModel; + + ConflictItem createOverwriteItem( MOShared::FileEntry::Index index, bool archive, const QString& fileName, const QString& relativeName, const MOShared::FileEntry::AlternativesVector& alternatives); - QTreeWidgetItem* createNoConflictItem( + ConflictItem createNoConflictItem( MOShared::FileEntry::Index index, bool archive, const QString& fileName, const QString& relativeName); - QTreeWidgetItem* createOverwrittenItem( + ConflictItem createOverwrittenItem( MOShared::FileEntry::Index index, int fileOrigin, bool archive, const QString& fileName, const QString& relativeName); - void onOverwriteActivated(QTreeWidgetItem* item, int); - void onOverwrittenActivated(QTreeWidgetItem *item, int); + void onOverwriteActivated(const QModelIndex& index); + void onOverwrittenActivated(const QModelIndex& index); void onOverwriteTreeContext(const QPoint &pos); void onOverwrittenTreeContext(const QPoint &pos); @@ -61,8 +67,6 @@ private: }; -class AdvancedListModel; - class AdvancedConflictsTab : public QObject { Q_OBJECT; @@ -85,9 +89,9 @@ private: Ui::ModInfoDialog* ui; OrganizerCore& m_core; FilterWidget m_filter; - AdvancedListModel* m_model; + ConflictListModel* m_model; - void addItem( + std::optional createItem( MOShared::FileEntry::Index index, int fileOrigin, bool archive, const QString& fileName, const QString& relativeName, const MOShared::FileEntry::AlternativesVector& alternatives); @@ -109,13 +113,12 @@ public: void restoreState(const Settings& s) override; bool canHandleUnmanaged() const override; - void openItems(const QList& items); - void previewItems(const QList& items); + void openItems(QTreeView* tree); + void previewItems(QTreeView* tree); - void changeItemsVisibility( - const QList& items, bool visible); + void changeItemsVisibility(QTreeView* tree, bool visible); - void showContextMenu(const QPoint &pos, QTreeWidget* tree); + void showContextMenu(const QPoint &pos, QTreeView* tree); private: struct Actions @@ -131,10 +134,8 @@ private: GeneralConflictsTab m_general; AdvancedConflictsTab m_advanced; - Actions createMenuActions(const QList& selection); - - std::vector createGotoActions( - const QList& selection); + Actions createMenuActions(QTreeView* tree); + std::vector createGotoActions(const ConflictItem* item); }; #endif // MODINFODIALOGCONFLICTS_H -- cgit v1.3.1