From 8a88421fd9748f64163f18d8b89ea9d651402014 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 30 Dec 2020 22:56:34 +0100 Subject: Start moving modlist context menu actions to separate structures. --- src/modlistviewactions.cpp | 339 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 src/modlistviewactions.cpp (limited to 'src/modlistviewactions.cpp') diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp new file mode 100644 index 00000000..8b234b81 --- /dev/null +++ b/src/modlistviewactions.cpp @@ -0,0 +1,339 @@ +#include "modlistviewactions.h" + +#include +#include +#include +#include + +#include + +#include "categories.h" +#include "filedialogmemory.h" +#include "filterlist.h" +#include "modlist.h" +#include "modlistview.h" +#include "nexusinterface.h" +#include "nxmaccessmanager.h" +#include "savetextasdialog.h" +#include "organizercore.h" +#include "csvbuilder.h" + +using namespace MOBase; + +ModListViewActions::ModListViewActions( + OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, QObject* nxmReceiver, ModListView* view) : + QObject(view) + , m_core(core) + , m_filters(filters) + , m_categories(categoryFactory) + , m_receiver(nxmReceiver) + , m_view(view) +{ + +} + +void ModListViewActions::installMod(const QString& archivePath) const +{ + try { + QString path = archivePath; + if (path.isEmpty()) { + QStringList extensions = m_core.installationManager()->getSupportedExtensions(); + for (auto iter = extensions.begin(); iter != extensions.end(); ++iter) { + *iter = "*." + *iter; + } + + path = FileDialogMemory::getOpenFileName("installMod", m_view, tr("Choose Mod"), QString(), + tr("Mod Archive").append(QString(" (%1)").arg(extensions.join(" ")))); + } + + if (path.isEmpty()) { + return; + } + else { + m_core.installMod(path, false, nullptr, QString()); + } + } + catch (const std::exception& e) { + reportError(e.what()); + } +} + +void ModListViewActions::createEmptyMod(int modIndex) const +{ + GuessedValue name; + name.setFilter(&fixDirectoryName); + + while (name->isEmpty()) { + bool ok; + name.update(QInputDialog::getText(m_view, tr("Create Mod..."), + tr("This will create an empty mod.\n" + "Please enter a name:"), QLineEdit::Normal, "", &ok), + GUESS_USER); + if (!ok) { + return; + } + } + + if (m_core.modList()->getMod(name) != nullptr) { + reportError(tr("A mod with this name already exists")); + return; + } + + int newPriority = -1; + if (modIndex >= 0 && m_view->sortColumn() == ModList::COL_PRIORITY) { + newPriority = m_core.currentProfile()->getModPriority(modIndex); + } + + IModInterface* newMod = m_core.createMod(name); + if (newMod == nullptr) { + return; + } + + m_core.refresh(); + + if (newPriority >= 0) { + m_core.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); + } +} + +void ModListViewActions::createSeparator(int modIndex) const +{ + GuessedValue name; + name.setFilter(&fixDirectoryName); + while (name->isEmpty()) + { + bool ok; + name.update(QInputDialog::getText(m_view, tr("Create Separator..."), + tr("This will create a new separator.\n" + "Please enter a name:"), QLineEdit::Normal, "", &ok), + GUESS_USER); + if (!ok) { return; } + } + if (m_core.modList()->getMod(name) != nullptr) + { + reportError(tr("A separator with this name already exists")); + return; + } + name->append("_separator"); + if (m_core.modList()->getMod(name) != nullptr) + { + return; + } + + int newPriority = -1; + if (modIndex >= 0 && m_view->sortColumn() == ModList::COL_PRIORITY) + { + newPriority = m_core.currentProfile()->getModPriority(modIndex); + } + + if (m_core.createMod(name) == nullptr) { return; } + m_core.refresh(); + + if (newPriority >= 0) + { + m_core.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); + } + + if (auto c = m_core.settings().colors().previousSeparatorColor()) { + ModInfo::getByIndex(ModInfo::getIndex(name))->setColor(*c); + } +} + +void ModListViewActions::checkModsForUpdates() const +{ + bool checkingModsForUpdate = false; + if (NexusInterface::instance().getAccessManager()->validated()) { + checkingModsForUpdate = ModInfo::checkAllForUpdate(&m_core.pluginContainer(), m_receiver); + NexusInterface::instance().requestEndorsementInfo(m_receiver, QVariant(), QString()); + NexusInterface::instance().requestTrackingInfo(m_receiver, QVariant(), QString()); + } else { + QString apiKey; + if (GlobalSettings::nexusApiKey(apiKey)) { + m_core.doAfterLogin([=] () { checkModsForUpdates(); }); + NexusInterface::instance().getAccessManager()->apiCheck(apiKey); + } else { + log::warn("{}", tr("You are not currently authenticated with Nexus. Please do so under Settings -> Nexus.")); + } + } + + bool updatesAvailable = false; + for (auto mod : m_core.modList()->allMods()) { + ModInfo::Ptr modInfo = ModInfo::getByName(mod); + if (modInfo->updateAvailable()) { + updatesAvailable = true; + break; + } + } + + if (updatesAvailable || checkingModsForUpdate) { + m_view->setFilterCriteria({{ + ModListSortProxy::TypeSpecial, + CategoryFactory::UpdateAvailable, + false} + }); + + m_filters.setSelection({{ + ModListSortProxy::TypeSpecial, + CategoryFactory::UpdateAvailable, + false + }}); + } +} + +void ModListViewActions::exportModListCSV() const +{ + QDialog selection(m_view); + QGridLayout* grid = new QGridLayout; + selection.setWindowTitle(tr("Export to csv")); + + QLabel* csvDescription = new QLabel(); + csvDescription->setText(tr("CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet.\nYou can also use online editors and converters instead.")); + grid->addWidget(csvDescription); + + QGroupBox* groupBoxRows = new QGroupBox(tr("Select what mods you want export:")); + QRadioButton* all = new QRadioButton(tr("All installed mods")); + QRadioButton* active = new QRadioButton(tr("Only active (checked) mods from your current profile")); + QRadioButton* visible = new QRadioButton(tr("All currently visible mods in the mod list")); + + QVBoxLayout* vbox = new QVBoxLayout; + vbox->addWidget(all); + vbox->addWidget(active); + vbox->addWidget(visible); + vbox->addStretch(1); + groupBoxRows->setLayout(vbox); + + grid->addWidget(groupBoxRows); + + QButtonGroup* buttonGroupRows = new QButtonGroup(); + buttonGroupRows->addButton(all, 0); + buttonGroupRows->addButton(active, 1); + buttonGroupRows->addButton(visible, 2); + buttonGroupRows->button(0)->setChecked(true); + + QGroupBox* groupBoxColumns = new QGroupBox(tr("Choose what Columns to export:")); + groupBoxColumns->setFlat(true); + + QCheckBox* mod_Priority = new QCheckBox(tr("Mod_Priority")); + mod_Priority->setChecked(true); + QCheckBox* mod_Name = new QCheckBox(tr("Mod_Name")); + mod_Name->setChecked(true); + QCheckBox* mod_Note = new QCheckBox(tr("Notes_column")); + QCheckBox* mod_Status = new QCheckBox(tr("Mod_Status")); + mod_Status->setChecked(true); + QCheckBox* primary_Category = new QCheckBox(tr("Primary_Category")); + QCheckBox* nexus_ID = new QCheckBox(tr("Nexus_ID")); + QCheckBox* mod_Nexus_URL = new QCheckBox(tr("Mod_Nexus_URL")); + QCheckBox* mod_Version = new QCheckBox(tr("Mod_Version")); + QCheckBox* install_Date = new QCheckBox(tr("Install_Date")); + QCheckBox* download_File_Name = new QCheckBox(tr("Download_File_Name")); + + QVBoxLayout* vbox1 = new QVBoxLayout; + vbox1->addWidget(mod_Priority); + vbox1->addWidget(mod_Name); + vbox1->addWidget(mod_Status); + vbox1->addWidget(mod_Note); + vbox1->addWidget(primary_Category); + vbox1->addWidget(nexus_ID); + vbox1->addWidget(mod_Nexus_URL); + vbox1->addWidget(mod_Version); + vbox1->addWidget(install_Date); + vbox1->addWidget(download_File_Name); + groupBoxColumns->setLayout(vbox1); + + grid->addWidget(groupBoxColumns); + + QPushButton* ok = new QPushButton("Ok"); + QPushButton* cancel = new QPushButton("Cancel"); + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + connect(buttons, SIGNAL(accepted()), &selection, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), &selection, SLOT(reject())); + + grid->addWidget(buttons); + + selection.setLayout(grid); + + + if (selection.exec() == QDialog::Accepted) { + + unsigned int numMods = ModInfo::getNumMods(); + int selectedRowID = buttonGroupRows->checkedId(); + + try { + QBuffer buffer; + buffer.open(QIODevice::ReadWrite); + CSVBuilder builder(&buffer); + builder.setEscapeMode(CSVBuilder::TYPE_STRING, CSVBuilder::QUOTE_ALWAYS); + std::vector > fields; + if (mod_Priority->isChecked()) + fields.push_back(std::make_pair(QString("#Mod_Priority"), CSVBuilder::TYPE_STRING)); + if (mod_Status->isChecked()) + fields.push_back(std::make_pair(QString("#Mod_Status"), CSVBuilder::TYPE_STRING)); + if (mod_Name->isChecked()) + fields.push_back(std::make_pair(QString("#Mod_Name"), CSVBuilder::TYPE_STRING)); + if (mod_Note->isChecked()) + fields.push_back(std::make_pair(QString("#Note"), CSVBuilder::TYPE_STRING)); + if (primary_Category->isChecked()) + fields.push_back(std::make_pair(QString("#Primary_Category"), CSVBuilder::TYPE_STRING)); + if (nexus_ID->isChecked()) + fields.push_back(std::make_pair(QString("#Nexus_ID"), CSVBuilder::TYPE_INTEGER)); + if (mod_Nexus_URL->isChecked()) + fields.push_back(std::make_pair(QString("#Mod_Nexus_URL"), CSVBuilder::TYPE_STRING)); + if (mod_Version->isChecked()) + fields.push_back(std::make_pair(QString("#Mod_Version"), CSVBuilder::TYPE_STRING)); + if (install_Date->isChecked()) + fields.push_back(std::make_pair(QString("#Install_Date"), CSVBuilder::TYPE_STRING)); + if (download_File_Name->isChecked()) + fields.push_back(std::make_pair(QString("#Download_File_Name"), CSVBuilder::TYPE_STRING)); + + builder.setFields(fields); + + builder.writeHeader(); + + auto indexesByPriority = m_core.currentProfile()->getAllIndexesByPriority(); + for (auto& iter : indexesByPriority) { + ModInfo::Ptr info = ModInfo::getByIndex(iter.second); + bool enabled = m_core.currentProfile()->modEnabled(iter.second); + if ((selectedRowID == 1) && !enabled) { + continue; + } + else if ((selectedRowID == 2) && !m_view->isModVisible(iter.second)) { + continue; + } + std::vector flags = info->getFlags(); + if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) == flags.end()) && + (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end())) { + if (mod_Priority->isChecked()) + builder.setRowField("#Mod_Priority", QString("%1").arg(iter.first, 4, 10, QChar('0'))); + if (mod_Status->isChecked()) + builder.setRowField("#Mod_Status", (enabled) ? "+" : "-"); + if (mod_Name->isChecked()) + builder.setRowField("#Mod_Name", info->name()); + if (mod_Note->isChecked()) + builder.setRowField("#Note", QString("%1").arg(info->comments().remove(','))); + if (primary_Category->isChecked()) + builder.setRowField("#Primary_Category", (m_categories.categoryExists(info->primaryCategory())) ? m_categories.getCategoryNameByID(info->primaryCategory()) : ""); + if (nexus_ID->isChecked()) + builder.setRowField("#Nexus_ID", info->nexusId()); + if (mod_Nexus_URL->isChecked()) + builder.setRowField("#Mod_Nexus_URL", (info->nexusId() > 0) ? NexusInterface::instance().getModURL(info->nexusId(), info->gameName()) : ""); + if (mod_Version->isChecked()) + builder.setRowField("#Mod_Version", info->version().canonicalString()); + if (install_Date->isChecked()) + builder.setRowField("#Install_Date", info->creationTime().toString("yyyy/MM/dd HH:mm:ss")); + if (download_File_Name->isChecked()) + builder.setRowField("#Download_File_Name", info->installationFile()); + + builder.writeRow(); + } + } + + SaveTextAsDialog saveDialog(m_view); + saveDialog.setText(buffer.data()); + saveDialog.exec(); + } + catch (const std::exception& e) { + reportError(tr("export failed: %1").arg(e.what())); + } + } +} -- cgit v1.3.1 From e8c2d9cd29967be928b8649fd580a3be9cae3684 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Wed, 30 Dec 2020 23:35:44 +0100 Subject: More context menu stuff moved. --- src/mainwindow.cpp | 112 ++----------------- src/mainwindow.h | 7 -- src/modlistcontextmenu.cpp | 264 ++++++++++----------------------------------- src/modlistcontextmenu.h | 18 +++- src/modlistview.h | 4 +- src/modlistviewactions.cpp | 103 +++++++++++++++++- src/modlistviewactions.h | 19 +++- 7 files changed, 192 insertions(+), 335 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 58061c96..265cc5c2 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2629,70 +2629,9 @@ void MainWindow::overwriteClosed(int) m_OrganizerCore.refreshDirectoryStructure(); } - -void MainWindow::displayModInformation( - ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) +void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) { - if (!m_OrganizerCore.modList()->modInfoAboutToChange(modInfo)) { - log::debug("A different mod information dialog is open. If this is incorrect, please restart MO"); - return; - } - std::vector flags = modInfo->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { - QDialog *dialog = this->findChild("__overwriteDialog"); - try { - if (dialog == nullptr) { - dialog = new OverwriteInfoDialog(modInfo, this); - dialog->setObjectName("__overwriteDialog"); - } else { - qobject_cast(dialog)->setModInfo(modInfo); - } - - dialog->show(); - dialog->raise(); - dialog->activateWindow(); - connect(dialog, SIGNAL(finished(int)), this, SLOT(overwriteClosed(int))); - } catch (const std::exception &e) { - reportError(tr("Failed to display overwrite dialog: %1").arg(e.what())); - } - } else { - modInfo->saveMeta(); - - ModInfoDialog dialog(this, &m_OrganizerCore, &m_PluginContainer, modInfo); - connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); - - //Open the tab first if we want to use the standard indexes of the tabs. - if (tabID != ModInfoTabIDs::None) { - dialog.selectTab(tabID); - } - - dialog.exec(); - - modInfo->saveMeta(); - emit modInfoDisplayed(); - m_OrganizerCore.modList()->modInfoChanged(modInfo); - } - - if (m_OrganizerCore.currentProfile()->modEnabled(modIndex) - && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { - FilesOrigin& origin = m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(modInfo->name())); - origin.enable(false); - - if (m_OrganizerCore.directoryStructure()->originExists(ToWString(modInfo->name()))) { - FilesOrigin& origin = m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(modInfo->name())); - origin.enable(false); - - m_OrganizerCore.directoryRefresher()->addModToStructure(m_OrganizerCore.directoryStructure() - , modInfo->name() - , m_OrganizerCore.currentProfile()->getModPriority(modIndex) - , modInfo->absolutePath() - , modInfo->stealFiles() - , modInfo->archives()); - DirectoryRefresher::cleanStructure(m_OrganizerCore.directoryStructure()); - m_OrganizerCore.directoryStructure()->getFileRegister()->sortOrigins(); - m_OrganizerCore.refreshLists(); - } - } + ui->modList->actions().displayModInformation(modInfo, modIndex, tabID); } bool MainWindow::closeWindow() @@ -2723,26 +2662,6 @@ ModInfo::Ptr MainWindow::previousModInList(int modIndex) return ModInfo::getByIndex(modIndex); } -void MainWindow::displayModInformation(const QString &modName, ModInfoTabIDs tabID) -{ - unsigned int index = ModInfo::getIndex(modName); - if (index == UINT_MAX) { - log::error("failed to resolve mod name {}", modName); - return; - } - - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - displayModInformation(modInfo, index, tabID); -} - - -void MainWindow::displayModInformation(int row, ModInfoTabIDs tabID) -{ - ModInfo::Ptr modInfo = ModInfo::getByIndex(row); - displayModInformation(modInfo, row, tabID); -} - - void MainWindow::ignoreMissingData_clicked(int modIndex) { const auto rows = ui->modList->selectionModel()->selectedRows(); @@ -3141,7 +3060,7 @@ void MainWindow::updatePluginCount() void MainWindow::information_clicked(int modIndex) { try { - displayModInformation(modIndex); + ui->modList->actions().displayModInformation(modIndex); } catch (const std::exception &e) { reportError(e.what()); } @@ -3388,7 +3307,7 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index) case ModList::COL_CONFLICTFLAGS: tab = ModInfoTabIDs::Conflicts; break; } - displayModInformation(modIndex, tab); + ui->modList->actions().displayModInformation(modIndex, tab); // workaround to cancel the editor that might have opened because of // selection-click ui->modList->closePersistentEditor(index); @@ -3424,7 +3343,7 @@ void MainWindow::openOriginInformation_clicked() std::vector flags = modInfo->getFlags(); if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { - displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + ui->modList->actions().displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); } } catch (const std::exception &e) { @@ -3473,7 +3392,7 @@ void MainWindow::on_espList_doubleClicked(const QModelIndex &index) } else { - displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + ui->modList->actions().displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); // workaround to cancel the editor that might have opened because of // selection-click ui->espList->closePersistentEditor(index); @@ -3950,27 +3869,16 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) QTreeView *modList = findChild("modList"); QModelIndex contextIdx = mapToModel(m_OrganizerCore.modList(), ui->modList->indexAt(pos)); - int modIndex = ui->modList->indexAt(pos).data(ModList::IndexRole).toInt(); - - int contextColumn = contextIdx.column(); - if (modIndex == -1) { + if (!contextIdx.isValid()) { // no selection ModListGlobalContextMenu(m_OrganizerCore, ui->modList).exec(modList->viewport()->mapToGlobal(pos)); } else { - QMenu menu(this); - - QMenu *allMods = new ModListGlobalContextMenu(m_OrganizerCore, ui->modList, this); - allMods->setTitle(tr("All Mods")); - menu.addMenu(allMods); - - if (ui->modList->hasCollapsibleSeparators()) { - menu.addAction(tr("Collapse all"), ui->modList, &QTreeView::collapseAll); - menu.addAction(tr("Expand all"), ui->modList, &QTreeView::expandAll); - } + int modIndex = ui->modList->indexAt(pos).data(ModList::IndexRole).toInt(); + int contextColumn = contextIdx.column(); - menu.addSeparator(); + ModListContextMenu menu(m_OrganizerCore, contextIdx, ui->modList); ModInfo::Ptr info = ModInfo::getByIndex(modIndex); std::vector flags = info->getFlags(); diff --git a/src/mainwindow.h b/src/mainwindow.h index a49c12fe..57f26220 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -153,11 +153,6 @@ public slots: signals: - /** - * @brief emitted after the information dialog has been closed - */ - void modInfoDisplayed(); - /** * @brief emitted when the selected style changes */ @@ -209,7 +204,6 @@ private: void refreshExecutablesList(); bool modifyExecutablesDialog(int selection); - void displayModInformation(int row, ModInfoTabIDs tab=ModInfoTabIDs::None); /** * Sets category selections from menu; for multiple mods, this will only apply @@ -455,7 +449,6 @@ private slots: void onFiltersOptions( ModListSortProxy::FilterMode mode, ModListSortProxy::SeparatorsMode sep); - void displayModInformation(const QString &modName, ModInfoTabIDs tabID); void visitNexusOrWebPage(const QModelIndex& idx); void modRenamed(const QString &oldName, const QString &newName); diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 5b45a217..41031eaa 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -41,233 +41,77 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV addAction(tr("Export to csv..."), [=]() { view->actions().exportModListCSV(); }); } -ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndexList& index, ModListView* modListView) : - QMenu(modListView) +ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* view) : + QMenu(view) , m_core(core) - , m_index(index) + , m_index() { - // TODO: Change this. - QModelIndex contextIdx = index.at(0); - int contextColumn = contextIdx.column(); - int modIndex = contextIdx.data(ModList::IndexRole).toInt(); - - try { - /* - if (modIndex == -1) { - // no selection - QMenu menu(this); - initModListContextMenu(&menu); - menu.exec(modList->viewport()->mapToGlobal(pos)); - } - else { - QMenu menu(this); - - QMenu* allMods = new QMenu(&menu); - initModListContextMenu(allMods); - allMods->setTitle(tr("All Mods")); - menu.addMenu(allMods); - - if (ui->modList->hasCollapsibleSeparators()) { - menu.addAction(tr("Collapse all"), ui->modList, &QTreeView::collapseAll); - menu.addAction(tr("Expand all"), ui->modList, &QTreeView::expandAll); - } - - menu.addSeparator(); - - ModInfo::Ptr info = ModInfo::getByIndex(modIndex); - std::vector flags = info->getFlags(); - - // context menu for overwrites - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { - if (QDir(info->absolutePath()).count() > 2) { - menu.addAction(tr("Sync to Mods..."), [=]() { m_OrganizerCore.syncOverwrite(); }); - menu.addAction(tr("Create Mod..."), [=]() { createModFromOverwrite(); }); - menu.addAction(tr("Move content to Mod..."), [=]() { moveOverwriteContentToExistingMod(); }); - menu.addAction(tr("Clear Overwrite..."), [=]() { clearOverwrite(); }); - } - menu.addAction(tr("Open in Explorer"), [=]() { openExplorer_clicked(modIndex); }); - } - - // context menu for mod backups - else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) { - menu.addAction(tr("Restore Backup"), [=]() { restoreBackup_clicked(modIndex); }); - menu.addAction(tr("Remove Backup..."), [=]() { removeMod_clicked(modIndex); }); - menu.addSeparator(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { - menu.addAction(tr("Ignore missing data"), [=]() { ignoreMissingData_clicked(modIndex); }); - } - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { - menu.addAction(tr("Mark as converted/working"), [=]() { markConverted_clicked(modIndex); }); - } - menu.addSeparator(); - if (info->nexusId() > 0) { - menu.addAction(tr("Visit on Nexus"), [=]() { visitOnNexus_clicked(modIndex); }); - } - - const auto url = info->parseCustomURL(); - if (url.isValid()) { - menu.addAction(tr("Visit on %1").arg(url.host()), [=]() { visitWebPage_clicked(modIndex); }); - } - - menu.addAction(tr("Open in Explorer"), [=]() { openExplorer_clicked(modIndex); }); - } - - // separator - else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) { - menu.addSeparator(); - QMenu* addRemoveCategoriesMenu = new QMenu(tr("Change Categories"), &menu); - populateMenuCategories(modIndex, addRemoveCategoriesMenu, 0); - connect(addRemoveCategoriesMenu, &QMenu::aboutToHide, [=]() { addRemoveCategories_MenuHandler(addRemoveCategoriesMenu, modIndex, contextIdx); }); - addMenuAsPushButton(&menu, addRemoveCategoriesMenu); - QMenu* primaryCategoryMenu = new QMenu(tr("Primary Category"), &menu); - connect(primaryCategoryMenu, &QMenu::aboutToShow, [=]() { setPrimaryCategoryCandidates(primaryCategoryMenu, info); }); - addMenuAsPushButton(&menu, primaryCategoryMenu); - menu.addSeparator(); - menu.addAction(tr("Rename Separator..."), [=]() { renameMod_clicked(); }); - menu.addAction(tr("Remove Separator..."), [=]() { removeMod_clicked(modIndex); }); - menu.addSeparator(); - addModSendToContextMenu(&menu); - menu.addAction(tr("Select Color..."), [=]() { setColor_clicked(modIndex); }); - - if (info->color().isValid()) { - menu.addAction(tr("Reset Color"), [=]() { resetColor_clicked(modIndex); }); - } - - menu.addSeparator(); - } - - // foregin - else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - addModSendToContextMenu(&menu); - } - - // regular - else { - QMenu* addRemoveCategoriesMenu = new QMenu(tr("Change Categories"), &menu); - populateMenuCategories(modIndex, addRemoveCategoriesMenu, 0); - connect(addRemoveCategoriesMenu, &QMenu::aboutToHide, [=]() { addRemoveCategories_MenuHandler(addRemoveCategoriesMenu, modIndex, contextIdx); }); - addMenuAsPushButton(&menu, addRemoveCategoriesMenu); - - QMenu* primaryCategoryMenu = new QMenu(tr("Primary Category"), &menu); - connect(primaryCategoryMenu, &QMenu::aboutToShow, [=]() { setPrimaryCategoryCandidates(primaryCategoryMenu, info); }); - addMenuAsPushButton(&menu, primaryCategoryMenu); - - menu.addSeparator(); - - if (info->downgradeAvailable()) { - menu.addAction(tr("Change versioning scheme"), [=]() { changeVersioningScheme(modIndex); }); - } - - if (info->nexusId() > 0) - menu.addAction(tr("Force-check updates"), [=]() { checkModUpdates_clicked(modIndex); }); - if (info->updateIgnored()) { - menu.addAction(tr("Un-ignore update"), [=]() { unignoreUpdate(modIndex); }); - } - else { - if (info->updateAvailable() || info->downgradeAvailable()) { - menu.addAction(tr("Ignore update"), [=]() { ignoreUpdate(modIndex); }); - } - } - menu.addSeparator(); + if (view->selectionModel()->hasSelection()) { + m_index = view->indexViewToModel(view->selectionModel()->selectedRows()); + } + else { + m_index = { index }; + } - menu.addAction(tr("Enable selected"), [=]() { enableSelectedMods_clicked(); }); - menu.addAction(tr("Disable selected"), [=]() { disableSelectedMods_clicked(); }); - menu.addSeparator(); + QMenu* allMods = new ModListGlobalContextMenu(core, view, view); + allMods->setTitle(tr("All Mods")); + addMenu(allMods); - addModSendToContextMenu(&menu); + if (view->hasCollapsibleSeparators()) { + addAction(tr("Collapse all"), view, &QTreeView::collapseAll); + addAction(tr("Expand all"), view, &QTreeView::expandAll); + } - menu.addAction(tr("Rename Mod..."), [=]() { renameMod_clicked(); }); - menu.addAction(tr("Reinstall Mod"), [=]() { reinstallMod_clicked(modIndex); }); - menu.addAction(tr("Remove Mod..."), [=]() { removeMod_clicked(modIndex); }); - menu.addAction(tr("Create Backup"), [=]() { backupMod_clicked(modIndex); }); + addSeparator(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_HIDDEN_FILES) != flags.end()) { - menu.addAction(tr("Restore hidden files"), [=]() { restoreHiddenFiles_clicked(modIndex); }); - } + // Add type-specific items + ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); - menu.addSeparator(); + if (info->isOverwrite()) { + addOverwriteActions(core, view); + } + else if (info->isBackup()) { + addBackupActions(core, view); + } + else if (info->isSeparator()) { + addSeparatorActions(core, view); + } + else if (info->isForeign()) { + addForeignActions(core, view); + } + else { + addRegularActions(core, view); + } - if (contextColumn == ModList::COL_NOTES) { - menu.addAction(tr("Select Color..."), [=]() { setColor_clicked(modIndex); }); - if (info->color().isValid()) { - menu.addAction(tr("Reset Color"), [=]() { resetColor_clicked(modIndex); }); - } - menu.addSeparator(); - } + // add information for all except foreign + if (!info->isForeign()) { + QAction* infoAction = addAction(tr("Information..."), [=]() { view->actions().displayModInformation(m_index[0].row()); }); + setDefaultAction(infoAction); + } +} - if (info->nexusId() > 0 && Settings::instance().nexus().endorsementIntegration()) { - switch (info->endorsedState()) { - case EndorsedState::ENDORSED_TRUE: { - menu.addAction(tr("Un-Endorse"), [=]() { unendorse_clicked(); }); - } break; - case EndorsedState::ENDORSED_FALSE: { - menu.addAction(tr("Endorse"), [=]() { endorse_clicked(); }); - menu.addAction(tr("Won't endorse"), [=]() { dontendorse_clicked(modIndex); }); - } break; - case EndorsedState::ENDORSED_NEVER: { - menu.addAction(tr("Endorse"), [=]() { endorse_clicked(); }); - } break; - default: { - QAction* action = new QAction(tr("Endorsement state unknown"), &menu); - action->setEnabled(false); - menu.addAction(action); - } break; - } - } +void ModListContextMenu::addOverwriteActions(OrganizerCore& core, ModListView* modListView) +{ - if (info->nexusId() > 0 && Settings::instance().nexus().trackedIntegration()) { - switch (info->trackedState()) { - case TrackedState::TRACKED_FALSE: { - menu.addAction(tr("Start tracking"), [=]() { track_clicked(); }); - } break; - case TrackedState::TRACKED_TRUE: { - menu.addAction(tr("Stop tracking"), [=]() { untrack_clicked(); }); - } break; - default: { - QAction* action = new QAction(tr("Tracked state unknown"), &menu); - action->setEnabled(false); - menu.addAction(action); - } break; - } - } +} - menu.addSeparator(); +void ModListContextMenu::addSeparatorActions(OrganizerCore& core, ModListView* modListView) +{ - std::vector flags = info->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { - menu.addAction(tr("Ignore missing data"), [=]() { ignoreMissingData_clicked(modIndex); }); - } +} - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { - menu.addAction(tr("Mark as converted/working"), [=]() { markConverted_clicked(modIndex); }); - } +void ModListContextMenu::addForeignActions(OrganizerCore& core, ModListView* modListView) +{ - menu.addSeparator(); +} - if (info->nexusId() > 0) { - menu.addAction(tr("Visit on Nexus"), [=]() { visitOnNexus_clicked(modIndex); }); - } +void ModListContextMenu::addBackupActions(OrganizerCore& core, ModListView* modListView) +{ - const auto url = info->parseCustomURL(); - if (url.isValid()) { - menu.addAction(tr("Visit on %1").arg(url.host()), [=]() { visitWebPage_clicked(modIndex); }); - } +} - menu.addAction(tr("Open in Explorer"), [&, modIndex]() { openExplorer_clicked(modIndex); }); - } +void ModListContextMenu::addRegularActions(OrganizerCore& core, ModListView* modListView) +{ - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end()) { - QAction* infoAction = menu.addAction(tr("Information..."), [=]() { information_clicked(modIndex); }); - menu.setDefaultAction(infoAction); - } - } - */ - } - catch (const std::exception& e) { - reportError(tr("Exception: ").arg(e.what())); - } - catch (...) { - reportError(tr("Unknown exception")); - } } diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index bc72fbdf..9d015afc 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -23,13 +23,21 @@ class ModListContextMenu : public QMenu { Q_OBJECT -private: +public: - friend class ModListView; + // creates a new context menu, the given index is the one for the click and should be valid + // + ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* modListView); + +private: - // creates a new context menu that will act on the given mod list - // index (those should be index from the modlist) - ModListContextMenu(OrganizerCore& core, const QModelIndexList& index, ModListView* modListView); + // add actions/menus to this menu for each type of mod + // + void addOverwriteActions(OrganizerCore& core, ModListView* modListView); + void addSeparatorActions(OrganizerCore& core, ModListView* modListView); + void addForeignActions(OrganizerCore& core, ModListView* modListView); + void addBackupActions(OrganizerCore& core, ModListView* modListView); + void addRegularActions(OrganizerCore& core, ModListView* modListView); OrganizerCore& m_core; QModelIndexList m_index; diff --git a/src/modlistview.h b/src/modlistview.h index d5eea54f..0f131631 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -105,8 +105,6 @@ public slots: // void updateModCount(); -protected: - // map from/to the view indexes to the model // QModelIndex indexModelToView(const QModelIndex& index) const; @@ -114,6 +112,8 @@ protected: QModelIndex indexViewToModel(const QModelIndex& index) const; QModelIndexList indexViewToModel(const QModelIndexList& index) const; +protected: + // returns the next/previous index of the given index // QModelIndex nextIndex(const QModelIndex& index) const; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 8b234b81..6faebc15 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -5,28 +5,37 @@ #include #include +#include #include #include "categories.h" #include "filedialogmemory.h" #include "filterlist.h" +#include "modinfodialog.h" #include "modlist.h" #include "modlistview.h" +#include "mainwindow.h" #include "nexusinterface.h" #include "nxmaccessmanager.h" #include "savetextasdialog.h" #include "organizercore.h" +#include "overwriteinfodialog.h" #include "csvbuilder.h" +#include "shared/filesorigin.h" +#include "shared/directoryentry.h" +#include "shared/fileregister.h" +#include "directoryrefresher.h" using namespace MOBase; +using namespace MOShared; ModListViewActions::ModListViewActions( - OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, QObject* nxmReceiver, ModListView* view) : + OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, MainWindow* mainWindow, ModListView* view) : QObject(view) , m_core(core) , m_filters(filters) , m_categories(categoryFactory) - , m_receiver(nxmReceiver) + , m_main(mainWindow) , m_view(view) { @@ -143,9 +152,9 @@ void ModListViewActions::checkModsForUpdates() const { bool checkingModsForUpdate = false; if (NexusInterface::instance().getAccessManager()->validated()) { - checkingModsForUpdate = ModInfo::checkAllForUpdate(&m_core.pluginContainer(), m_receiver); - NexusInterface::instance().requestEndorsementInfo(m_receiver, QVariant(), QString()); - NexusInterface::instance().requestTrackingInfo(m_receiver, QVariant(), QString()); + checkingModsForUpdate = ModInfo::checkAllForUpdate(&m_core.pluginContainer(), m_main); + NexusInterface::instance().requestEndorsementInfo(m_main, QVariant(), QString()); + NexusInterface::instance().requestTrackingInfo(m_main, QVariant(), QString()); } else { QString apiKey; if (GlobalSettings::nexusApiKey(apiKey)) { @@ -337,3 +346,87 @@ void ModListViewActions::exportModListCSV() const } } } + +void ModListViewActions::displayModInformation(const QString& modName, ModInfoTabIDs tab) const +{ + unsigned int index = ModInfo::getIndex(modName); + if (index == UINT_MAX) { + log::error("failed to resolve mod name {}", modName); + return; + } + + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + displayModInformation(modInfo, index, tab); +} + +void ModListViewActions::displayModInformation(unsigned int index, ModInfoTabIDs tab) const +{ + ModInfo::Ptr modInfo = ModInfo::getByIndex(index); + displayModInformation(modInfo, index, tab); +} + +void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tab) const +{ + if (!m_core.modList()->modInfoAboutToChange(modInfo)) { + log::debug("a different mod information dialog is open. If this is incorrect, please restart MO"); + return; + } + std::vector flags = modInfo->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { + QDialog* dialog = m_main->findChild("__overwriteDialog"); + try { + if (dialog == nullptr) { + dialog = new OverwriteInfoDialog(modInfo, m_main); + dialog->setObjectName("__overwriteDialog"); + } + else { + qobject_cast(dialog)->setModInfo(modInfo); + } + + dialog->show(); + dialog->raise(); + dialog->activateWindow(); + connect(dialog, SIGNAL(finished(int)), this, SLOT(overwriteClosed(int))); + } + catch (const std::exception& e) { + reportError(tr("Failed to display overwrite dialog: %1").arg(e.what())); + } + } + else { + modInfo->saveMeta(); + + ModInfoDialog dialog(m_main, &m_core, &m_core.pluginContainer(), modInfo); + connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); + + //Open the tab first if we want to use the standard indexes of the tabs. + if (tab != ModInfoTabIDs::None) { + dialog.selectTab(tab); + } + + dialog.exec(); + + modInfo->saveMeta(); + m_core.modList()->modInfoChanged(modInfo); + } + + if (m_core.currentProfile()->modEnabled(modIndex) + && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { + FilesOrigin& origin = m_core.directoryStructure()->getOriginByName(ToWString(modInfo->name())); + origin.enable(false); + + if (m_core.directoryStructure()->originExists(ToWString(modInfo->name()))) { + FilesOrigin& origin = m_core.directoryStructure()->getOriginByName(ToWString(modInfo->name())); + origin.enable(false); + + m_core.directoryRefresher()->addModToStructure(m_core.directoryStructure() + , modInfo->name() + , m_core.currentProfile()->getModPriority(modIndex) + , modInfo->absolutePath() + , modInfo->stealFiles() + , modInfo->archives()); + DirectoryRefresher::cleanStructure(m_core.directoryStructure()); + m_core.directoryStructure()->getFileRegister()->sortOrigins(); + m_core.refreshLists(); + } + } +} diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 49cffaef..b01b8e79 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -4,8 +4,12 @@ #include #include +#include "modinfo.h" +#include "modinfodialogfwd.h" + class CategoryFactory; class FilterList; +class MainWindow; class ModListView; class OrganizerCore; @@ -15,14 +19,14 @@ class ModListViewActions : public QObject public: - // the nxmReceiver is a (hopefully temporary) "hack" because it would require a lots of change - // to do otherwise since NXM is mostly based on the old Qt signal-slot system + // currently passing the main window itself because a lots of stuff needs it but + // it would be nice to avoid passing it at some point // ModListViewActions( OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, - QObject* nxmReceiver, + MainWindow* mainWindow, ModListView* view); // install the mod from the given archive @@ -43,12 +47,19 @@ public: // void exportModListCSV() const; + // display mod information + // + void displayModInformation(const QString& modName, ModInfoTabIDs tabID = ModInfoTabIDs::None) const; + void displayModInformation(unsigned int index, ModInfoTabIDs tab = ModInfoTabIDs::None) const; + void displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID = ModInfoTabIDs::None) const; + + private: OrganizerCore& m_core; FilterList& m_filters; CategoryFactory& m_categories; - QObject* m_receiver; // receiver for NXM signals (temporary) + MainWindow* m_main; ModListView* m_view; }; -- cgit v1.3.1 From fb52a129b3a878511cf754daed433d9a789689c8 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 00:06:12 +0100 Subject: More stuff moved to mod list context. --- src/mainwindow.cpp | 121 +++------------------------------------------ src/mainwindow.h | 7 --- src/modlist.cpp | 22 ++++++++- src/modlist.h | 6 ++- src/modlistcontextmenu.cpp | 39 ++++++++++----- src/modlistcontextmenu.h | 17 ++++--- src/modlistview.cpp | 2 +- src/modlistviewactions.cpp | 68 +++++++++++++++++++++++++ src/modlistviewactions.h | 6 +++ 9 files changed, 148 insertions(+), 140 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 265cc5c2..91897d2f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3832,22 +3832,6 @@ QMenu *MainWindow::openFolderMenu() return FolderMenu; } -void MainWindow::addModSendToContextMenu(QMenu *menu) -{ - if (ui->modList->sortColumn() != ModList::COL_PRIORITY) - return; - - QMenu *sub_menu = new QMenu(menu); - sub_menu->setTitle(tr("Send to")); - sub_menu->addAction(tr("Top"), [&]() { sendSelectedModsToTop_clicked(); }); - sub_menu->addAction(tr("Bottom"), [&]() { sendSelectedModsToBottom_clicked(); }); - sub_menu->addAction(tr("Priority..."), [&]() { sendSelectedModsToPriority_clicked(); }); - sub_menu->addAction(tr("Separator..."), [&]() { sendSelectedModsToSeparator_clicked(); }); - - menu->addMenu(sub_menu); - menu->addSeparator(); -} - void MainWindow::addPluginSendToContextMenu(QMenu *menu) { if (m_PluginListSortProxy->sortColumn() != PluginList::COL_PRIORITY) @@ -3932,7 +3916,10 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Rename Separator..."), [=]() { renameMod_clicked(); }); menu.addAction(tr("Remove Separator..."), [=]() { removeMod_clicked(modIndex); }); menu.addSeparator(); - addModSendToContextMenu(&menu); + if (ui->modList->sortColumn() == ModList::COL_PRIORITY) { + menu.addMenu(menu.createSendToContextMenu()); + menu.addSeparator(); + } menu.addAction(tr("Select Color..."), [=]() { setColor_clicked(modIndex); }); if (info->color().isValid()) { @@ -3944,7 +3931,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) // foregin else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - addModSendToContextMenu(&menu); } // regular @@ -3981,7 +3967,10 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addSeparator(); - addModSendToContextMenu(&menu); + if (ui->modList->sortColumn() == ModList::COL_PRIORITY) { + menu.addMenu(menu.createSendToContextMenu()); + menu.addSeparator(); + } menu.addAction(tr("Rename Mod..."), [=]() { renameMod_clicked(); }); menu.addAction(tr("Reinstall Mod"), [=]() { reinstallMod_clicked(modIndex); }); @@ -5477,97 +5466,3 @@ void MainWindow::on_clearFiltersButton_clicked() ui->modFilterEdit->clear(); deselectFilters(); } - -void MainWindow::sendSelectedModsToPriority(int newPriority) -{ - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection()) { - std::vector modsToMove; - for (auto idx : selection->selectedRows(ModList::COL_PRIORITY)) { - modsToMove.push_back(m_OrganizerCore.currentProfile()->modIndexByPriority(idx.data().toInt())); - } - - if (modsToMove.size() == 1) { - m_OrganizerCore.modList()->changeModPriority(modsToMove[0], newPriority); - } - else { - m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority); - } - } -} - -void MainWindow::sendSelectedModsToTop_clicked() -{ - sendSelectedModsToPriority(0); -} - -void MainWindow::sendSelectedModsToBottom_clicked() -{ - sendSelectedModsToPriority(INT_MAX); -} - -void MainWindow::sendSelectedModsToPriority_clicked() -{ - bool ok; - int newPriority = QInputDialog::getInt(this, - tr("Set Priority"), tr("Set the priority of the selected mods"), - 0, 0, INT_MAX, 1, &ok); - if (!ok) return; - - sendSelectedModsToPriority(newPriority); -} - -void MainWindow::sendSelectedModsToSeparator_clicked() -{ - QStringList separators; - auto indexesByPriority = m_OrganizerCore.currentProfile()->getAllIndexesByPriority(); - for (auto iter = indexesByPriority.begin(); iter != indexesByPriority.end(); iter++) { - if ((iter->second != UINT_MAX)) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(iter->second); - if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { - separators << modInfo->name().chopped(10); // Chops the "_separator" away from the name - } - } - } - - ListDialog dialog(this); - dialog.setWindowTitle("Select a separator..."); - dialog.setChoices(separators); - - if (dialog.exec() == QDialog::Accepted) { - QString result = dialog.getChoice(); - if (!result.isEmpty()) { - result += "_separator"; - - int newPriority = INT_MAX; - bool foundSection = false; - for (auto mod : m_OrganizerCore.modsSortedByProfilePriority(m_OrganizerCore.currentProfile())) { - unsigned int modIndex = ModInfo::getIndex(mod); - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - if (!foundSection && result.compare(mod) == 0) { - foundSection = true; - } else if (foundSection && modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { - newPriority = m_OrganizerCore.currentProfile()->getModPriority(modIndex); - break; - } - } - - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection()) { - std::vector modsToMove; - for (QModelIndex idx : selection->selectedRows(ModList::COL_PRIORITY)) { - modsToMove.push_back(m_OrganizerCore.currentProfile()->modIndexByPriority(idx.data().toInt())); - } - if (modsToMove.size() == 1) { - int oldPriority = m_OrganizerCore.currentProfile()->getModPriority(modsToMove[0]); - if (oldPriority < newPriority) - --newPriority; - m_OrganizerCore.modList()->changeModPriority(modsToMove[0], newPriority); - } - else { - m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority); - } - } - } - } -} diff --git a/src/mainwindow.h b/src/mainwindow.h index 57f26220..790960e1 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -244,15 +244,12 @@ private: bool createBackup(const QString &filePath, const QDateTime &time); QString queryRestore(const QString &filePath); - void addModSendToContextMenu(QMenu *menu); void addPluginSendToContextMenu(QMenu *menu); QMenu *openFolderMenu(); void dropLocalFile(const QUrl &url, const QString &outputDir, bool move); - void sendSelectedModsToPriority(int newPriority); - void toggleMO2EndorseState(); void toggleUpdateAction(); @@ -374,10 +371,6 @@ private slots: void information_clicked(int modIndex); void enableSelectedMods_clicked(); void disableSelectedMods_clicked(); - void sendSelectedModsToTop_clicked(); - void sendSelectedModsToBottom_clicked(); - void sendSelectedModsToPriority_clicked(); - void sendSelectedModsToSeparator_clicked(); // data-tree context menu // pluginlist context menu diff --git a/src/modlist.cpp b/src/modlist.cpp index 7da6fe3b..22899aaa 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1417,7 +1417,7 @@ QString ModList::getColumnToolTip(int column) const } } -void ModList::shiftMods(const QModelIndexList& indices, int offset) +void ModList::shiftModsPriority(const QModelIndexList& indices, int offset) { // retrieve the mod index and sort them by priority to avoid issue // when moving them @@ -1451,6 +1451,26 @@ void ModList::shiftMods(const QModelIndexList& indices, int offset) emit modPrioritiesChanged(allIndex); } +void ModList::changeModsPriority(const QModelIndexList& indices, int priority) +{ + if (indices.isEmpty()) { + return; + } + + std::vector allIndex; + for (auto& idx : indices) { + auto index = idx.data(IndexRole).toInt(); + allIndex.push_back(index); + } + + if (allIndex.size() == 1) { + changeModPriority(allIndex[0], priority); + } + else { + changeModPriority(allIndex, priority); + } +} + bool ModList::toggleState(const QModelIndexList& indices) { emit aboutToChangeData(); diff --git a/src/modlist.h b/src/modlist.h index 870978f9..cabd1f32 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -226,7 +226,11 @@ public slots: // shift the priority of mods at the given indices by the given offset // - void shiftMods(const QModelIndexList& indices, int offset); + void shiftModsPriority(const QModelIndexList& indices, int offset); + + // change the priority of the mods specified by the given indices + // + void changeModsPriority(const QModelIndexList& indices, int priority); // toggle the active state of mods at the given indices // diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 41031eaa..c4a82e46 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -45,6 +45,7 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i QMenu(view) , m_core(core) , m_index() + , m_view(view) { if (view->selectionModel()->hasSelection()) { m_index = view->indexViewToModel(view->selectionModel()->selectedRows()); @@ -68,20 +69,23 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i // Add type-specific items ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + // TODO: + // - Don't forget to check for the sort priority for "Send To... " + if (info->isOverwrite()) { - addOverwriteActions(core, view); + addOverwriteActions(); } else if (info->isBackup()) { - addBackupActions(core, view); + addBackupActions(); } else if (info->isSeparator()) { - addSeparatorActions(core, view); + addSeparatorActions(); } else if (info->isForeign()) { - addForeignActions(core, view); + addForeignActions(); } else { - addRegularActions(core, view); + addRegularActions(); } // add information for all except foreign @@ -91,27 +95,40 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i } } -void ModListContextMenu::addOverwriteActions(OrganizerCore& core, ModListView* modListView) +QMenu* ModListContextMenu::createSendToContextMenu() { - + QMenu* menu = new QMenu(m_view); + menu->setTitle(tr("Send to... ")); + menu->addAction(tr("Top"), [=]() { m_view->actions().sendModsToTop(m_index); }); + menu->addAction(tr("Bottom"), [=]() { m_view->actions().sendModsToBottom(m_index); }); + menu->addAction(tr("Priority..."), [=]() { m_view->actions().sendModsToPriority(m_index); }); + menu->addAction(tr("Separator..."), [=]() { m_view->actions().sendModsToSeparator(m_index); }); + return menu; } -void ModListContextMenu::addSeparatorActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addOverwriteActions() { } -void ModListContextMenu::addForeignActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addSeparatorActions() { } -void ModListContextMenu::addBackupActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addForeignActions() +{ + if (m_view->sortColumn() == ModList::COL_PRIORITY) { + addMenu(createSendToContextMenu()); + } +} + +void ModListContextMenu::addBackupActions() { } -void ModListContextMenu::addRegularActions(OrganizerCore& core, ModListView* modListView) +void ModListContextMenu::addRegularActions() { } diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 9d015afc..3bc15c57 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -29,18 +29,23 @@ public: // ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* modListView); -private: +public: // TODO: Move this to private when all is done + + // create the "Send to... " context menu + // + QMenu* createSendToContextMenu(); // add actions/menus to this menu for each type of mod // - void addOverwriteActions(OrganizerCore& core, ModListView* modListView); - void addSeparatorActions(OrganizerCore& core, ModListView* modListView); - void addForeignActions(OrganizerCore& core, ModListView* modListView); - void addBackupActions(OrganizerCore& core, ModListView* modListView); - void addRegularActions(OrganizerCore& core, ModListView* modListView); + void addOverwriteActions(); + void addSeparatorActions(); + void addForeignActions(); + void addBackupActions(); + void addRegularActions(); OrganizerCore& m_core; QModelIndexList m_index; + ModListView* m_view; }; diff --git a/src/modlistview.cpp b/src/modlistview.cpp index f8192758..8c826882 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -575,7 +575,7 @@ bool ModListView::moveSelection(int key) offset = -offset; } - m_core->modList()->shiftMods(sourceRows, offset); + m_core->modList()->shiftModsPriority(sourceRows, offset); // reset the selection and the index setCurrentIndex(indexModelToView(cindex)); diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 6faebc15..0d556e64 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -11,6 +11,7 @@ #include "categories.h" #include "filedialogmemory.h" #include "filterlist.h" +#include "listdialog.h" #include "modinfodialog.h" #include "modlist.h" #include "modlistview.h" @@ -430,3 +431,70 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in } } } + +void ModListViewActions::sendModsToTop(const QModelIndexList& index) const +{ + m_core.modList()->changeModsPriority(index, 0); +} + +void ModListViewActions::sendModsToBottom(const QModelIndexList& index) const +{ + m_core.modList()->changeModsPriority(index, std::numeric_limits::max()); +} + +void ModListViewActions::sendModsToPriority(const QModelIndexList& index) const +{ + bool ok; + int priority = QInputDialog::getInt(m_view, + tr("Set Priority"), tr("Set the priority of the selected mods"), + 0, 0, std::numeric_limits::max(), 1, &ok); + if (!ok) return; + + m_core.modList()->changeModsPriority(index, priority); +} + +void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const +{ + QStringList separators; + auto indexesByPriority = m_core.currentProfile()->getAllIndexesByPriority(); + for (auto iter = indexesByPriority.begin(); iter != indexesByPriority.end(); iter++) { + if ((iter->second != UINT_MAX)) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(iter->second); + if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { + separators << modInfo->name().chopped(10); // Chops the "_separator" away from the name + } + } + } + + ListDialog dialog(m_view); + dialog.setWindowTitle("Select a separator..."); + dialog.setChoices(separators); + + if (dialog.exec() == QDialog::Accepted) { + QString result = dialog.getChoice(); + if (!result.isEmpty()) { + result += "_separator"; + + int newPriority = std::numeric_limits::max(); + bool foundSection = false; + for (auto mod : m_core.modsSortedByProfilePriority(m_core.currentProfile())) { + unsigned int modIndex = ModInfo::getIndex(mod); + ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); + if (!foundSection && result.compare(mod) == 0) { + foundSection = true; + } + else if (foundSection && modInfo->isSeparator()) { + newPriority = m_core.currentProfile()->getModPriority(modIndex); + break; + } + } + + if (index.size() == 1 + && m_core.currentProfile()->getModPriority(index[0].data(ModList::IndexRole).toInt()) < newPriority) { + --newPriority; + } + + m_core.modList()->changeModsPriority(index, newPriority); + } + } +} diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index b01b8e79..d8e1a3d0 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -53,6 +53,12 @@ public: void displayModInformation(unsigned int index, ModInfoTabIDs tab = ModInfoTabIDs::None) const; void displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID = ModInfoTabIDs::None) const; + // move mods to top/bottom, start the "Send to priority" and "Send to separator" dialog + // + void sendModsToTop(const QModelIndexList& index) const; + void sendModsToBottom(const QModelIndexList& index) const; + void sendModsToPriority(const QModelIndexList& index) const; + void sendModsToSeparator(const QModelIndexList& index) const; private: -- cgit v1.3.1 From 7f0c9b8d8278f14754b375967267ff67d6fdb6ee Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 00:32:15 +0100 Subject: Move the overwrite context menu. --- src/mainwindow.cpp | 154 +++------------------------------------------ src/mainwindow.h | 12 ---- src/modlistcontextmenu.cpp | 44 +++++++------ src/modlistcontextmenu.h | 15 +++-- src/modlistviewactions.cpp | 128 ++++++++++++++++++++++++++++++++++++- src/modlistviewactions.h | 20 ++++++ src/organizercore.cpp | 2 - 7 files changed, 188 insertions(+), 187 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 91897d2f..056eeef7 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -535,14 +535,17 @@ MainWindow::MainWindow(Settings &settings void MainWindow::setupModList() { - ui->modList->setup(m_OrganizerCore, new ModListViewActions(m_OrganizerCore, *m_Filters, m_CategoryFactory, this, ui->modList), ui); + auto* actions = new ModListViewActions(m_OrganizerCore, *m_Filters, m_CategoryFactory, this, ui->modList); + ui->modList->setup(m_OrganizerCore, actions, ui); + connect(actions, &ModListViewActions::overwriteCleared, [=]() { scheduleCheckForProblems(); }); connect(ui->modList, &ModListView::removeSelectedMods, [=]() { removeMod_clicked(-1); }); + connect(m_OrganizerCore.modList(), &ModList::clearOverwrite, actions, &ModListViewActions::clearOverwrite); + connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, [&]() { m_ArchiveListWriter.write(); }); // keep here for now connect(ui->modList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::modlistSelectionsChanged); - connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, [&]() { m_ArchiveListWriter.write(); }); } void MainWindow::resetActionIcons() @@ -2925,21 +2928,6 @@ void MainWindow::visitNexusOrWebPage_clicked(int index) { } } -void MainWindow::openExplorer_clicked(int index) -{ - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); - shell::Explore(info->absolutePath()); - } - } - else { - ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - shell::Explore(modInfo->absolutePath()); - } -} - void MainWindow::openPluginOriginExplorer_clicked() { QItemSelectionModel *selection = ui->espList->selectionModel(); @@ -3121,127 +3109,6 @@ void MainWindow::resetColor_clicked(int modIndex) m_OrganizerCore.settings().colors().removePreviousSeparatorColor(); } -void MainWindow::createModFromOverwrite() -{ - GuessedValue name; - name.setFilter(&fixDirectoryName); - - while (name->isEmpty()) { - bool ok; - name.update(QInputDialog::getText(this, tr("Create Mod..."), - tr("This will move all files from overwrite into a new, regular mod.\n" - "Please enter a name:"), QLineEdit::Normal, "", &ok), - GUESS_USER); - if (!ok) { - return; - } - } - - if (m_OrganizerCore.modList()->getMod(name) != nullptr) { - reportError(tr("A mod with this name already exists")); - return; - } - - const IModInterface *newMod = m_OrganizerCore.createMod(name); - if (newMod == nullptr) { - return; - } - - doMoveOverwriteContentToMod(newMod->absolutePath()); -} - -void MainWindow::moveOverwriteContentToExistingMod() -{ - QStringList mods; - auto indexesByPriority = m_OrganizerCore.currentProfile()->getAllIndexesByPriority(); - for (auto & iter : indexesByPriority) { - if ((iter.second != UINT_MAX)) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(iter.second); - if (!modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN) && !modInfo->hasFlag(ModInfo::FLAG_OVERWRITE)) { - mods << modInfo->name(); - } - } - } - - ListDialog dialog(this); - dialog.setWindowTitle("Select a mod..."); - dialog.setChoices(mods); - - if (dialog.exec() == QDialog::Accepted) { - QString result = dialog.getChoice(); - if (!result.isEmpty()) { - - QString modAbsolutePath; - - for (const auto& mod : m_OrganizerCore.modsSortedByProfilePriority(m_OrganizerCore.currentProfile())) { - if (result.compare(mod) == 0) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(mod)); - modAbsolutePath = modInfo->absolutePath(); - break; - } - } - - if (modAbsolutePath.isNull()) { - log::warn("Mod {} has not been found, for some reason", result); - return; - } - - doMoveOverwriteContentToMod(modAbsolutePath); - } - } -} - -void MainWindow::doMoveOverwriteContentToMod(const QString &modAbsolutePath) -{ - unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool { - std::vector flags = mod->getFlags(); - return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end(); }); - - ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(overwriteIndex); - bool successful = shellMove((QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"), - (QDir::toNativeSeparators(modAbsolutePath)), false, this); - - if (successful) { - MessageDialog::showMessage(tr("Move successful."), this); - } - else { - const auto e = GetLastError(); - log::error("Move operation failed: {}", formatSystemMessage(e)); - } - - m_OrganizerCore.refresh(); -} - -void MainWindow::clearOverwrite() -{ - unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool { - std::vector flags = mod->getFlags(); - return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) - != flags.end(); - }); - - ModInfo::Ptr modInfo = ModInfo::getByIndex(overwriteIndex); - if (modInfo) - { - QDir overwriteDir(modInfo->absolutePath()); - if (QMessageBox::question(this, tr("Are you sure?"), - tr("About to recursively delete:\n") + overwriteDir.absolutePath(), - QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) - { - QStringList delList; - for (auto f : overwriteDir.entryList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot)) - delList.push_back(overwriteDir.absoluteFilePath(f)); - if (shellDelete(delList, true)) { - scheduleCheckForProblems(); - m_OrganizerCore.refresh(); - } else { - const auto e = GetLastError(); - log::error("Delete operation failed: {}", formatSystemMessage(e)); - } - } - } -} - void MainWindow::cancelModListEditor() { ui->modList->setEnabled(false); @@ -3869,13 +3736,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) // context menu for overwrites if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { - if (QDir(info->absolutePath()).count() > 2) { - menu.addAction(tr("Sync to Mods..."), [=]() { m_OrganizerCore.syncOverwrite(); }); - menu.addAction(tr("Create Mod..."), [=]() { createModFromOverwrite(); }); - menu.addAction(tr("Move content to Mod..."), [=]() { moveOverwriteContentToExistingMod(); }); - menu.addAction(tr("Clear Overwrite..."), [=]() { clearOverwrite(); }); - } - menu.addAction(tr("Open in Explorer"), [=]() { openExplorer_clicked(modIndex); }); } // context menu for mod backups @@ -3899,7 +3759,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Visit on %1").arg(url.host()), [=]() { visitWebPage_clicked(modIndex); }); } - menu.addAction(tr("Open in Explorer"), [=]() { openExplorer_clicked(modIndex); }); + menu.addAction(tr("Open in Explorer"), [=]() { ui->modList->actions().openExplorer({ contextIdx }); }); } // separator @@ -4049,7 +3909,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Visit on %1").arg(url.host()), [=]() { visitWebPage_clicked(modIndex); }); } - menu.addAction(tr("Open in Explorer"), [&, modIndex]() { openExplorer_clicked(modIndex); }); + menu.addAction(tr("Open in Explorer"), [=]() { ui->modList->actions().openExplorer({ contextIdx }); }); } if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end()) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 790960e1..21c8f2aa 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -365,7 +365,6 @@ private slots: void visitOnNexus_clicked(int modIndex); void visitWebPage_clicked(int modIndex); void visitNexusOrWebPage_clicked(int modIndex); - void openExplorer_clicked(int modIndex); void openPluginOriginExplorer_clicked(); void openOriginInformation_clicked(); void information_clicked(int modIndex); @@ -390,17 +389,6 @@ private slots: BSA::EErrorCode extractBSA(BSA::Archive &archive, BSA::Folder::Ptr folder, const QString &destination, QProgressDialog &extractProgress); - void createModFromOverwrite(); - /** - * @brief sends the content of the overwrite folder to an already existing mod - */ - void moveOverwriteContentToExistingMod(); - /** - * @brief actually sends the content of the overwrite folder to specified mod - */ - void doMoveOverwriteContentToMod(const QString &modAbsolutePath); - void clearOverwrite(); - // nexus related void checkModsForUpdates(); diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index c4a82e46..dda88735 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -44,14 +44,14 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* view) : QMenu(view) , m_core(core) - , m_index() + , m_index(index) , m_view(view) { if (view->selectionModel()->hasSelection()) { - m_index = view->indexViewToModel(view->selectionModel()->selectedRows()); + m_selected = view->indexViewToModel(view->selectionModel()->selectedRows()); } else { - m_index = { index }; + m_selected = { index }; } @@ -73,24 +73,24 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i // - Don't forget to check for the sort priority for "Send To... " if (info->isOverwrite()) { - addOverwriteActions(); + addOverwriteActions(info); } else if (info->isBackup()) { - addBackupActions(); + addBackupActions(info); } else if (info->isSeparator()) { - addSeparatorActions(); + addSeparatorActions(info); } else if (info->isForeign()) { - addForeignActions(); + addForeignActions(info); } else { - addRegularActions(); + addRegularActions(info); } // add information for all except foreign if (!info->isForeign()) { - QAction* infoAction = addAction(tr("Information..."), [=]() { view->actions().displayModInformation(m_index[0].row()); }); + QAction* infoAction = addAction(tr("Information..."), [=]() { view->actions().displayModInformation(m_index.data(ModList::IndexRole).toInt()); }); setDefaultAction(infoAction); } } @@ -99,36 +99,42 @@ QMenu* ModListContextMenu::createSendToContextMenu() { QMenu* menu = new QMenu(m_view); menu->setTitle(tr("Send to... ")); - menu->addAction(tr("Top"), [=]() { m_view->actions().sendModsToTop(m_index); }); - menu->addAction(tr("Bottom"), [=]() { m_view->actions().sendModsToBottom(m_index); }); - menu->addAction(tr("Priority..."), [=]() { m_view->actions().sendModsToPriority(m_index); }); - menu->addAction(tr("Separator..."), [=]() { m_view->actions().sendModsToSeparator(m_index); }); + menu->addAction(tr("Top"), [=]() { m_view->actions().sendModsToTop(m_selected); }); + menu->addAction(tr("Bottom"), [=]() { m_view->actions().sendModsToBottom(m_selected); }); + menu->addAction(tr("Priority..."), [=]() { m_view->actions().sendModsToPriority(m_selected); }); + menu->addAction(tr("Separator..."), [=]() { m_view->actions().sendModsToSeparator(m_selected); }); return menu; } -void ModListContextMenu::addOverwriteActions() +void ModListContextMenu::addOverwriteActions(ModInfo::Ptr mod) { - + if (QDir(mod->absolutePath()).count() > 2) { + addAction(tr("Sync to Mods..."), [=]() { m_core.syncOverwrite(); }); + addAction(tr("Create Mod..."), [=]() { m_view->actions().createModFromOverwrite(); }); + addAction(tr("Move content to Mod..."), [=]() { m_view->actions().moveOverwriteContentToExistingMod(); }); + addAction(tr("Clear Overwrite..."), [=]() { m_view->actions().clearOverwrite(); }); + } + addAction(tr("Open in Explorer"), [=]() { m_view->actions().openExplorer(m_selected); }); } -void ModListContextMenu::addSeparatorActions() +void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) { } -void ModListContextMenu::addForeignActions() +void ModListContextMenu::addForeignActions(ModInfo::Ptr mod) { if (m_view->sortColumn() == ModList::COL_PRIORITY) { addMenu(createSendToContextMenu()); } } -void ModListContextMenu::addBackupActions() +void ModListContextMenu::addBackupActions(ModInfo::Ptr mod) { } -void ModListContextMenu::addRegularActions() +void ModListContextMenu::addRegularActions(ModInfo::Ptr mod) { } diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 3bc15c57..22318575 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -7,6 +7,8 @@ #include #include +#include "modinfo.h" + class ModListView; class OrganizerCore; @@ -37,14 +39,15 @@ public: // TODO: Move this to private when all is done // add actions/menus to this menu for each type of mod // - void addOverwriteActions(); - void addSeparatorActions(); - void addForeignActions(); - void addBackupActions(); - void addRegularActions(); + void addOverwriteActions(ModInfo::Ptr mod); + void addSeparatorActions(ModInfo::Ptr mod); + void addForeignActions(ModInfo::Ptr mod); + void addBackupActions(ModInfo::Ptr mod); + void addRegularActions(ModInfo::Ptr mod); OrganizerCore& m_core; - QModelIndexList m_index; + QModelIndex m_index; + QModelIndexList m_selected; ModListView* m_view; }; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 0d556e64..a07fb27c 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "categories.h" #include "filedialogmemory.h" @@ -16,6 +17,7 @@ #include "modlist.h" #include "modlistview.h" #include "mainwindow.h" +#include "messagedialog.h" #include "nexusinterface.h" #include "nxmaccessmanager.h" #include "savetextasdialog.h" @@ -387,7 +389,11 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in dialog->show(); dialog->raise(); dialog->activateWindow(); - connect(dialog, SIGNAL(finished(int)), this, SLOT(overwriteClosed(int))); + connect(dialog, &QDialog::finished, [=]() { + m_core.modList()->modInfoChanged(modInfo); + dialog->deleteLater(); + m_core.refreshDirectoryStructure(); + }); } catch (const std::exception& e) { reportError(tr("Failed to display overwrite dialog: %1").arg(e.what())); @@ -498,3 +504,123 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const } } } + +void ModListViewActions::openExplorer(const QModelIndexList& index) const +{ + for (auto& idx : index) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + shell::Explore(info->absolutePath()); + } +} + +void ModListViewActions::moveOverwriteContentsTo(const QString& absolutePath) const +{ + ModInfo::Ptr overwriteInfo = ModInfo::getOverwrite(); + bool successful = shellMove((QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"), + (QDir::toNativeSeparators(absolutePath)), false, m_view); + + if (successful) { + MessageDialog::showMessage(tr("Move successful."), m_view); + } + else { + const auto e = GetLastError(); + log::error("Move operation failed: {}", formatSystemMessage(e)); + } + + m_core.refresh(); +} + +void ModListViewActions::createModFromOverwrite() const +{ + GuessedValue name; + name.setFilter(&fixDirectoryName); + + while (name->isEmpty()) { + bool ok; + name.update(QInputDialog::getText(m_view, tr("Create Mod..."), + tr("This will move all files from overwrite into a new, regular mod.\n" + "Please enter a name:"), QLineEdit::Normal, "", &ok), + GUESS_USER); + if (!ok) { + return; + } + } + + if (m_core.modList()->getMod(name) != nullptr) { + reportError(tr("A mod with this name already exists")); + return; + } + + const IModInterface* newMod = m_core.createMod(name); + if (newMod == nullptr) { + return; + } + + moveOverwriteContentsTo(newMod->absolutePath()); +} + +void ModListViewActions::moveOverwriteContentToExistingMod() const +{ + QStringList mods; + auto indexesByPriority = m_core.currentProfile()->getAllIndexesByPriority(); + for (auto& iter : indexesByPriority) { + if ((iter.second != UINT_MAX)) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(iter.second); + if (!modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN) && !modInfo->hasFlag(ModInfo::FLAG_OVERWRITE)) { + mods << modInfo->name(); + } + } + } + + ListDialog dialog(m_view); + dialog.setWindowTitle("Select a mod..."); + dialog.setChoices(mods); + + if (dialog.exec() == QDialog::Accepted) { + QString result = dialog.getChoice(); + if (!result.isEmpty()) { + + QString modAbsolutePath; + + for (const auto& mod : m_core.modsSortedByProfilePriority(m_core.currentProfile())) { + if (result.compare(mod) == 0) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(mod)); + modAbsolutePath = modInfo->absolutePath(); + break; + } + } + + if (modAbsolutePath.isNull()) { + log::warn("Mod {} has not been found, for some reason", result); + return; + } + + moveOverwriteContentsTo(modAbsolutePath); + } + } +} + +void ModListViewActions::clearOverwrite() const +{ + ModInfo::Ptr modInfo = ModInfo::getOverwrite(); + if (modInfo) + { + QDir overwriteDir(modInfo->absolutePath()); + if (QMessageBox::question(m_view, tr("Are you sure?"), + tr("About to recursively delete:\n") + overwriteDir.absolutePath(), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) + { + QStringList delList; + for (auto f : overwriteDir.entryList(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot)) + delList.push_back(overwriteDir.absoluteFilePath(f)); + if (shellDelete(delList, true)) { + emit overwriteCleared(); + m_core.refresh(); + } + else { + const auto e = GetLastError(); + log::error("Delete operation failed: {}", formatSystemMessage(e)); + } + } + } +} diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index d8e1a3d0..10aa6c39 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -60,6 +60,26 @@ public: void sendModsToPriority(const QModelIndexList& index) const; void sendModsToSeparator(const QModelIndexList& index) const; + // open the Windows explorer for the specified mods + // + void openExplorer(const QModelIndexList& index) const; + + // overwrite-specific actions + // + void createModFromOverwrite() const; + void moveOverwriteContentToExistingMod() const; + void clearOverwrite() const; + +signals: + + // emitted when the overwrite mod has been clear + // + void overwriteCleared() const; + +private: + + void moveOverwriteContentsTo(const QString& absolutePath) const; + private: OrganizerCore& m_core; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 986b6fbb..507932e2 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -249,8 +249,6 @@ void OrganizerCore::setUserInterface(IUserInterface* ui) SLOT(modRemoved(QString))); connect(&m_InstallationManager, SIGNAL(modReplaced(QString)), w, SLOT(modRemoved(QString))); - connect(&m_ModList, SIGNAL(clearOverwrite()), w, - SLOT(clearOverwrite())); connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), w, SLOT(fileMoved(QString, QString, QString))); connect(&m_PluginList, SIGNAL(writePluginsList()), w, -- cgit v1.3.1 From 4ee929b68a5ba3f622fd6cecf37f61b983eb0874 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 00:55:49 +0100 Subject: Move the backup context menu. --- src/mainwindow.cpp | 48 +------------ src/mainwindow.h | 4 +- src/modlistcontextmenu.cpp | 37 +++++++--- src/modlistcontextmenu.h | 2 + src/modlistviewactions.cpp | 171 +++++++++++++++++++++++++++++++++++++++++++++ src/modlistviewactions.h | 11 +++ 6 files changed, 216 insertions(+), 57 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 056eeef7..6942f83b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2365,32 +2365,6 @@ void MainWindow::renameMod_clicked() } } - -void MainWindow::restoreBackup_clicked(int modIndex) -{ - QRegExp backupRegEx("(.*)_backup[0-9]*$"); - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - if (backupRegEx.indexIn(modInfo->name()) != -1) { - QString regName = backupRegEx.cap(1); - QDir modDir(QDir::fromNativeSeparators(m_OrganizerCore.settings().paths().mods())); - if (!modDir.exists(regName) || - (QMessageBox::question(this, tr("Overwrite?"), - tr("This will replace the existing mod \"%1\". Continue?").arg(regName), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - if (modDir.exists(regName) && !shellDelete(QStringList(modDir.absoluteFilePath(regName)))) { - reportError(tr("failed to remove mod \"%1\"").arg(regName)); - } else { - QString destinationPath = QDir::fromNativeSeparators(m_OrganizerCore.settings().paths().mods()) + "/" + regName; - if (!modDir.rename(modInfo->absolutePath(), destinationPath)) { - reportError(tr("failed to rename \"%1\" to \"%2\"").arg(modInfo->absolutePath()).arg(destinationPath)); - } - m_OrganizerCore.refresh(); - ui->modList->updateModCount(); - } - } - } -} - void MainWindow::modlistChanged(const QModelIndex&, int) { m_OrganizerCore.currentProfile()->writeModlist(); @@ -2850,7 +2824,7 @@ void MainWindow::visitOnNexus_clicked(int modIndex) void MainWindow::visitWebPage_clicked(int index) { - QItemSelectionModel *selection = ui->modList->selectionModel(); + QItemSelectionModel* selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { int count = selection->selectedRows().count(); if (count > 10) { @@ -3740,26 +3714,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) // context menu for mod backups else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) { - menu.addAction(tr("Restore Backup"), [=]() { restoreBackup_clicked(modIndex); }); - menu.addAction(tr("Remove Backup..."), [=]() { removeMod_clicked(modIndex); }); - menu.addSeparator(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { - menu.addAction(tr("Ignore missing data"), [=]() { ignoreMissingData_clicked(modIndex); }); - } - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { - menu.addAction(tr("Mark as converted/working"), [=]() { markConverted_clicked(modIndex); }); - } - menu.addSeparator(); - if (info->nexusId() > 0) { - menu.addAction(tr("Visit on Nexus"), [=]() { visitOnNexus_clicked(modIndex); }); - } - - const auto url = info->parseCustomURL(); - if (url.isValid()) { - menu.addAction(tr("Visit on %1").arg(url.host()), [=]() { visitWebPage_clicked(modIndex); }); - } - - menu.addAction(tr("Open in Explorer"), [=]() { ui->modList->actions().openExplorer({ contextIdx }); }); } // separator diff --git a/src/mainwindow.h b/src/mainwindow.h index 21c8f2aa..49ecee32 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -151,6 +151,8 @@ public slots: void directory_refreshed(); + void updatePluginCount(); + signals: /** @@ -347,7 +349,6 @@ private slots: // modlist context menu void installMod_clicked(); - void restoreBackup_clicked(int modIndex); void renameMod_clicked(); void removeMod_clicked(int modIndex); void setColor_clicked(int modIndex); @@ -498,7 +499,6 @@ private slots: void esplistSelectionsChanged(const QItemSelection ¤t); void resetActionIcons(); - void updatePluginCount(); private slots: // ui slots // actions diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index dda88735..df80f0d9 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -46,6 +46,7 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i , m_core(core) , m_index(index) , m_view(view) + , m_actions(view->actions()) { if (view->selectionModel()->hasSelection()) { m_selected = view->indexViewToModel(view->selectionModel()->selectedRows()); @@ -99,10 +100,10 @@ QMenu* ModListContextMenu::createSendToContextMenu() { QMenu* menu = new QMenu(m_view); menu->setTitle(tr("Send to... ")); - menu->addAction(tr("Top"), [=]() { m_view->actions().sendModsToTop(m_selected); }); - menu->addAction(tr("Bottom"), [=]() { m_view->actions().sendModsToBottom(m_selected); }); - menu->addAction(tr("Priority..."), [=]() { m_view->actions().sendModsToPriority(m_selected); }); - menu->addAction(tr("Separator..."), [=]() { m_view->actions().sendModsToSeparator(m_selected); }); + menu->addAction(tr("Top"), [=]() { m_actions.sendModsToTop(m_selected); }); + menu->addAction(tr("Bottom"), [=]() { m_actions.sendModsToBottom(m_selected); }); + menu->addAction(tr("Priority..."), [=]() { m_actions.sendModsToPriority(m_selected); }); + menu->addAction(tr("Separator..."), [=]() { m_actions.sendModsToSeparator(m_selected); }); return menu; } @@ -110,11 +111,11 @@ void ModListContextMenu::addOverwriteActions(ModInfo::Ptr mod) { if (QDir(mod->absolutePath()).count() > 2) { addAction(tr("Sync to Mods..."), [=]() { m_core.syncOverwrite(); }); - addAction(tr("Create Mod..."), [=]() { m_view->actions().createModFromOverwrite(); }); - addAction(tr("Move content to Mod..."), [=]() { m_view->actions().moveOverwriteContentToExistingMod(); }); - addAction(tr("Clear Overwrite..."), [=]() { m_view->actions().clearOverwrite(); }); + addAction(tr("Create Mod..."), [=]() { m_actions.createModFromOverwrite(); }); + addAction(tr("Move content to Mod..."), [=]() { m_actions.moveOverwriteContentToExistingMod(); }); + addAction(tr("Clear Overwrite..."), [=]() { m_actions.clearOverwrite(); }); } - addAction(tr("Open in Explorer"), [=]() { m_view->actions().openExplorer(m_selected); }); + addAction(tr("Open in Explorer"), [=]() { m_actions.openExplorer(m_selected); }); } void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) @@ -131,7 +132,27 @@ void ModListContextMenu::addForeignActions(ModInfo::Ptr mod) void ModListContextMenu::addBackupActions(ModInfo::Ptr mod) { + auto flags = mod->getFlags(); + addAction(tr("Restore Backup"), [=]() { m_actions.restoreBackup(m_index); }); + addAction(tr("Remove Backup..."), [=]() { m_actions.removeMods(m_selected); }); + addSeparator(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { + addAction(tr("Ignore missing data"), [=]() { m_actions.ignoreMissingData(m_selected); }); + } + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { + addAction(tr("Mark as converted/working"), [=]() { m_actions.markConverted(m_selected); }); + } + addSeparator(); + if (mod->nexusId() > 0) { + addAction(tr("Visit on Nexus"), [=]() { m_actions.visitOnNexus(m_selected); }); + } + + const auto url = mod->parseCustomURL(); + if (url.isValid()) { + addAction(tr("Visit on %1").arg(url.host()), [=]() { m_actions.visitWebPage(m_selected); }); + } + addAction(tr("Open in Explorer"), [=]() { m_actions.openExplorer(m_selected); }); } void ModListContextMenu::addRegularActions(ModInfo::Ptr mod) diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 22318575..05e6b601 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -10,6 +10,7 @@ #include "modinfo.h" class ModListView; +class ModListViewActions; class OrganizerCore; class ModListGlobalContextMenu : public QMenu @@ -49,6 +50,7 @@ public: // TODO: Move this to private when all is done QModelIndex m_index; QModelIndexList m_selected; ModListView* m_view; + ModListViewActions& m_actions; // shortcut for m_view->actions() }; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index a07fb27c..53c29a5d 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -505,6 +506,150 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const } } +void ModListViewActions::removeMods(const QModelIndexList& indices) const +{ + const int max_items = 20; + + try { + if (indices.size() > 1) { + QString mods; + QStringList modNames; + + int i = 0; + for (auto& idx : indices) { + QString name = idx.data().toString(); + if (!ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->isRegular()) { + continue; + } + + // adds an item for the mod name until `i` reaches `max_items`, which + // adds one "..." item; subsequent mods are not shown on the list but + // are still added to `modNames` below so they can be removed correctly + + if (i < max_items) { + mods += "
  • " + name + "
  • "; + } + else if (i == max_items) { + mods += "
  • ...
  • "; + } + + modNames.append(ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->name()); + ++i; + } + if (QMessageBox::question(m_view, tr("Confirm"), + tr("Remove the following mods?
      %1
    ").arg(mods), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + // use mod names instead of indexes because those become invalid during the removal + DownloadManager::startDisableDirWatcher(); + for (QString name : modNames) { + m_core.modList()->removeRowForce(ModInfo::getIndex(name), QModelIndex()); + } + DownloadManager::endDisableDirWatcher(); + } + } + else if (!indices.isEmpty()) { + m_core.modList()->removeRow(indices[0].data(ModList::IndexRole).toInt(), QModelIndex()); + } + m_view->updateModCount(); + m_main->updatePluginCount(); + } + catch (const std::exception& e) { + reportError(tr("failed to remove mod: %1").arg(e.what())); + } +} + +void ModListViewActions::ignoreMissingData(const QModelIndexList& indices) const +{ + for (auto& idx : indices) { + int row_idx = idx.data(ModList::IndexRole).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(row_idx); + info->markValidated(true); + m_core.modList()->notifyChange(row_idx); + } +} + +void ModListViewActions::markConverted(const QModelIndexList& indices) const +{ + for (auto& idx : indices) { + int row_idx = idx.data(ModList::IndexRole).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(row_idx); + info->markConverted(true); + m_core.modList()->notifyChange(row_idx); + } +} + +void ModListViewActions::visitOnNexus(const QModelIndexList& indices) const +{ + if (indices.size() > 1) { + if (indices.size() > 10) { + if (QMessageBox::question(m_view, tr("Opening Nexus Links"), + tr("You are trying to open %1 links to Nexus Mods. Are you sure you want to do this?").arg(indices.size()), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + int row_idx; + ModInfo::Ptr info; + QString gameName; + + for (auto& idx : indices) { + row_idx = idx.data(ModList::IndexRole).toInt(); + info = ModInfo::getByIndex(row_idx); + int modID = info->nexusId(); + gameName = info->gameName(); + if (modID > 0) { + shell::Open(QUrl(NexusInterface::instance().getModURL(modID, gameName))); + } + else { + log::error("mod '{}' has no nexus id", info->name()); + } + } + } + else if (!indices.isEmpty()) { + int modID = indices[0].data(Qt::UserRole).toInt(); + QString gameName = indices[0].data(Qt::UserRole + 4).toString(); + if (modID > 0) { + shell::Open(QUrl(NexusInterface::instance().getModURL(modID, gameName))); + } + else { + MessageDialog::showMessage(tr("Nexus ID for this mod is unknown"), m_view); + } + } +} + +void ModListViewActions::visitWebPage(const QModelIndexList& indices) const +{ + if (indices.size() > 1) { + if (indices.size() > 10) { + if (QMessageBox::question(m_view, tr("Opening Web Pages"), + tr("You are trying to open %1 Web Pages. Are you sure you want to do this?").arg(indices.size()), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + int row_idx; + ModInfo::Ptr info; + QString gameName; + for (auto& idx : indices) { + row_idx = idx.data(ModList::IndexRole).toInt(); + info = ModInfo::getByIndex(row_idx); + + const auto url = info->parseCustomURL(); + if (url.isValid()) { + shell::Open(url); + } + } + } + else if (!indices.isEmpty()) { + ModInfo::Ptr info = ModInfo::getByIndex(indices[0].data(ModList::IndexRole).toInt()); + + const auto url = info->parseCustomURL(); + if (url.isValid()) { + shell::Open(url); + } + } +} + void ModListViewActions::openExplorer(const QModelIndexList& index) const { for (auto& idx : index) { @@ -513,6 +658,32 @@ void ModListViewActions::openExplorer(const QModelIndexList& index) const } } +void ModListViewActions::restoreBackup(const QModelIndex& index) const +{ + QRegExp backupRegEx("(.*)_backup[0-9]*$"); + ModInfo::Ptr modInfo = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + if (backupRegEx.indexIn(modInfo->name()) != -1) { + QString regName = backupRegEx.cap(1); + QDir modDir(QDir::fromNativeSeparators(m_core.settings().paths().mods())); + if (!modDir.exists(regName) || + (QMessageBox::question(m_view, tr("Overwrite?"), + tr("This will replace the existing mod \"%1\". Continue?").arg(regName), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { + if (modDir.exists(regName) && !shellDelete(QStringList(modDir.absoluteFilePath(regName)))) { + reportError(tr("failed to remove mod \"%1\"").arg(regName)); + } + else { + QString destinationPath = QDir::fromNativeSeparators(m_core.settings().paths().mods()) + "/" + regName; + if (!modDir.rename(modInfo->absolutePath(), destinationPath)) { + reportError(tr("failed to rename \"%1\" to \"%2\"").arg(modInfo->absolutePath()).arg(destinationPath)); + } + m_core.refresh(); + m_view->updateModCount(); + } + } + } +} + void ModListViewActions::moveOverwriteContentsTo(const QString& absolutePath) const { ModInfo::Ptr overwriteInfo = ModInfo::getOverwrite(); diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 10aa6c39..05994813 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -60,10 +60,21 @@ public: void sendModsToPriority(const QModelIndexList& index) const; void sendModsToSeparator(const QModelIndexList& index) const; + // actions for most type of mods + void removeMods(const QModelIndexList& indices) const; + void ignoreMissingData(const QModelIndexList& indices) const; + void markConverted(const QModelIndexList& indices) const; + void visitOnNexus(const QModelIndexList& indices) const; + void visitWebPage(const QModelIndexList& indices) const; + // open the Windows explorer for the specified mods // void openExplorer(const QModelIndexList& index) const; + // backup-specific actions + // + void restoreBackup(const QModelIndex& index) const; + // overwrite-specific actions // void createModFromOverwrite() const; -- cgit v1.3.1 From 6e4b1790ae4057dfafd39dadff04c8d9b5f2eaeb Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 02:29:31 +0100 Subject: Move the separator context menu. --- src/mainwindow.cpp | 66 +------------------- src/mainwindow.h | 3 +- src/modlist.h | 2 + src/modlistcontextmenu.cpp | 150 +++++++++++++++++++++++++++++++++++++++++++-- src/modlistcontextmenu.h | 52 +++++++++++++++- src/modlistviewactions.cpp | 98 +++++++++++++++++++++++++++++ src/modlistviewactions.h | 29 ++++++++- 7 files changed, 325 insertions(+), 75 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6942f83b..cd31a0bf 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3363,45 +3363,6 @@ void MainWindow::addRemoveCategories_MenuHandler(QMenu* menu, int modIndex, cons refreshFilters(); } -void MainWindow::replaceCategories_MenuHandler(QMenu* menu, int modIndex) -{ - QList selected; - for (const QModelIndex &idx : ui->modList->selectionModel()->selectedRows()) { - selected.append(QPersistentModelIndex(idx)); - } - - if (selected.size() > 0) { - QStringList selectedMods; - int minRow = INT_MAX; - int maxRow = -1; - for (int i = 0; i < selected.size(); ++i) { - QModelIndex temp = mapToModel(m_OrganizerCore.modList(), selected.at(i)); - selectedMods.append(temp.data().toString()); - replaceCategoriesFromMenu(menu, mapToModel(m_OrganizerCore.modList(), selected.at(i)).row()); - if (temp.row() < minRow) minRow = temp.row(); - if (temp.row() > maxRow) maxRow = temp.row(); - } - - m_OrganizerCore.modList()->notifyChange(minRow, maxRow + 1); - - // find mods by their name because indices are invalidated - QAbstractItemModel *model = ui->modList->model(); - for (const QString &mod : selectedMods) { - QModelIndexList matches = model->match(model->index(0, 0), Qt::DisplayRole, mod, 1, - Qt::MatchFixedString | Qt::MatchCaseSensitive | Qt::MatchRecursive); - if (matches.size() > 0) { - ui->modList->selectionModel()->select(matches.at(0), QItemSelectionModel::Select | QItemSelectionModel::Rows); - } - } - } else { - //For single mod selections, just do a replace - replaceCategoriesFromMenu(menu, modIndex); - m_OrganizerCore.modList()->notifyChange(modIndex); - } - - refreshFilters(); -} - void MainWindow::saveArchiveList() { if (m_OrganizerCore.isArchivesInit()) { @@ -3703,7 +3664,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) int modIndex = ui->modList->indexAt(pos).data(ModList::IndexRole).toInt(); int contextColumn = contextIdx.column(); - ModListContextMenu menu(m_OrganizerCore, contextIdx, ui->modList); + ModListContextMenu menu(contextIdx, m_OrganizerCore, m_CategoryFactory, ui->modList); ModInfo::Ptr info = ModInfo::getByIndex(modIndex); std::vector flags = info->getFlags(); @@ -3718,29 +3679,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) // separator else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()){ - menu.addSeparator(); - QMenu *addRemoveCategoriesMenu = new QMenu(tr("Change Categories"), &menu); - populateMenuCategories(modIndex, addRemoveCategoriesMenu, 0); - connect(addRemoveCategoriesMenu, &QMenu::aboutToHide, [=]() { addRemoveCategories_MenuHandler(addRemoveCategoriesMenu, modIndex, contextIdx); }); - addMenuAsPushButton(&menu, addRemoveCategoriesMenu); - QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category"), &menu); - connect(primaryCategoryMenu, &QMenu::aboutToShow, [=]() { setPrimaryCategoryCandidates(primaryCategoryMenu, info); }); - addMenuAsPushButton(&menu, primaryCategoryMenu); - menu.addSeparator(); - menu.addAction(tr("Rename Separator..."), [=]() { renameMod_clicked(); }); - menu.addAction(tr("Remove Separator..."), [=]() { removeMod_clicked(modIndex); }); - menu.addSeparator(); - if (ui->modList->sortColumn() == ModList::COL_PRIORITY) { - menu.addMenu(menu.createSendToContextMenu()); - menu.addSeparator(); - } - menu.addAction(tr("Select Color..."), [=]() { setColor_clicked(modIndex); }); - - if (info->color().isValid()) { - menu.addAction(tr("Reset Color"), [=]() { resetColor_clicked(modIndex); }); - } - - menu.addSeparator(); } // foregin @@ -3864,9 +3802,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) } menu.addAction(tr("Open in Explorer"), [=]() { ui->modList->actions().openExplorer({ contextIdx }); }); - } - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end()) { QAction* infoAction = menu.addAction(tr("Information..."), [=]() { information_clicked(modIndex); }); menu.setDefaultAction(infoAction); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 49ecee32..440e39cc 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -152,6 +152,7 @@ public slots: void directory_refreshed(); void updatePluginCount(); + void refreshFilters(); signals: @@ -406,7 +407,6 @@ private slots: void setPrimaryCategoryCandidates(QMenu* menu, ModInfo::Ptr info); void addRemoveCategories_MenuHandler(QMenu* menu, int modIndex, const QModelIndex& rowIdx); - void replaceCategories_MenuHandler(QMenu* menu, int modIndex); void modInstalled(const QString &modName); @@ -426,7 +426,6 @@ private slots: void onRequestsChanged(const APIStats& stats, const APIUserAccount& user); void deselectFilters(); - void refreshFilters(); void onFiltersCriteria(const std::vector& filters); void onFiltersOptions( ModListSortProxy::FilterMode mode, ModListSortProxy::SeparatorsMode sep); diff --git a/src/modlist.h b/src/modlist.h index cabd1f32..e4f4dfab 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -131,6 +131,8 @@ public: void highlightMods(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry); +public: + /** * @brief Notify the mod list that the given mod has been installed. This is used * to notify the plugin that registered through onModInstalled(). diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index df80f0d9..05c2eebf 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -29,22 +29,126 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { view->enableAllVisible(); } - }); + }); addAction(tr("Disable all visible"), [=]() { if (QMessageBox::question(view, tr("Confirm"), tr("Really disable all visible mods?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { view->disableAllVisible(); } - }); + }); addAction(tr("Check for updates"), [=]() { view->actions().checkModsForUpdates(); }); addAction(tr("Refresh"), &core, &OrganizerCore::profileRefresh); addAction(tr("Export to csv..."), [=]() { view->actions().exportModListCSV(); }); } -ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* view) : + +ModListChangeCategoryMenu::ModListChangeCategoryMenu(CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent) + : QMenu(tr("Change Categories"), parent) +{ + populate(this, categories, mod); +} + +std::vector> ModListChangeCategoryMenu::categories() const +{ + return categories(this); +} + +std::vector> ModListChangeCategoryMenu::categories(const QMenu* menu) const +{ + std::vector> cats; + for (QAction* action : menu->actions()) { + if (action->menu() != nullptr) { + auto pcats = categories(action->menu()); + cats.insert(cats.end(), pcats.begin(), pcats.end()); + } + else { + QWidgetAction* widgetAction = qobject_cast(action); + if (widgetAction != nullptr) { + QCheckBox* checkbox = qobject_cast(widgetAction->defaultWidget()); + cats.emplace_back(widgetAction->data().toInt(), checkbox->isChecked()); + } + } + } + return cats; +} + +bool ModListChangeCategoryMenu::populate(QMenu* menu, CategoryFactory& factory, ModInfo::Ptr mod, int targetId) +{ + const std::set& categories = mod->getCategories(); + + bool childEnabled = false; + + for (unsigned int i = 1; i < factory.numCategories(); ++i) { + if (factory.getParentID(i) == targetId) { + QMenu* targetMenu = menu; + if (factory.hasChildren(i)) { + targetMenu = menu->addMenu(factory.getCategoryName(i).replace('&', "&&")); + } + + int id = factory.getCategoryID(i); + QScopedPointer checkBox(new QCheckBox(targetMenu)); + bool enabled = categories.find(id) != categories.end(); + checkBox->setText(factory.getCategoryName(i).replace('&', "&&")); + if (enabled) { + childEnabled = true; + } + checkBox->setChecked(enabled ? Qt::Checked : Qt::Unchecked); + + QScopedPointer checkableAction(new QWidgetAction(targetMenu)); + checkableAction->setDefaultWidget(checkBox.take()); + checkableAction->setData(id); + targetMenu->addAction(checkableAction.take()); + + if (factory.hasChildren(i)) { + if (populate(targetMenu, factory, mod, factory.getCategoryID(i)) || enabled) { + targetMenu->setIcon(QIcon(":/MO/gui/resources/check.png")); + } + } + } + } + return childEnabled; +} + +ModListPrimaryCategoryMenu::ModListPrimaryCategoryMenu(CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent) + : QMenu(tr("Primary Category"), parent) +{ + connect(this, &QMenu::aboutToShow, [=]() { populate(categories, mod); }); +} + +void ModListPrimaryCategoryMenu::populate(const CategoryFactory& factory, ModInfo::Ptr mod) +{ + clear(); + const std::set& categories = mod->getCategories(); + for (int categoryID : categories) { + int catIdx = factory.getCategoryIndex(categoryID); + QWidgetAction* action = new QWidgetAction(this); + try { + QRadioButton* categoryBox = new QRadioButton( + factory.getCategoryName(catIdx).replace('&', "&&"), + this); + connect(categoryBox, &QRadioButton::toggled, [mod, categoryID](bool enable) { + if (enable) { + mod->setPrimaryCategory(categoryID); + } + }); + categoryBox->setChecked(categoryID == mod->primaryCategory()); + action->setDefaultWidget(categoryBox); + } + catch (const std::exception& e) { + log::error("failed to create category checkbox: {}", e.what()); + } + + action->setData(categoryID); + addAction(action); + } +} + +ModListContextMenu::ModListContextMenu( + const QModelIndex& index, OrganizerCore& core, CategoryFactory& categories, ModListView* view) : QMenu(view) , m_core(core) - , m_index(index) + , m_categories(categories) + , m_index(index.model() == view->model() ? view->indexViewToModel(index) : index) , m_view(view) , m_actions(view->actions()) { @@ -55,7 +159,6 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i m_selected = { index }; } - QMenu* allMods = new ModListGlobalContextMenu(core, view, view); allMods->setTitle(tr("All Mods")); addMenu(allMods); @@ -96,6 +199,15 @@ ModListContextMenu::ModListContextMenu(OrganizerCore& core, const QModelIndex& i } } +void ModListContextMenu::addMenuAsPushButton(QMenu* menu) +{ + QPushButton* pushBtn = new QPushButton(menu->title()); + pushBtn->setMenu(menu); + QWidgetAction* action = new QWidgetAction(this); + action->setDefaultWidget(pushBtn); + addAction(action); +} + QMenu* ModListContextMenu::createSendToContextMenu() { QMenu* menu = new QMenu(m_view); @@ -120,7 +232,35 @@ void ModListContextMenu::addOverwriteActions(ModInfo::Ptr mod) void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) { + addSeparator(); + + // categories + ModListChangeCategoryMenu* categoriesMenu = new ModListChangeCategoryMenu(m_categories, mod, this); + connect(categoriesMenu, &QMenu::aboutToHide, [=]() { + m_actions.setCategories(m_selected, m_index, categoriesMenu->categories()); + }); + addMenuAsPushButton(categoriesMenu); + ModListPrimaryCategoryMenu* primaryCategoryMenu = new ModListPrimaryCategoryMenu(m_categories, mod, this); + addMenuAsPushButton(primaryCategoryMenu); + addSeparator(); + + + addAction(tr("Rename Separator..."), [=]() { m_actions.renameMod(m_index); }); + addAction(tr("Remove Separator..."), [=]() { m_actions.removeMods(m_selected); }); + addSeparator(); + + if (m_view->sortColumn() == ModList::COL_PRIORITY) { + addMenu(createSendToContextMenu()); + addSeparator(); + } + addAction(tr("Select Color..."), [=]() { m_actions.setColor(m_selected, m_index); }); + + if (mod->color().isValid()) { + addAction(tr("Reset Color"), [=]() { m_actions.resetColor(m_selected, m_index); }); + } + + addSeparator(); } void ModListContextMenu::addForeignActions(ModInfo::Ptr mod) diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 05e6b601..8452bc65 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -9,6 +9,7 @@ #include "modinfo.h" +class CategoryFactory; class ModListView; class ModListViewActions; class OrganizerCore; @@ -18,7 +19,47 @@ class ModListGlobalContextMenu : public QMenu Q_OBJECT public: - ModListGlobalContextMenu(OrganizerCore& core, ModListView* modListView, QWidget* parent = nullptr); + ModListGlobalContextMenu( + OrganizerCore& core, ModListView* modListView, QWidget* parent = nullptr); + +}; + +class ModListChangeCategoryMenu : public QMenu +{ + Q_OBJECT +public: + + ModListChangeCategoryMenu( + CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent = nullptr); + + // return a list of pair from the menu + // + std::vector> categories() const; + +private: + + // populate the tree with the category, using the enabled/disabled state from the + // given mod + // + bool populate(QMenu* menu, CategoryFactory& categories, ModInfo::Ptr mod, int targetId = 0); + + // internal implementation of categories() for recursion + // + std::vector> categories(const QMenu* menu) const; +}; + +class ModListPrimaryCategoryMenu : public QMenu +{ + Q_OBJECT +public: + + ModListPrimaryCategoryMenu(CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent = nullptr); + +private: + + // populate the categories + // + void populate(const CategoryFactory& categories, ModInfo::Ptr mod); }; @@ -30,7 +71,8 @@ public: // creates a new context menu, the given index is the one for the click and should be valid // - ModListContextMenu(OrganizerCore& core, const QModelIndex& index, ModListView* modListView); + ModListContextMenu( + const QModelIndex& index, OrganizerCore& core, CategoryFactory& categories, ModListView* modListView); public: // TODO: Move this to private when all is done @@ -38,6 +80,11 @@ public: // TODO: Move this to private when all is done // QMenu* createSendToContextMenu(); + // special menu for categories + // + void addMenuAsPushButton(QMenu* menu); + + // add actions/menus to this menu for each type of mod // void addOverwriteActions(ModInfo::Ptr mod); @@ -47,6 +94,7 @@ public: // TODO: Move this to private when all is done void addRegularActions(ModInfo::Ptr mod); OrganizerCore& m_core; + CategoryFactory& m_categories; QModelIndex m_index; QModelIndexList m_selected; ModListView* m_view; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 53c29a5d..9a00353f 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -506,6 +506,16 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const } } +void ModListViewActions::renameMod(const QModelIndex& index) const +{ + try { + m_view->edit(index); + } + catch (const std::exception& e) { + reportError(tr("failed to rename mod: %1").arg(e.what())); + } +} + void ModListViewActions::removeMods(const QModelIndexList& indices) const { const int max_items = 20; @@ -650,6 +660,94 @@ void ModListViewActions::visitWebPage(const QModelIndexList& indices) const } } +void ModListViewActions::setColor(const QModelIndexList& indices, const QModelIndex& refIndex) const +{ + auto& settings = m_core.settings(); + ModInfo::Ptr modInfo = ModInfo::getByIndex(refIndex.data(ModList::IndexRole).toInt()); + + QColorDialog dialog(m_view); + dialog.setOption(QColorDialog::ShowAlphaChannel); + + QColor currentColor = modInfo->color(); + if (currentColor.isValid()) { + dialog.setCurrentColor(currentColor); + } + else if (auto c = settings.colors().previousSeparatorColor()) { + dialog.setCurrentColor(*c); + } + + if (!dialog.exec()) + return; + + currentColor = dialog.currentColor(); + if (!currentColor.isValid()) + return; + + settings.colors().setPreviousSeparatorColor(currentColor); + + for (auto& idx : indices) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + info->setColor(currentColor); + } + +} + +void ModListViewActions::resetColor(const QModelIndexList& indices, const QModelIndex& refIndex) const +{ + for (auto& idx : indices) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + info->setColor(QColor()); + } + m_core.settings().colors().removePreviousSeparatorColor(); +} + +void ModListViewActions::setCategories(ModInfo::Ptr mod, const std::vector>& categories) const +{ + for (auto& [id, enabled] : categories) { + mod->setCategory(id, enabled); + } +} + +void ModListViewActions::setCategoriesIf(ModInfo::Ptr mod, ModInfo::Ptr ref, const std::vector>& categories) const +{ + for (auto& [id, enabled] : categories) { + if (ref->categorySet(id) != enabled) { + mod->setCategory(id, enabled); + } + } +} + +void ModListViewActions::setCategories(const QModelIndexList& selected, const QModelIndex& ref, + const std::vector>& categories) const +{ + ModInfo::Ptr refMod = ModInfo::getByIndex(ref.data(ModList::IndexRole).toInt()); + if (selected.size() > 1) { + + for (auto& idx : selected) { + if (idx.row() != ref.row()) { + setCategoriesIf( + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()), + refMod, categories); + } + } + setCategories(refMod, categories); + } + else if (!selected.isEmpty()) { + // for single mod selections, just do a replace + setCategories(refMod, categories); + } + + for (auto& idx : selected) { + m_core.modList()->notifyChange(idx.data(ModList::IndexRole).toInt()); + } + + // reset the selection manually - still needed + auto viewIndices = m_view->indexModelToView(selected); + for (auto& idx : viewIndices) { + m_view->selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); + } +} + void ModListViewActions::openExplorer(const QModelIndexList& index) const { for (auto& idx : index) { diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 05994813..33442ce7 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -60,13 +60,29 @@ public: void sendModsToPriority(const QModelIndexList& index) const; void sendModsToSeparator(const QModelIndexList& index) const; - // actions for most type of mods + // actions for most regular mods + // + void renameMod(const QModelIndex& index) const; void removeMods(const QModelIndexList& indices) const; void ignoreMissingData(const QModelIndexList& indices) const; void markConverted(const QModelIndexList& indices) const; void visitOnNexus(const QModelIndexList& indices) const; void visitWebPage(const QModelIndexList& indices) const; + // set/reset color of the given selection, using the given reference index (index + // at which the context menu was shown) + // + void setColor(const QModelIndexList& indices, const QModelIndex& refIndex) const; + void resetColor(const QModelIndexList& indices, const QModelIndex& refIndex) const; + + // set the category of the mod in the given list, using the given index as reference + // - the categories are set as-is on the refernce mod + // - for the other mods, the category is only set if the current state of the category + // on the reference is different + // + void setCategories(const QModelIndexList& selected, const QModelIndex& ref, + const std::vector>& categories) const; + // open the Windows explorer for the specified mods // void openExplorer(const QModelIndexList& index) const; @@ -89,8 +105,19 @@ signals: private: + // move the contents of the overwrite to the given path + // void moveOverwriteContentsTo(const QString& absolutePath) const; + // set the category of the given mod based on the given array + // + void setCategories(ModInfo::Ptr mod, const std::vector>& categories) const; + + // set the category of the given mod if the category from the reference mod does not match + // the one in the array of categories + // + void setCategoriesIf(ModInfo::Ptr mod, ModInfo::Ptr ref, const std::vector>& categories) const; + private: OrganizerCore& m_core; -- cgit v1.3.1 From 51281ac304f64170d310ae543d50879614c550d3 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 02:36:41 +0100 Subject: Clean visitOnX methods. --- src/modlistviewactions.cpp | 98 ++++++++++++++++++++++++---------------------- src/modlistviewactions.h | 1 + 2 files changed, 52 insertions(+), 47 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 9a00353f..f8fd0e4c 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -590,73 +590,77 @@ void ModListViewActions::markConverted(const QModelIndexList& indices) const void ModListViewActions::visitOnNexus(const QModelIndexList& indices) const { - if (indices.size() > 1) { - if (indices.size() > 10) { - if (QMessageBox::question(m_view, tr("Opening Nexus Links"), - tr("You are trying to open %1 links to Nexus Mods. Are you sure you want to do this?").arg(indices.size()), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } - int row_idx; - ModInfo::Ptr info; - QString gameName; - - for (auto& idx : indices) { - row_idx = idx.data(ModList::IndexRole).toInt(); - info = ModInfo::getByIndex(row_idx); - int modID = info->nexusId(); - gameName = info->gameName(); - if (modID > 0) { - shell::Open(QUrl(NexusInterface::instance().getModURL(modID, gameName))); - } - else { - log::error("mod '{}' has no nexus id", info->name()); - } + if (indices.size() > 10) { + if (QMessageBox::question(m_view, tr("Opening Nexus Links"), + tr("You are trying to open %1 links to Nexus Mods. Are you sure you want to do this?").arg(indices.size()), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; } } - else if (!indices.isEmpty()) { - int modID = indices[0].data(Qt::UserRole).toInt(); - QString gameName = indices[0].data(Qt::UserRole + 4).toString(); + + for (auto& idx : indices) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + int modID = info->nexusId(); + QString gameName = info->gameName(); if (modID > 0) { shell::Open(QUrl(NexusInterface::instance().getModURL(modID, gameName))); } else { - MessageDialog::showMessage(tr("Nexus ID for this mod is unknown"), m_view); + log::error("mod '{}' has no nexus id", info->name()); } } } void ModListViewActions::visitWebPage(const QModelIndexList& indices) const { - if (indices.size() > 1) { - if (indices.size() > 10) { - if (QMessageBox::question(m_view, tr("Opening Web Pages"), - tr("You are trying to open %1 Web Pages. Are you sure you want to do this?").arg(indices.size()), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } + if (indices.size() > 10) { + if (QMessageBox::question(m_view, tr("Opening Web Pages"), + tr("You are trying to open %1 Web Pages. Are you sure you want to do this?").arg(indices.size()), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; } - int row_idx; - ModInfo::Ptr info; - QString gameName; - for (auto& idx : indices) { - row_idx = idx.data(ModList::IndexRole).toInt(); - info = ModInfo::getByIndex(row_idx); + } - const auto url = info->parseCustomURL(); - if (url.isValid()) { - shell::Open(url); - } + for (auto& idx : indices) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + + const auto url = info->parseCustomURL(); + if (url.isValid()) { + shell::Open(url); + } + } +} + +void ModListViewActions::visitNexusOrWebPage(const QModelIndexList& indices) const +{ + if (indices.size() > 10) { + if (QMessageBox::question(m_view, tr("Opening Web Pages"), + tr("You are trying to open %1 Web Pages. Are you sure you want to do this?").arg(indices.size()), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; } } - else if (!indices.isEmpty()) { - ModInfo::Ptr info = ModInfo::getByIndex(indices[0].data(ModList::IndexRole).toInt()); + for (auto& idx : indices) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + if (!info) { + log::error("mod {} not found", idx.data(ModList::IndexRole).toInt()); + continue; + } + + int modID = info->nexusId(); + QString gameName = info->gameName(); const auto url = info->parseCustomURL(); - if (url.isValid()) { + + if (modID > 0) { + shell::Open(QUrl(NexusInterface::instance().getModURL(modID, gameName))); + } + else if (url.isValid()) { shell::Open(url); } + else { + log::error("mod '{}' has no valid link", info->name()); + } } } diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 33442ce7..52db019c 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -68,6 +68,7 @@ public: void markConverted(const QModelIndexList& indices) const; void visitOnNexus(const QModelIndexList& indices) const; void visitWebPage(const QModelIndexList& indices) const; + void visitNexusOrWebPage(const QModelIndexList& indices) const; // set/reset color of the given selection, using the given reference index (index // at which the context menu was shown) -- cgit v1.3.1 From 4f89665056b2256ca353bc27314cd025db2f554c Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 03:28:18 +0100 Subject: Move the regular context menu. --- src/mainwindow.cpp | 233 +-------------------------------------------- src/mainwindow.h | 6 -- src/modlistcontextmenu.cpp | 118 ++++++++++++++++++++++- src/modlistview.cpp | 14 --- src/modlistview.h | 5 - src/modlistviewactions.cpp | 232 +++++++++++++++++++++++++++++++++++++++++++- src/modlistviewactions.h | 19 +++- 7 files changed, 366 insertions(+), 261 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index cd31a0bf..1475be50 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -539,6 +539,7 @@ void MainWindow::setupModList() ui->modList->setup(m_OrganizerCore, actions, ui); connect(actions, &ModListViewActions::overwriteCleared, [=]() { scheduleCheckForProblems(); }); + connect(actions, &ModListViewActions::originModified, this, &MainWindow::originModified); connect(ui->modList, &ModListView::removeSelectedMods, [=]() { removeMod_clicked(-1); }); connect(m_OrganizerCore.modList(), &ModList::clearOverwrite, actions, &ModListViewActions::clearOverwrite); connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, [&]() { m_ArchiveListWriter.write(); }); @@ -2287,10 +2288,7 @@ void MainWindow::modInstalled(const QString &modName) } // force an update to happen - std::multimap IDs; - ModInfo::Ptr info = ModInfo::getByIndex(index); - IDs.insert(std::make_pair(info->gameName(), info->nexusId())); - modUpdateCheck(IDs); + ui->modList->actions().checkModsForUpdates({ m_OrganizerCore.modList()->index(index, 0) }); } void MainWindow::showMessage(const QString &message) @@ -3423,35 +3421,6 @@ void MainWindow::checkModsForUpdates() } } -void MainWindow::changeVersioningScheme(int modIndex) { - if (QMessageBox::question(this, tr("Continue?"), - tr("The versioning scheme decides which version is considered newer than another.\n" - "This function will guess the versioning scheme under the assumption that the installed version is outdated."), - QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) { - - ModInfo::Ptr info = ModInfo::getByIndex(modIndex); - - bool success = false; - - static VersionInfo::VersionScheme schemes[] = { VersionInfo::SCHEME_REGULAR, VersionInfo::SCHEME_DECIMALMARK, VersionInfo::SCHEME_NUMBERSANDLETTERS }; - - for (int i = 0; i < sizeof(schemes) / sizeof(VersionInfo::VersionScheme) && !success; ++i) { - VersionInfo verOld(info->version().canonicalString(), schemes[i]); - VersionInfo verNew(info->newestVersion().canonicalString(), schemes[i]); - if (verOld < verNew) { - info->setVersion(verOld); - info->setNewestVersion(verNew); - success = true; - } - } - if (!success) { - QMessageBox::information(this, tr("Sorry"), - tr("I don't know a versioning scheme where %1 is newer than %2.").arg(info->newestVersion().canonicalString()).arg(info->version().canonicalString()), - QMessageBox::Ok); - } - } -} - void MainWindow::ignoreUpdate(int modIndex) { QItemSelectionModel *selection = ui->modList->selectionModel(); @@ -3470,22 +3439,6 @@ void MainWindow::ignoreUpdate(int modIndex) } } -void MainWindow::checkModUpdates_clicked(int modIndex) -{ - std::multimap IDs; - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); - IDs.insert(std::make_pair(info->gameName(), info->nexusId())); - } - } else { - ModInfo::Ptr info = ModInfo::getByIndex(modIndex); - IDs.insert(std::make_pair(info->gameName(), info->nexusId())); - } - modUpdateCheck(IDs); -} - void MainWindow::unignoreUpdate(int modIndex) { QItemSelectionModel *selection = ui->modList->selectionModel(); @@ -3652,162 +3605,14 @@ void MainWindow::addPluginSendToContextMenu(QMenu *menu) void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) { try { - QTreeView *modList = findChild("modList"); - QModelIndex contextIdx = mapToModel(m_OrganizerCore.modList(), ui->modList->indexAt(pos)); if (!contextIdx.isValid()) { // no selection - ModListGlobalContextMenu(m_OrganizerCore, ui->modList).exec(modList->viewport()->mapToGlobal(pos)); + ModListGlobalContextMenu(m_OrganizerCore, ui->modList).exec(ui->modList->viewport()->mapToGlobal(pos)); } else { - int modIndex = ui->modList->indexAt(pos).data(ModList::IndexRole).toInt(); - int contextColumn = contextIdx.column(); - - ModListContextMenu menu(contextIdx, m_OrganizerCore, m_CategoryFactory, ui->modList); - - ModInfo::Ptr info = ModInfo::getByIndex(modIndex); - std::vector flags = info->getFlags(); - - // context menu for overwrites - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { - } - - // context menu for mod backups - else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) { - } - - // separator - else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()){ - } - - // foregin - else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - } - - // regular - else { - QMenu* addRemoveCategoriesMenu = new QMenu(tr("Change Categories"), &menu); - populateMenuCategories(modIndex, addRemoveCategoriesMenu, 0); - connect(addRemoveCategoriesMenu, &QMenu::aboutToHide, [=]() { addRemoveCategories_MenuHandler(addRemoveCategoriesMenu, modIndex, contextIdx); }); - addMenuAsPushButton(&menu, addRemoveCategoriesMenu); - - QMenu* primaryCategoryMenu = new QMenu(tr("Primary Category"), &menu); - connect(primaryCategoryMenu, &QMenu::aboutToShow, [=]() { setPrimaryCategoryCandidates(primaryCategoryMenu, info); }); - addMenuAsPushButton(&menu, primaryCategoryMenu); - - menu.addSeparator(); - - if (info->downgradeAvailable()) { - menu.addAction(tr("Change versioning scheme"), [=]() { changeVersioningScheme(modIndex); }); - } - - if (info->nexusId() > 0) - menu.addAction(tr("Force-check updates"), [=]() { checkModUpdates_clicked(modIndex); }); - if (info->updateIgnored()) { - menu.addAction(tr("Un-ignore update"), [=]() { unignoreUpdate(modIndex); }); - } - else { - if (info->updateAvailable() || info->downgradeAvailable()) { - menu.addAction(tr("Ignore update"), [=]() { ignoreUpdate(modIndex); }); - } - } - menu.addSeparator(); - - menu.addAction(tr("Enable selected"), [=]() { enableSelectedMods_clicked(); }); - menu.addAction(tr("Disable selected"), [=]() { disableSelectedMods_clicked(); }); - - menu.addSeparator(); - - if (ui->modList->sortColumn() == ModList::COL_PRIORITY) { - menu.addMenu(menu.createSendToContextMenu()); - menu.addSeparator(); - } - - menu.addAction(tr("Rename Mod..."), [=]() { renameMod_clicked(); }); - menu.addAction(tr("Reinstall Mod"), [=]() { reinstallMod_clicked(modIndex); }); - menu.addAction(tr("Remove Mod..."), [=]() { removeMod_clicked(modIndex); }); - menu.addAction(tr("Create Backup"), [=]() { backupMod_clicked(modIndex); }); - - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_HIDDEN_FILES) != flags.end()) { - menu.addAction(tr("Restore hidden files"), [=]() { restoreHiddenFiles_clicked(modIndex); }); - } - - menu.addSeparator(); - - if (contextColumn == ModList::COL_NOTES) { - menu.addAction(tr("Select Color..."), [=]() { setColor_clicked(modIndex); }); - if (info->color().isValid()) { - menu.addAction(tr("Reset Color"), [=]() { resetColor_clicked(modIndex); }); - } - menu.addSeparator(); - } - - if (info->nexusId() > 0 && Settings::instance().nexus().endorsementIntegration()) { - switch (info->endorsedState()) { - case EndorsedState::ENDORSED_TRUE: { - menu.addAction(tr("Un-Endorse"), [=]() { unendorse_clicked(); }); - } break; - case EndorsedState::ENDORSED_FALSE: { - menu.addAction(tr("Endorse"), [=]() { endorse_clicked(); }); - menu.addAction(tr("Won't endorse"), [=]() { dontendorse_clicked(modIndex); }); - } break; - case EndorsedState::ENDORSED_NEVER: { - menu.addAction(tr("Endorse"), [=]() { endorse_clicked(); }); - } break; - default: { - QAction *action = new QAction(tr("Endorsement state unknown"), &menu); - action->setEnabled(false); - menu.addAction(action); - } break; - } - } - - if (info->nexusId() > 0 && Settings::instance().nexus().trackedIntegration()) { - switch (info->trackedState()) { - case TrackedState::TRACKED_FALSE: { - menu.addAction(tr("Start tracking"), [=]() { track_clicked(); }); - } break; - case TrackedState::TRACKED_TRUE: { - menu.addAction(tr("Stop tracking"), [=]() { untrack_clicked(); }); - } break; - default: { - QAction *action = new QAction(tr("Tracked state unknown"), &menu); - action->setEnabled(false); - menu.addAction(action); - } break; - } - } - - menu.addSeparator(); - - std::vector flags = info->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { - menu.addAction(tr("Ignore missing data"), [=]() { ignoreMissingData_clicked(modIndex); }); - } - - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { - menu.addAction(tr("Mark as converted/working"), [=]() { markConverted_clicked(modIndex); }); - } - - menu.addSeparator(); - - if (info->nexusId() > 0) { - menu.addAction(tr("Visit on Nexus"), [=]() { visitOnNexus_clicked(modIndex); }); - } - - const auto url = info->parseCustomURL(); - if (url.isValid()) { - menu.addAction(tr("Visit on %1").arg(url.host()), [=]() { visitWebPage_clicked(modIndex); }); - } - - menu.addAction(tr("Open in Explorer"), [=]() { ui->modList->actions().openExplorer({ contextIdx }); }); - - QAction* infoAction = menu.addAction(tr("Information..."), [=]() { information_clicked(modIndex); }); - menu.setDefaultAction(infoAction); - } - - menu.exec(modList->viewport()->mapToGlobal(pos)); + ModListContextMenu(contextIdx, m_OrganizerCore, m_CategoryFactory, ui->modList).exec(ui->modList->viewport()->mapToGlobal(pos)); } } catch (const std::exception &e) { reportError(tr("Exception: ").arg(e.what())); @@ -4091,18 +3896,6 @@ void MainWindow::sendSelectedPluginsToPriority_clicked() m_OrganizerCore.pluginList()->sendToPriority(ui->espList->selectionModel(), newPriority); } - -void MainWindow::enableSelectedMods_clicked() -{ - ui->modList->enableSelected(); -} - - -void MainWindow::disableSelectedMods_clicked() -{ - ui->modList->disableSelected(); -} - void MainWindow::updateAvailable() { ui->actionUpdate->setEnabled(true); @@ -4166,24 +3959,6 @@ void MainWindow::actionWontEndorseMO() } } -void MainWindow::modUpdateCheck(std::multimap IDs) -{ - if (m_OrganizerCore.settings().network().offlineMode()) { - return; - } - - if (NexusInterface::instance().getAccessManager()->validated()) { - ModInfo::manualUpdateCheck(this, IDs); - } else { - QString apiKey; - if (GlobalSettings::nexusApiKey(apiKey)) { - m_OrganizerCore.doAfterLogin([=]() { this->modUpdateCheck(IDs); }); - NexusInterface::instance().getAccessManager()->apiCheck(apiKey); - } else - log::warn("{}", tr("You are not currently authenticated with Nexus. Please do so under Settings -> Nexus.")); - } -} - void MainWindow::toggleMO2EndorseState() { const auto& s = m_OrganizerCore.settings(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 440e39cc..61bd9326 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -370,8 +370,6 @@ private slots: void openPluginOriginExplorer_clicked(); void openOriginInformation_clicked(); void information_clicked(int modIndex); - void enableSelectedMods_clicked(); - void disableSelectedMods_clicked(); // data-tree context menu // pluginlist context menu @@ -410,8 +408,6 @@ private slots: void modInstalled(const QString &modName); - void modUpdateCheck(std::multimap IDs); - void finishUpdateInfo(); void nxmEndorsementsAvailable(QVariant userData, QVariant resultData, int); @@ -487,8 +483,6 @@ private slots: void removeFromToolbar(QAction* action); void overwriteClosed(int); - void changeVersioningScheme(int modIndex); - void checkModUpdates_clicked(int modIndex); void ignoreUpdate(int modIndex); void unignoreUpdate(int modIndex); diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 05c2eebf..303362ba 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -257,7 +257,7 @@ void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) addAction(tr("Select Color..."), [=]() { m_actions.setColor(m_selected, m_index); }); if (mod->color().isValid()) { - addAction(tr("Reset Color"), [=]() { m_actions.resetColor(m_selected, m_index); }); + addAction(tr("Reset Color"), [=]() { m_actions.resetColor(m_selected); }); } addSeparator(); @@ -297,5 +297,121 @@ void ModListContextMenu::addBackupActions(ModInfo::Ptr mod) void ModListContextMenu::addRegularActions(ModInfo::Ptr mod) { + auto flags = mod->getFlags(); + + // categories + ModListChangeCategoryMenu* categoriesMenu = new ModListChangeCategoryMenu(m_categories, mod, this); + connect(categoriesMenu, &QMenu::aboutToHide, [=]() { + m_actions.setCategories(m_selected, m_index, categoriesMenu->categories()); + }); + addMenuAsPushButton(categoriesMenu); + + ModListPrimaryCategoryMenu* primaryCategoryMenu = new ModListPrimaryCategoryMenu(m_categories, mod, this); + addMenuAsPushButton(primaryCategoryMenu); + addSeparator(); + + if (mod->downgradeAvailable()) { + addAction(tr("Change versioning scheme"), [=]() { m_actions.changeVersioningScheme(m_index); }); + } + + if (mod->nexusId() > 0) + addAction(tr("Force-check updates"), [=]() { m_actions.checkModsForUpdates(m_selected); }); + if (mod->updateIgnored()) { + addAction(tr("Un-ignore update"), [=]() { m_actions.setIgnoreUpdate(m_selected, false); }); + } + else { + if (mod->updateAvailable() || mod->downgradeAvailable()) { + addAction(tr("Ignore update"), [=]() { m_actions.setIgnoreUpdate(m_selected, true); }); + } + } + addSeparator(); + + addAction(tr("Enable selected"), [=]() { m_core.modList()->setActive(m_selected, true); }); + addAction(tr("Disable selected"), [=]() { m_core.modList()->setActive(m_selected, false); }); + + addSeparator(); + + + if (m_view->sortColumn() == ModList::COL_PRIORITY) { + addMenu(createSendToContextMenu()); + addSeparator(); + } + + addAction(tr("Rename Mod..."), [=]() { m_actions.renameMod(m_index); }); + addAction(tr("Reinstall Mod"), [=]() { m_actions.reinstallMod(m_index); }); + addAction(tr("Remove Mod..."), [=]() { m_actions.removeMods(m_selected); }); + addAction(tr("Create Backup"), [=]() { m_actions.createBackup(m_index); }); + + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_HIDDEN_FILES) != flags.end()) { + addAction(tr("Restore hidden files"), [=]() { m_actions.restoreHiddenFiles(m_selected); }); + } + + addSeparator(); + + if (m_index.column() == ModList::COL_NOTES) { + addAction(tr("Select Color..."), [=]() { m_actions.setColor(m_selected, m_index); }); + if (mod->color().isValid()) { + addAction(tr("Reset Color"), [=]() { m_actions.resetColor(m_selected); }); + } + addSeparator(); + } + + if (mod->nexusId() > 0 && Settings::instance().nexus().endorsementIntegration()) { + switch (mod->endorsedState()) { + case EndorsedState::ENDORSED_TRUE: { + addAction(tr("Un-Endorse"), [=]() { m_actions.setEndorsed(m_selected, false); }); + } break; + case EndorsedState::ENDORSED_FALSE: { + addAction(tr("Endorse"), [=]() { m_actions.setEndorsed(m_selected, true); }); + addAction(tr("Won't endorse"), [=]() { m_actions.willNotEndorsed(m_selected); }); + } break; + case EndorsedState::ENDORSED_NEVER: { + addAction(tr("Endorse"), [=]() { m_actions.setEndorsed(m_selected, true); }); + } break; + default: { + QAction* action = new QAction(tr("Endorsement state unknown"), this); + action->setEnabled(false); + addAction(action); + } break; + } + } + + if (mod->nexusId() > 0 && Settings::instance().nexus().trackedIntegration()) { + switch (mod->trackedState()) { + case TrackedState::TRACKED_FALSE: { + addAction(tr("Start tracking"), [=]() { m_actions.setTracked(m_selected, true); }); + } break; + case TrackedState::TRACKED_TRUE: { + addAction(tr("Stop tracking"), [=]() { m_actions.setTracked(m_selected, false); }); + } break; + default: { + QAction* action = new QAction(tr("Tracked state unknown"), this); + action->setEnabled(false); + addAction(action); + } break; + } + } + + addSeparator(); + + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { + addAction(tr("Ignore missing data"), [=]() { m_actions.ignoreMissingData(m_selected); }); + } + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { + addAction(tr("Mark as converted/working"), [=]() { m_actions.markConverted(m_selected); }); + } + + addSeparator(); + + if (mod->nexusId() > 0) { + addAction(tr("Visit on Nexus"), [=]() { m_actions.visitOnNexus(m_selected); }); + } + + const auto url = mod->parseCustomURL(); + if (url.isValid()) { + addAction(tr("Visit on %1").arg(url.host()), [=]() { m_actions.visitWebPage(m_selected); }); + } + + addAction(tr("Open in Explorer"), [=]() { m_actions.openExplorer(m_selected); }); } diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 8c826882..cf35abdd 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -196,20 +196,6 @@ void ModListView::disableAllVisible() m_core->modList()->setActive(indexViewToModel(allIndex(model())), false); } -void ModListView::enableSelected() -{ - if (selectionModel()->hasSelection()) { - m_core->modList()->setActive(indexViewToModel(selectionModel()->selectedRows()), true); - } -} - -void ModListView::disableSelected() -{ - if (selectionModel()->hasSelection()) { - m_core->modList()->setActive(indexViewToModel(selectionModel()->selectedRows()), false); - } -} - void ModListView::setFilterCriteria(const std::vector& criteria) { m_sortProxy->setCriteria(criteria); diff --git a/src/modlistview.h b/src/modlistview.h index 0f131631..4f27769c 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -91,11 +91,6 @@ public slots: void enableAllVisible(); void disableAllVisible(); - // enable/disable all selected mods - // - void enableSelected(); - void disableSelected(); - // set the filter criteria/options for mods // void setFilterCriteria(const std::vector& criteria); diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index f8fd0e4c..5b9c07bc 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -193,6 +193,36 @@ void ModListViewActions::checkModsForUpdates() const } } +void ModListViewActions::checkModsForUpdates(std::multimap const& IDs) const +{ + if (m_core.settings().network().offlineMode()) { + return; + } + + if (NexusInterface::instance().getAccessManager()->validated()) { + ModInfo::manualUpdateCheck(m_main, IDs); + } + else { + QString apiKey; + if (GlobalSettings::nexusApiKey(apiKey)) { + m_core.doAfterLogin([=]() { checkModsForUpdates(IDs); }); + NexusInterface::instance().getAccessManager()->apiCheck(apiKey); + } + else + log::warn("{}", tr("You are not currently authenticated with Nexus. Please do so under Settings -> Nexus.")); + } +} + +void ModListViewActions::checkModsForUpdates(const QModelIndexList& indices) const +{ + std::multimap ids; + for (auto& idx : indices) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + ids.insert(std::make_pair(info->gameName(), info->nexusId())); + } + checkModsForUpdates(ids); +} + void ModListViewActions::exportModListCSV() const { QDialog selection(m_view); @@ -509,7 +539,7 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const void ModListViewActions::renameMod(const QModelIndex& index) const { try { - m_view->edit(index); + m_view->edit(m_view->indexModelToView(index)); } catch (const std::exception& e) { reportError(tr("failed to rename mod: %1").arg(e.what())); @@ -578,13 +608,52 @@ void ModListViewActions::ignoreMissingData(const QModelIndexList& indices) const } } +void ModListViewActions::setIgnoreUpdate(const QModelIndexList& indices, bool ignore) const +{ + for (auto& idx : indices) { + int modIdx = idx.data(ModList::IndexRole).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(modIdx); + info->ignoreUpdate(ignore); + m_core.modList()->notifyChange(modIdx); + } +} + +void ModListViewActions::changeVersioningScheme(const QModelIndex& index) const { + if (QMessageBox::question(m_view, tr("Continue?"), + tr("The versioning scheme decides which version is considered newer than another.\n" + "This function will guess the versioning scheme under the assumption that the installed version is outdated."), + QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) { + + ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + + bool success = false; + + static VersionInfo::VersionScheme schemes[] = { VersionInfo::SCHEME_REGULAR, VersionInfo::SCHEME_DECIMALMARK, VersionInfo::SCHEME_NUMBERSANDLETTERS }; + + for (int i = 0; i < sizeof(schemes) / sizeof(VersionInfo::VersionScheme) && !success; ++i) { + VersionInfo verOld(info->version().canonicalString(), schemes[i]); + VersionInfo verNew(info->newestVersion().canonicalString(), schemes[i]); + if (verOld < verNew) { + info->setVersion(verOld); + info->setNewestVersion(verNew); + success = true; + } + } + if (!success) { + QMessageBox::information(m_view, tr("Sorry"), + tr("I don't know a versioning scheme where %1 is newer than %2.").arg(info->newestVersion().canonicalString()).arg(info->version().canonicalString()), + QMessageBox::Ok); + } + } +} + void ModListViewActions::markConverted(const QModelIndexList& indices) const { for (auto& idx : indices) { - int row_idx = idx.data(ModList::IndexRole).toInt(); - ModInfo::Ptr info = ModInfo::getByIndex(row_idx); + int modIdx = idx.data(ModList::IndexRole).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(modIdx); info->markConverted(true); - m_core.modList()->notifyChange(row_idx); + m_core.modList()->notifyChange(modIdx); } } @@ -664,6 +733,159 @@ void ModListViewActions::visitNexusOrWebPage(const QModelIndexList& indices) con } } +void ModListViewActions::reinstallMod(const QModelIndex& index) const +{ + ModInfo::Ptr modInfo = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + QString installationFile = modInfo->installationFile(); + if (installationFile.length() != 0) { + QString fullInstallationFile; + QFileInfo fileInfo(installationFile); + if (fileInfo.isAbsolute()) { + if (fileInfo.exists()) { + fullInstallationFile = installationFile; + } + else { + fullInstallationFile = m_core.downloadManager()->getOutputDirectory() + "/" + fileInfo.fileName(); + } + } + else { + fullInstallationFile = m_core.downloadManager()->getOutputDirectory() + "/" + installationFile; + } + if (QFile::exists(fullInstallationFile)) { + m_core.installMod(fullInstallationFile, true, modInfo, modInfo->name()); + } + else { + QMessageBox::information(m_view, tr("Failed"), tr("Installation file no longer exists")); + } + } + else { + QMessageBox::information(m_view, tr("Failed"), + tr("Mods installed with old versions of MO can't be reinstalled in this way.")); + } +} + +void ModListViewActions::createBackup(const QModelIndex& index) const +{ + ModInfo::Ptr modInfo = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); + QString backupDirectory = m_core.installationManager()->generateBackupName(modInfo->absolutePath()); + if (!copyDir(modInfo->absolutePath(), backupDirectory, false)) { + QMessageBox::information(m_view, tr("Failed"), + tr("Failed to create backup.")); + } + m_core.refresh(); + m_view->updateModCount(); +} + +void ModListViewActions::restoreHiddenFiles(const QModelIndexList& indices) const +{ + const int max_items = 20; + + QFlags flags = FileRenamer::UNHIDE; + flags |= FileRenamer::MULTIPLE; + + FileRenamer renamer(m_view, flags); + + FileRenamer::RenameResults result = FileRenamer::RESULT_OK; + + // multi selection + if (indices.size() > 1) { + + QStringList modNames; + for (auto& idx : indices) { + + ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + const auto flags = modInfo->getFlags(); + + if (!modInfo->isRegular() || + std::find(flags.begin(), flags.end(), ModInfo::FLAG_HIDDEN_FILES) == flags.end()) { + continue; + } + + modNames.append(idx.data(Qt::DisplayRole).toString()); + } + + QString mods = "
  • " + modNames.mid(0, max_items).join("
  • ") + "
  • "; + if (modNames.size() > max_items) { + mods += "
  • ...
  • "; + } + + if (QMessageBox::question(m_view, tr("Confirm"), + tr("Restore all hidden files in the following mods?
      %1
    ").arg(mods), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + + for (auto& idx : indices) { + + ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + + const auto flags = modInfo->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_HIDDEN_FILES) != flags.end()) { + const QString modDir = modInfo->absolutePath(); + + auto partialResult = restoreHiddenFilesRecursive(renamer, modDir); + + if (partialResult == FileRenamer::RESULT_CANCEL) { + result = FileRenamer::RESULT_CANCEL; + break; + } + emit originModified((m_core.directoryStructure()->getOriginByName( + ToWString(modInfo->internalName()))).getID()); + } + } + } + } + else if (!indices.isEmpty()) { + //single selection + ModInfo::Ptr modInfo = ModInfo::getByIndex(indices[0].data(ModList::IndexRole).toInt()); + const QString modDir = modInfo->absolutePath(); + + if (QMessageBox::question(m_view, tr("Are you sure?"), + tr("About to restore all hidden files in:\n") + modInfo->name(), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { + + result = restoreHiddenFilesRecursive(renamer, modDir); + + emit originModified((m_core.directoryStructure()->getOriginByName( + ToWString(modInfo->internalName()))).getID()); + } + } + + if (result == FileRenamer::RESULT_CANCEL) { + log::debug("Restoring hidden files operation cancelled"); + } + else { + log::debug("Finished restoring hidden files"); + } +} + +void ModListViewActions::setTracked(const QModelIndexList& indices, bool tracked) const +{ + m_core.loggedInAction(m_view, [=] { + for (auto& idx : indices) { + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->track(tracked); + } + }); +} + +void ModListViewActions::setEndorsed(const QModelIndexList& indices, bool endorsed) const +{ + m_core.loggedInAction(m_view, [=] { + if (indices.size() > 1) { + MessageDialog::showMessage(tr("Endorsing multiple mods will take a while. Please wait..."), m_view); + } + + for (auto& idx : indices) { + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->endorse(endorsed); + } + }); +} + +void ModListViewActions::willNotEndorsed(const QModelIndexList& indices) const +{ + for (auto& idx : indices) { + ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->setNeverEndorse(); + } +} + void ModListViewActions::setColor(const QModelIndexList& indices, const QModelIndex& refIndex) const { auto& settings = m_core.settings(); @@ -696,7 +918,7 @@ void ModListViewActions::setColor(const QModelIndexList& indices, const QModelIn } -void ModListViewActions::resetColor(const QModelIndexList& indices, const QModelIndex& refIndex) const +void ModListViewActions::resetColor(const QModelIndexList& indices) const { for (auto& idx : indices) { ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 52db019c..26efde47 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -42,6 +42,7 @@ public: // check all mods for update // void checkModsForUpdates() const; + void checkModsForUpdates(const QModelIndexList& indices) const; // start the "Export Mod List" dialog // @@ -65,16 +66,24 @@ public: void renameMod(const QModelIndex& index) const; void removeMods(const QModelIndexList& indices) const; void ignoreMissingData(const QModelIndexList& indices) const; + void setIgnoreUpdate(const QModelIndexList& indices, bool ignore) const; + void changeVersioningScheme(const QModelIndex& indices) const; void markConverted(const QModelIndexList& indices) const; void visitOnNexus(const QModelIndexList& indices) const; void visitWebPage(const QModelIndexList& indices) const; void visitNexusOrWebPage(const QModelIndexList& indices) const; + void reinstallMod(const QModelIndex& index) const; + void createBackup(const QModelIndex& index) const; + void restoreHiddenFiles(const QModelIndexList& indices) const; + void setTracked(const QModelIndexList& indices, bool tracked) const; + void setEndorsed(const QModelIndexList& indices, bool endorsed) const; + void willNotEndorsed(const QModelIndexList& indices) const; // set/reset color of the given selection, using the given reference index (index // at which the context menu was shown) // void setColor(const QModelIndexList& indices, const QModelIndex& refIndex) const; - void resetColor(const QModelIndexList& indices, const QModelIndex& refIndex) const; + void resetColor(const QModelIndexList& indices) const; // set the category of the mod in the given list, using the given index as reference // - the categories are set as-is on the refernce mod @@ -104,6 +113,10 @@ signals: // void overwriteCleared() const; + // emitted when the origin of a file is modified + // + void originModified(int originId) const; + private: // move the contents of the overwrite to the given path @@ -119,6 +132,10 @@ private: // void setCategoriesIf(ModInfo::Ptr mod, ModInfo::Ptr ref, const std::vector>& categories) const; + // check the given mods from update, the map should map game names to nexus ID + // + void checkModsForUpdates(std::multimap const& IDs) const; + private: OrganizerCore& m_core; -- cgit v1.3.1 From 48d92fbd9f40eb722fa0bda9043bf6725eb5a083 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 12:02:26 +0100 Subject: Fix originModified signal handling. --- src/modlistviewactions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 5b9c07bc..97fb03ae 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -434,7 +434,7 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in modInfo->saveMeta(); ModInfoDialog dialog(m_main, &m_core, &m_core.pluginContainer(), modInfo); - connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); + connect(&dialog, &ModInfoDialog::originModified, this, &ModListViewActions::originModified); //Open the tab first if we want to use the standard indexes of the tabs. if (tab != ModInfoTabIDs::None) { -- cgit v1.3.1 From fcf0aff98b43c9cb3b64c01cd03693e55f82fba7 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 12:04:17 +0100 Subject: Replace hasFlag by isX. --- src/modlistviewactions.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 97fb03ae..2b5bf026 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -447,8 +447,7 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in m_core.modList()->modInfoChanged(modInfo); } - if (m_core.currentProfile()->modEnabled(modIndex) - && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { + if (m_core.currentProfile()->modEnabled(modIndex) && !modInfo->isForeign()) { FilesOrigin& origin = m_core.directoryStructure()->getOriginByName(ToWString(modInfo->name())); origin.enable(false); @@ -497,7 +496,7 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const for (auto iter = indexesByPriority.begin(); iter != indexesByPriority.end(); iter++) { if ((iter->second != UINT_MAX)) { ModInfo::Ptr modInfo = ModInfo::getByIndex(iter->second); - if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { + if (modInfo->isSeparator()) { separators << modInfo->name().chopped(10); // Chops the "_separator" away from the name } } @@ -1061,7 +1060,7 @@ void ModListViewActions::moveOverwriteContentToExistingMod() const for (auto& iter : indexesByPriority) { if ((iter.second != UINT_MAX)) { ModInfo::Ptr modInfo = ModInfo::getByIndex(iter.second); - if (!modInfo->hasFlag(ModInfo::FLAG_SEPARATOR) && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN) && !modInfo->hasFlag(ModInfo::FLAG_OVERWRITE)) { + if (!modInfo->isSeparator() && !modInfo->isForeign() && !modInfo->isOverwrite()) { mods << modInfo->name(); } } -- cgit v1.3.1 From a105e4c8c881ac720c41ef342769adee14caca47 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 12:34:17 +0100 Subject: Move more stuff from MainWindow. Minor improvements for prev/next button in ModInfoDialog. --- src/mainwindow.cpp | 29 ---------------------------- src/mainwindow.h | 4 ---- src/modinfodialog.cpp | 48 +++++++++++++++++++++++++++++----------------- src/modinfodialog.h | 16 ++++++++++------ src/modlistview.cpp | 20 +++++++++++++++---- src/modlistview.h | 8 ++++---- src/modlistviewactions.cpp | 7 ++++++- src/organizercore.cpp | 1 - 8 files changed, 66 insertions(+), 67 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2ca15b00..3d509379 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -48,7 +48,6 @@ along with Mod Organizer. If not, see . #include "categories.h" #include "categoriesdialog.h" #include "genericicondelegate.h" -#include "modinfodialog.h" #include "overwriteinfodialog.h" #include "downloadlist.h" #include "downloadlistwidget.h" @@ -2377,16 +2376,6 @@ void MainWindow::windowTutorialFinished(const QString &windowName) m_OrganizerCore.settings().interface().setTutorialCompleted(windowName); } -void MainWindow::overwriteClosed(int) -{ - OverwriteInfoDialog *dialog = this->findChild("__overwriteDialog"); - if (dialog != nullptr) { - m_OrganizerCore.modList()->modInfoChanged(dialog->modInfo()); - dialog->deleteLater(); - } - m_OrganizerCore.refreshDirectoryStructure(); -} - void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) { ui->modList->actions().displayModInformation(modInfo, modIndex, tabID); @@ -2402,24 +2391,6 @@ void MainWindow::setWindowEnabled(bool enabled) setEnabled(enabled); } -ModInfo::Ptr MainWindow::nextModInList(int modIndex) -{ - modIndex = ui->modList->nextMod(modIndex); - if (modIndex == -1) { - return {}; - } - return ModInfo::getByIndex(modIndex); -} - -ModInfo::Ptr MainWindow::previousModInList(int modIndex) -{ - modIndex = ui->modList->prevMod(modIndex); - if (modIndex == -1) { - return {}; - } - return ModInfo::getByIndex(modIndex); -} - void MainWindow::openPluginOriginExplorer_clicked() { QItemSelectionModel *selection = ui->espList->selectionModel(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 1beed2f2..d80f4f63 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -142,9 +142,6 @@ public: virtual MOBase::DelayedFileWriterBase &archivesWriter() override { return m_ArchiveListWriter; } - ModInfo::Ptr nextModInList(int modIndex); - ModInfo::Ptr previousModInList(int modIndex); - public slots: void esplist_changed(); void refresherProgress(const DirectoryRefreshProgress* p); @@ -428,7 +425,6 @@ private slots: void toolBar_customContextMenuRequested(const QPoint &point); void removeFromToolbar(QAction* action); - void overwriteClosed(int); void about(); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index cb282195..54c97406 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -21,7 +21,7 @@ along with Mod Organizer. If not, see . #include "ui_modinfodialog.h" #include "plugincontainer.h" #include "organizercore.h" -#include "mainwindow.h" +#include "modlistview.h" #include "modinfodialogtextfiles.h" #include "modinfodialogimages.h" #include "modinfodialogesps.h" @@ -176,11 +176,14 @@ bool ModInfoDialog::TabInfo::isVisible() const ModInfoDialog::ModInfoDialog( - MainWindow* mw, OrganizerCore* core, PluginContainer* plugin, - ModInfo::Ptr mod) : - TutorableDialog("ModInfoDialog", mw), - ui(new Ui::ModInfoDialog), m_mainWindow(mw), - m_core(core), m_plugin(plugin), m_initialTab(ModInfoTabIDs::None), + OrganizerCore& core, PluginContainer& plugin, + ModInfo::Ptr mod, ModListView* modListView) : + TutorableDialog("ModInfoDialog", modListView), + ui(new Ui::ModInfoDialog), + m_core(core), + m_plugin(plugin), + m_modListView(modListView), + m_initialTab(ModInfoTabIDs::None), m_arrangingTabs(false) { ui->setupUi(this); @@ -204,7 +207,7 @@ template std::unique_ptr createTab(ModInfoDialog& d, ModInfoTabIDs id) { return std::make_unique(ModInfoDialogTabContext( - *d.m_core, *d.m_plugin, &d, d.ui.get(), id, d.m_mod, d.getOrigin())); + d.m_core, d.m_plugin, &d, d.ui.get(), id, d.m_mod, d.getOrigin())); } void ModInfoDialog::createTabs() @@ -269,7 +272,7 @@ int ModInfoDialog::exec() update(true); if (noCustomTabRequested) { - m_core->settings().widgets().restoreIndex(ui->tabWidget); + m_core.settings().widgets().restoreIndex(ui->tabWidget); } const int r = TutorableDialog::exec(); @@ -430,7 +433,7 @@ void ModInfoDialog::reAddTabs( Q_ASSERT(visibility.size() == m_tabs.size()); // ordered tab names from settings - const auto orderedNames = m_core->settings().geometry().modInfoTabOrder(); + const auto orderedNames = m_core.settings().geometry().modInfoTabOrder(); // whether the tabs can be sorted // @@ -586,7 +589,7 @@ void ModInfoDialog::feedFiles(std::vector& interestedTabs) void ModInfoDialog::setTabsColors() { - const auto p = m_mainWindow->palette(); + const auto p = m_modListView->parentWidget()->palette(); for (const auto& tabInfo : m_tabs) { if (!tabInfo.isVisible()) { @@ -619,7 +622,7 @@ void ModInfoDialog::switchToTab(ModInfoTabIDs id) MOShared::FilesOrigin* ModInfoDialog::getOrigin() { - auto* ds = m_core->directoryStructure(); + auto* ds = m_core.directoryStructure(); if (!ds->originExists(m_mod->name().toStdWString())) { return nullptr; @@ -639,7 +642,7 @@ void ModInfoDialog::saveState() const // save state for each tab for (const auto& tabInfo : m_tabs) { - tabInfo.tab->saveState(m_core->settings()); + tabInfo.tab->saveState(m_core.settings()); } } @@ -650,7 +653,7 @@ void ModInfoDialog::restoreState() // restore state for each tab for (const auto& tabInfo : m_tabs) { - tabInfo.tab->restoreState(m_core->settings()); + tabInfo.tab->restoreState(m_core.settings()); } } @@ -678,9 +681,9 @@ void ModInfoDialog::saveTabOrder() const names += ui->tabWidget->widget(i)->objectName(); } - m_core->settings().geometry().setModInfoTabOrder(names); + m_core.settings().geometry().setModInfoTabOrder(names); // save last opened index - m_core->settings().widgets().saveIndex(ui->tabWidget); + m_core.settings().widgets().saveIndex(ui->tabWidget); } void ModInfoDialog::onOriginModified(int originID) @@ -776,22 +779,31 @@ void ModInfoDialog::onTabMoved() void ModInfoDialog::onNextMod() { - auto mod = m_mainWindow->nextModInList(ModInfo::getIndex(m_mod->name())); + auto index = m_modListView->nextMod(ModInfo::getIndex(m_mod->name())); + if (!index) { + return; + } + auto mod = ModInfo::getByIndex(*index); if (!mod || mod == m_mod) { return; } setMod(mod); update(); + + emit modChanged(*index); } void ModInfoDialog::onPreviousMod() { - auto mod = m_mainWindow->previousModInList(ModInfo::getIndex(m_mod->name())); - if (!mod || mod == m_mod) { + auto index = m_modListView->prevMod(ModInfo::getIndex(m_mod->name())); + if (!index) { return; } + auto mod = ModInfo::getByIndex(*index); setMod(mod); update(); + + emit modChanged(*index); } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 48680ca4..a3b6ffdb 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -33,7 +33,7 @@ class PluginContainer; class OrganizerCore; class Settings; class ModInfoDialogTab; -class MainWindow; +class ModListView; /** * this is a larger dialog used to visualise information about the mod. @@ -52,8 +52,8 @@ class ModInfoDialog : public MOBase::TutorableDialog public: ModInfoDialog( - MainWindow* mw, OrganizerCore* core, PluginContainer* plugin, - ModInfo::Ptr mod); + OrganizerCore& core, PluginContainer& plugin, + ModInfo::Ptr mod, ModListView* view); ~ModInfoDialog(); @@ -71,6 +71,10 @@ signals: // void originModified(int originID); + // emitted when the mod of the dialog is changed + // + void modChanged(unsigned int modIndex); + protected: // forwards to tryClose() // @@ -115,10 +119,10 @@ private: }; std::unique_ptr ui; - MainWindow* m_mainWindow; + OrganizerCore& m_core; + PluginContainer& m_plugin; + ModListView* m_modListView; ModInfo::Ptr m_mod; - OrganizerCore* m_core; - PluginContainer* m_plugin; std::vector m_tabs; // initial tab requested by the main window when the dialog is opened; whether diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 40959e6d..f2b83beb 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -128,7 +128,7 @@ ModListViewActions& ModListView::actions() const return *m_actions; } -int ModListView::nextMod(int modIndex) const +std::optional ModListView::nextMod(unsigned int modIndex) const { const QModelIndex start = indexModelToView(m_core->modList()->index(modIndex, 0)); @@ -156,10 +156,10 @@ int ModListView::nextMod(int modIndex) const return modIndex; } - return -1; + return {}; } -int ModListView::prevMod(int modIndex) const +std::optional ModListView::prevMod(unsigned int modIndex) const { const QModelIndex start = indexModelToView(m_core->modList()->index(modIndex, 0)); @@ -187,7 +187,7 @@ int ModListView::prevMod(int modIndex) const return modIndex; } - return -1; + return {}; } void ModListView::enableAllVisible() @@ -730,6 +730,7 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, FilterLis m_byPriorityProxy->refreshExpandedItems(); } }); + connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &ModListView::onSelectionChanged); } void ModListView::setModel(QAbstractItemModel* model) @@ -848,6 +849,17 @@ void ModListView::onDoubleClicked(const QModelIndex& index) } } +void ModListView::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) +{ + if (hasCollapsibleSeparators()) { + for (auto& idx : selected.indexes()) { + if (idx.parent().isValid() && !isExpanded(idx.parent())) { + setExpanded(idx.parent(), true); + } + } + } +} + void ModListView::dragEnterEvent(QDragEnterEvent* event) { emit dragEntered(event->mimeData()); diff --git a/src/modlistview.h b/src/modlistview.h index 1678d01e..c7f4e5f0 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -58,11 +58,10 @@ public: ModListViewActions& actions() const; // retrieve the next/previous mod in the current view, the given index - // should be a mod index (not a model row), and the return value will be - // a mod index or -1 if no mod was found + // should be a mod index (not a model row) // - int nextMod(int index) const; - int prevMod(int index) const; + std::optional nextMod(unsigned int index) const; + std::optional prevMod(unsigned int index) const; // check if the given mod is visible // @@ -142,6 +141,7 @@ protected slots: void onCustomContextMenuRequested(const QPoint& pos); void onDoubleClicked(const QModelIndex& index); + void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); private: diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 2b5bf026..3cc2a841 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -433,8 +433,13 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in else { modInfo->saveMeta(); - ModInfoDialog dialog(m_main, &m_core, &m_core.pluginContainer(), modInfo); + ModInfoDialog dialog(m_core, m_core.pluginContainer(), modInfo, m_view); connect(&dialog, &ModInfoDialog::originModified, this, &ModListViewActions::originModified); + connect(&dialog, &ModInfoDialog::modChanged, [=](unsigned int index) { + auto idx = m_view->indexModelToView(m_core.modList()->index(index, 0)); + m_view->selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + m_view->scrollTo(idx); + }); //Open the tab first if we want to use the standard indexes of the tabs. if (tab != ModInfoTabIDs::None) { diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 507932e2..17cd80c2 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -15,7 +15,6 @@ #include "profile.h" #include "credentialsdialog.h" #include "filedialogmemory.h" -#include "modinfodialog.h" #include "spawn.h" #include "syncoverwritedialog.h" #include "nxmaccessmanager.h" -- cgit v1.3.1 From f4fff85d6de679172b0fe69dc28f8314cfc634fb Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 16:54:31 +0100 Subject: Fix parent of ModInfoDialog. --- src/modinfodialog.cpp | 5 ++--- src/modinfodialog.h | 2 +- src/modlistviewactions.cpp | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 54c97406..e627c219 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -174,11 +174,10 @@ bool ModInfoDialog::TabInfo::isVisible() const return (realPos != -1); } - ModInfoDialog::ModInfoDialog( OrganizerCore& core, PluginContainer& plugin, - ModInfo::Ptr mod, ModListView* modListView) : - TutorableDialog("ModInfoDialog", modListView), + ModInfo::Ptr mod, ModListView* modListView, QWidget *parent) : + TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_core(core), m_plugin(plugin), diff --git a/src/modinfodialog.h b/src/modinfodialog.h index a3b6ffdb..6d408f05 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -53,7 +53,7 @@ class ModInfoDialog : public MOBase::TutorableDialog public: ModInfoDialog( OrganizerCore& core, PluginContainer& plugin, - ModInfo::Ptr mod, ModListView* view); + ModInfo::Ptr mod, ModListView* view, QWidget* parent = nullptr); ~ModInfoDialog(); diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 3cc2a841..2029ddd3 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -433,7 +433,7 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in else { modInfo->saveMeta(); - ModInfoDialog dialog(m_core, m_core.pluginContainer(), modInfo, m_view); + ModInfoDialog dialog(m_core, m_core.pluginContainer(), modInfo, m_view, m_main); connect(&dialog, &ModInfoDialog::originModified, this, &ModListViewActions::originModified); connect(&dialog, &ModInfoDialog::modChanged, [=](unsigned int index) { auto idx = m_view->indexModelToView(m_core.modList()->index(index, 0)); -- cgit v1.3.1 From 142ba74acf020bb40fe5d8f1fcb2d1c789832116 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 17:07:54 +0100 Subject: Use proper parent object in modlistviewactions. --- src/modlistviewactions.cpp | 67 +++++++++++++++++++++++----------------------- src/modlistviewactions.h | 5 +++- 2 files changed, 38 insertions(+), 34 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 2029ddd3..d9841517 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -39,8 +39,9 @@ ModListViewActions::ModListViewActions( , m_core(core) , m_filters(filters) , m_categories(categoryFactory) - , m_main(mainWindow) , m_view(view) + , m_parent(mainWindow) + , m_main(mainWindow) { } @@ -55,7 +56,7 @@ void ModListViewActions::installMod(const QString& archivePath) const *iter = "*." + *iter; } - path = FileDialogMemory::getOpenFileName("installMod", m_view, tr("Choose Mod"), QString(), + path = FileDialogMemory::getOpenFileName("installMod", m_parent, tr("Choose Mod"), QString(), tr("Mod Archive").append(QString(" (%1)").arg(extensions.join(" ")))); } @@ -78,7 +79,7 @@ void ModListViewActions::createEmptyMod(int modIndex) const while (name->isEmpty()) { bool ok; - name.update(QInputDialog::getText(m_view, tr("Create Mod..."), + name.update(QInputDialog::getText(m_parent, tr("Create Mod..."), tr("This will create an empty mod.\n" "Please enter a name:"), QLineEdit::Normal, "", &ok), GUESS_USER); @@ -116,7 +117,7 @@ void ModListViewActions::createSeparator(int modIndex) const while (name->isEmpty()) { bool ok; - name.update(QInputDialog::getText(m_view, tr("Create Separator..."), + name.update(QInputDialog::getText(m_parent, tr("Create Separator..."), tr("This will create a new separator.\n" "Please enter a name:"), QLineEdit::Normal, "", &ok), GUESS_USER); @@ -225,7 +226,7 @@ void ModListViewActions::checkModsForUpdates(const QModelIndexList& indices) con void ModListViewActions::exportModListCSV() const { - QDialog selection(m_view); + QDialog selection(m_parent); QGridLayout* grid = new QGridLayout; selection.setWindowTitle(tr("Export to csv")); @@ -371,7 +372,7 @@ void ModListViewActions::exportModListCSV() const } } - SaveTextAsDialog saveDialog(m_view); + SaveTextAsDialog saveDialog(m_parent); saveDialog.setText(buffer.data()); saveDialog.exec(); } @@ -407,10 +408,10 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in } std::vector flags = modInfo->getFlags(); if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { - QDialog* dialog = m_main->findChild("__overwriteDialog"); + QDialog* dialog = m_parent->findChild("__overwriteDialog"); try { if (dialog == nullptr) { - dialog = new OverwriteInfoDialog(modInfo, m_main); + dialog = new OverwriteInfoDialog(modInfo, m_parent); dialog->setObjectName("__overwriteDialog"); } else { @@ -433,7 +434,7 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in else { modInfo->saveMeta(); - ModInfoDialog dialog(m_core, m_core.pluginContainer(), modInfo, m_view, m_main); + ModInfoDialog dialog(m_core, m_core.pluginContainer(), modInfo, m_view, m_parent); connect(&dialog, &ModInfoDialog::originModified, this, &ModListViewActions::originModified); connect(&dialog, &ModInfoDialog::modChanged, [=](unsigned int index) { auto idx = m_view->indexModelToView(m_core.modList()->index(index, 0)); @@ -486,7 +487,7 @@ void ModListViewActions::sendModsToBottom(const QModelIndexList& index) const void ModListViewActions::sendModsToPriority(const QModelIndexList& index) const { bool ok; - int priority = QInputDialog::getInt(m_view, + int priority = QInputDialog::getInt(m_parent, tr("Set Priority"), tr("Set the priority of the selected mods"), 0, 0, std::numeric_limits::max(), 1, &ok); if (!ok) return; @@ -507,7 +508,7 @@ void ModListViewActions::sendModsToSeparator(const QModelIndexList& index) const } } - ListDialog dialog(m_view); + ListDialog dialog(m_parent); dialog.setWindowTitle("Select a separator..."); dialog.setChoices(separators); @@ -580,7 +581,7 @@ void ModListViewActions::removeMods(const QModelIndexList& indices) const modNames.append(ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->name()); ++i; } - if (QMessageBox::question(m_view, tr("Confirm"), + if (QMessageBox::question(m_parent, tr("Confirm"), tr("Remove the following mods?
      %1
    ").arg(mods), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { // use mod names instead of indexes because those become invalid during the removal @@ -623,7 +624,7 @@ void ModListViewActions::setIgnoreUpdate(const QModelIndexList& indices, bool ig } void ModListViewActions::changeVersioningScheme(const QModelIndex& index) const { - if (QMessageBox::question(m_view, tr("Continue?"), + if (QMessageBox::question(m_parent, tr("Continue?"), tr("The versioning scheme decides which version is considered newer than another.\n" "This function will guess the versioning scheme under the assumption that the installed version is outdated."), QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) { @@ -644,7 +645,7 @@ void ModListViewActions::changeVersioningScheme(const QModelIndex& index) const } } if (!success) { - QMessageBox::information(m_view, tr("Sorry"), + QMessageBox::information(m_parent, tr("Sorry"), tr("I don't know a versioning scheme where %1 is newer than %2.").arg(info->newestVersion().canonicalString()).arg(info->version().canonicalString()), QMessageBox::Ok); } @@ -664,7 +665,7 @@ void ModListViewActions::markConverted(const QModelIndexList& indices) const void ModListViewActions::visitOnNexus(const QModelIndexList& indices) const { if (indices.size() > 10) { - if (QMessageBox::question(m_view, tr("Opening Nexus Links"), + if (QMessageBox::question(m_parent, tr("Opening Nexus Links"), tr("You are trying to open %1 links to Nexus Mods. Are you sure you want to do this?").arg(indices.size()), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { return; @@ -687,7 +688,7 @@ void ModListViewActions::visitOnNexus(const QModelIndexList& indices) const void ModListViewActions::visitWebPage(const QModelIndexList& indices) const { if (indices.size() > 10) { - if (QMessageBox::question(m_view, tr("Opening Web Pages"), + if (QMessageBox::question(m_parent, tr("Opening Web Pages"), tr("You are trying to open %1 Web Pages. Are you sure you want to do this?").arg(indices.size()), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { return; @@ -707,7 +708,7 @@ void ModListViewActions::visitWebPage(const QModelIndexList& indices) const void ModListViewActions::visitNexusOrWebPage(const QModelIndexList& indices) const { if (indices.size() > 10) { - if (QMessageBox::question(m_view, tr("Opening Web Pages"), + if (QMessageBox::question(m_parent, tr("Opening Web Pages"), tr("You are trying to open %1 Web Pages. Are you sure you want to do this?").arg(indices.size()), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { return; @@ -759,11 +760,11 @@ void ModListViewActions::reinstallMod(const QModelIndex& index) const m_core.installMod(fullInstallationFile, true, modInfo, modInfo->name()); } else { - QMessageBox::information(m_view, tr("Failed"), tr("Installation file no longer exists")); + QMessageBox::information(m_parent, tr("Failed"), tr("Installation file no longer exists")); } } else { - QMessageBox::information(m_view, tr("Failed"), + QMessageBox::information(m_parent, tr("Failed"), tr("Mods installed with old versions of MO can't be reinstalled in this way.")); } } @@ -773,7 +774,7 @@ void ModListViewActions::createBackup(const QModelIndex& index) const ModInfo::Ptr modInfo = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); QString backupDirectory = m_core.installationManager()->generateBackupName(modInfo->absolutePath()); if (!copyDir(modInfo->absolutePath(), backupDirectory, false)) { - QMessageBox::information(m_view, tr("Failed"), + QMessageBox::information(m_parent, tr("Failed"), tr("Failed to create backup.")); } m_core.refresh(); @@ -787,7 +788,7 @@ void ModListViewActions::restoreHiddenFiles(const QModelIndexList& indices) cons QFlags flags = FileRenamer::UNHIDE; flags |= FileRenamer::MULTIPLE; - FileRenamer renamer(m_view, flags); + FileRenamer renamer(m_parent, flags); FileRenamer::RenameResults result = FileRenamer::RESULT_OK; @@ -813,7 +814,7 @@ void ModListViewActions::restoreHiddenFiles(const QModelIndexList& indices) cons mods += "
  • ...
  • "; } - if (QMessageBox::question(m_view, tr("Confirm"), + if (QMessageBox::question(m_parent, tr("Confirm"), tr("Restore all hidden files in the following mods?
      %1
    ").arg(mods), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { @@ -842,7 +843,7 @@ void ModListViewActions::restoreHiddenFiles(const QModelIndexList& indices) cons ModInfo::Ptr modInfo = ModInfo::getByIndex(indices[0].data(ModList::IndexRole).toInt()); const QString modDir = modInfo->absolutePath(); - if (QMessageBox::question(m_view, tr("Are you sure?"), + if (QMessageBox::question(m_parent, tr("Are you sure?"), tr("About to restore all hidden files in:\n") + modInfo->name(), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { @@ -863,7 +864,7 @@ void ModListViewActions::restoreHiddenFiles(const QModelIndexList& indices) cons void ModListViewActions::setTracked(const QModelIndexList& indices, bool tracked) const { - m_core.loggedInAction(m_view, [=] { + m_core.loggedInAction(m_parent, [=] { for (auto& idx : indices) { ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt())->track(tracked); } @@ -872,9 +873,9 @@ void ModListViewActions::setTracked(const QModelIndexList& indices, bool tracked void ModListViewActions::setEndorsed(const QModelIndexList& indices, bool endorsed) const { - m_core.loggedInAction(m_view, [=] { + m_core.loggedInAction(m_parent, [=] { if (indices.size() > 1) { - MessageDialog::showMessage(tr("Endorsing multiple mods will take a while. Please wait..."), m_view); + MessageDialog::showMessage(tr("Endorsing multiple mods will take a while. Please wait..."), m_parent); } for (auto& idx : indices) { @@ -895,7 +896,7 @@ void ModListViewActions::setColor(const QModelIndexList& indices, const QModelIn auto& settings = m_core.settings(); ModInfo::Ptr modInfo = ModInfo::getByIndex(refIndex.data(ModList::IndexRole).toInt()); - QColorDialog dialog(m_view); + QColorDialog dialog(m_parent); dialog.setOption(QColorDialog::ShowAlphaChannel); QColor currentColor = modInfo->color(); @@ -994,7 +995,7 @@ void ModListViewActions::restoreBackup(const QModelIndex& index) const QString regName = backupRegEx.cap(1); QDir modDir(QDir::fromNativeSeparators(m_core.settings().paths().mods())); if (!modDir.exists(regName) || - (QMessageBox::question(m_view, tr("Overwrite?"), + (QMessageBox::question(m_parent, tr("Overwrite?"), tr("This will replace the existing mod \"%1\". Continue?").arg(regName), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { if (modDir.exists(regName) && !shellDelete(QStringList(modDir.absoluteFilePath(regName)))) { @@ -1016,10 +1017,10 @@ void ModListViewActions::moveOverwriteContentsTo(const QString& absolutePath) co { ModInfo::Ptr overwriteInfo = ModInfo::getOverwrite(); bool successful = shellMove((QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"), - (QDir::toNativeSeparators(absolutePath)), false, m_view); + (QDir::toNativeSeparators(absolutePath)), false, m_parent); if (successful) { - MessageDialog::showMessage(tr("Move successful."), m_view); + MessageDialog::showMessage(tr("Move successful."), m_parent); } else { const auto e = GetLastError(); @@ -1036,7 +1037,7 @@ void ModListViewActions::createModFromOverwrite() const while (name->isEmpty()) { bool ok; - name.update(QInputDialog::getText(m_view, tr("Create Mod..."), + name.update(QInputDialog::getText(m_parent, tr("Create Mod..."), tr("This will move all files from overwrite into a new, regular mod.\n" "Please enter a name:"), QLineEdit::Normal, "", &ok), GUESS_USER); @@ -1071,7 +1072,7 @@ void ModListViewActions::moveOverwriteContentToExistingMod() const } } - ListDialog dialog(m_view); + ListDialog dialog(m_parent); dialog.setWindowTitle("Select a mod..."); dialog.setChoices(mods); @@ -1105,7 +1106,7 @@ void ModListViewActions::clearOverwrite() const if (modInfo) { QDir overwriteDir(modInfo->absolutePath()); - if (QMessageBox::question(m_view, tr("Are you sure?"), + if (QMessageBox::question(m_parent, tr("Are you sure?"), tr("About to recursively delete:\n") + overwriteDir.absolutePath(), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 26efde47..135b3035 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -141,8 +141,11 @@ private: OrganizerCore& m_core; FilterList& m_filters; CategoryFactory& m_categories; - MainWindow* m_main; ModListView* m_view; + QWidget* m_parent; + + // hope to get rid of this some day + MainWindow* m_main; }; #endif -- cgit v1.3.1 From f2d8469ed6cd0fafc22097cdba2ee8f325f00513 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 21:02:04 +0100 Subject: Start moving stuff from MainWindow to PluginListView. --- src/CMakeLists.txt | 1 + src/mainwindow.cpp | 88 +++----------------------- src/mainwindow.h | 5 -- src/modelutils.cpp | 58 ++++++++++++++++++ src/modelutils.h | 14 +++++ src/modlistview.cpp | 51 +++------------ src/modlistviewactions.cpp | 23 ++++--- src/modlistviewactions.h | 12 ++-- src/organizercore.cpp | 8 --- src/organizercore.h | 2 - src/pluginlistview.cpp | 150 ++++++++++++++++++++++++++++++++++----------- src/pluginlistview.h | 55 ++++++++++++++--- 12 files changed, 270 insertions(+), 197 deletions(-) create mode 100644 src/modelutils.cpp create mode 100644 src/modelutils.h (limited to 'src/modlistviewactions.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4c7bcd46..7773e845 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -227,6 +227,7 @@ add_filter(NAME src/widgets GROUPS qtgroupingproxy texteditor viewmarkingscrollbar + modelutils ) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b000ba57..60e2a1e0 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -320,14 +320,7 @@ MainWindow::MainWindow(Settings &settings TaskProgressManager::instance().tryCreateTaskbar(); setupModList(); - - // set up plugin list - m_PluginListSortProxy = m_OrganizerCore.createPluginListProxyModel(); - - ui->espList->setModel(m_PluginListSortProxy); - ui->espList->sortByColumn(PluginList::COL_PRIORITY, Qt::AscendingOrder); - ui->espList->setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(ui->espList)); - ui->espList->installEventFilter(m_OrganizerCore.pluginList()); + ui->espList->setup(m_OrganizerCore, this, ui); ui->bsaList->setLocalMoveOnly(true); ui->bsaList->setHeaderHidden(true); @@ -400,9 +393,6 @@ MainWindow::MainWindow(Settings &settings connect(&m_PluginContainer, SIGNAL(diagnosisUpdate()), this, SLOT(scheduleCheckForProblems())); - connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), m_PluginListSortProxy, SLOT(updateFilter(QString))); - connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString))); - connect(m_OrganizerCore.directoryRefresher(), SIGNAL(refreshed()), this, SLOT(directory_refreshed())); connect( m_OrganizerCore.directoryRefresher(), @@ -513,10 +503,10 @@ MainWindow::MainWindow(Settings &settings refreshExecutablesList(); updatePinnedExecutables(); resetActionIcons(); - updatePluginCount(); processUpdates(); ui->modList->updateModCount(); + ui->espList->updatePluginCount(); ui->statusBar->updateNormalMessage(m_OrganizerCore); } @@ -1129,18 +1119,6 @@ void MainWindow::createHelpMenu() menu->addAction(tr("About Qt"), qApp, SLOT(aboutQt())); } -void MainWindow::espFilterChanged(const QString &filter) -{ - if (!filter.isEmpty()) { - ui->espList->setStyleSheet("QTreeView { border: 2px ridge #f00; }"); - ui->activePluginsCounter->setStyleSheet("QLCDNumber { border: 2px ridge #f00; }"); - } else { - ui->espList->setStyleSheet(""); - ui->activePluginsCounter->setStyleSheet(""); - } - updatePluginCount(); -} - bool MainWindow::addProfile() { QComboBox *profileBox = findChild("profileBox"); @@ -1544,7 +1522,7 @@ void MainWindow::activateSelectedProfile() m_SavesTab->refreshSaveList(); m_OrganizerCore.refresh(); ui->modList->updateModCount(); - updatePluginCount(); + ui->espList->updatePluginCount(); ui->statusBar->updateNormalMessage(m_OrganizerCore); } @@ -2238,7 +2216,7 @@ void MainWindow::directory_refreshed() void MainWindow::esplist_changed() { - updatePluginCount(); + ui->espList->updatePluginCount(); } void MainWindow::modInstalled(const QString &modName) @@ -2332,6 +2310,7 @@ void MainWindow::esplistSelectionsChanged(const QItemSelection &selected) { m_OrganizerCore.modList()->highlightMods(ui->espList->selectionModel(), *m_OrganizerCore.directoryStructure()); ui->modList->verticalScrollBar()->repaint(); + ui->modList->repaint(); } void MainWindow::modRemoved(const QString &fileName) @@ -2427,57 +2406,6 @@ void MainWindow::refreshProfile_activated() m_OrganizerCore.profileRefresh(); } -void MainWindow::updatePluginCount() -{ - int activeMasterCount = 0; - int activeLightMasterCount = 0; - int activeRegularCount = 0; - int masterCount = 0; - int lightMasterCount = 0; - int regularCount = 0; - int activeVisibleCount = 0; - - PluginList *list = m_OrganizerCore.pluginList(); - QString filter = ui->espFilterEdit->text(); - - for (QString plugin : list->pluginNames()) { - bool active = list->isEnabled(plugin); - bool visible = m_PluginListSortProxy->filterMatchesPlugin(plugin); - if (list->isLight(plugin) || list->isLightFlagged(plugin)) { - lightMasterCount++; - activeLightMasterCount += active; - activeVisibleCount += visible && active; - } else if (list->isMaster(plugin)) { - masterCount++; - activeMasterCount += active; - activeVisibleCount += visible && active; - } else { - regularCount++; - activeRegularCount += active; - activeVisibleCount += visible && active; - } - } - - int activeCount = activeMasterCount + activeLightMasterCount + activeRegularCount; - int totalCount = masterCount + lightMasterCount + regularCount; - - ui->activePluginsCounter->display(activeVisibleCount); - ui->activePluginsCounter->setToolTip(tr("" - "" - "" - "" - "" - "" - "" - "
    TypeActive Total
    All plugins:%1 %2
    ESMs:%3 %4
    ESPs:%7 %8
    ESMs+ESPs:%9 %10
    ESLs:%5 %6
    ") - .arg(activeCount).arg(totalCount) - .arg(activeMasterCount).arg(masterCount) - .arg(activeLightMasterCount).arg(lightMasterCount) - .arg(activeRegularCount).arg(regularCount) - .arg(activeMasterCount+activeRegularCount).arg(masterCount+regularCount) - ); -} - void MainWindow::openOriginInformation_clicked() { try { @@ -2680,7 +2608,7 @@ QMenu *MainWindow::openFolderMenu() void MainWindow::addPluginSendToContextMenu(QMenu *menu) { - if (m_PluginListSortProxy->sortColumn() != PluginList::COL_PRIORITY) + if (ui->espList->sortColumn() != PluginList::COL_PRIORITY) return; QMenu *sub_menu = new QMenu(this); @@ -3623,7 +3551,7 @@ void MainWindow::toolBar_customContextMenuRequested(const QPoint &point) void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) { - int espIndex = m_PluginListSortProxy->mapToSource(ui->espList->indexAt(pos)).row(); + int espIndex = ui->espList->indexViewToModel(ui->espList->indexAt(pos)).row(); QMenu menu; menu.addAction(tr("Enable selected"), [=]() { enableSelectedPlugins_clicked(); }); @@ -3642,7 +3570,7 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) bool hasLocked = false; bool hasUnlocked = false; for (const QModelIndex &idx : currentSelection.indexes()) { - int row = m_PluginListSortProxy->mapToSource(idx).row(); + int row = ui->espList->indexViewToModel(idx).row(); if (m_OrganizerCore.pluginList()->isEnabled(row)) { if (m_OrganizerCore.pluginList()->isESPLocked(row)) { hasLocked = true; diff --git a/src/mainwindow.h b/src/mainwindow.h index 75e7124c..71845874 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -148,8 +148,6 @@ public slots: void directory_refreshed(); - void updatePluginCount(); - signals: /** @@ -262,8 +260,6 @@ private: QStringList m_DefaultArchives; - PluginListSortProxy *m_PluginListSortProxy; - int m_OldExecutableIndex; QAction *m_ContextAction; @@ -406,7 +402,6 @@ private slots: void modlistChanged(const QModelIndexList &indicies, int role); void fileMoved(const QString &filePath, const QString &oldOriginName, const QString &newOriginName); - void espFilterChanged(const QString &filter); void resizeLists(bool pluginListCustom); /** diff --git a/src/modelutils.cpp b/src/modelutils.cpp new file mode 100644 index 00000000..43aa6b99 --- /dev/null +++ b/src/modelutils.cpp @@ -0,0 +1,58 @@ +#include "modelutils.h" + +#include + +QModelIndex indexModelToView(const QModelIndex& index, const QAbstractItemView* view) +{ + // we need to stack the proxy + std::vector proxies; + { + auto* currentModel = view->model(); + while (auto* proxy = qobject_cast(currentModel)) { + proxies.push_back(proxy); + currentModel = proxy->sourceModel(); + } + } + + if (proxies.empty() || proxies.back()->sourceModel() != index.model()) { + return QModelIndex(); + } + + auto qindex = index; + for (auto rit = proxies.rbegin(); rit != proxies.rend(); ++rit) { + qindex = (*rit)->mapFromSource(qindex); + } + + return qindex; +} + +QModelIndexList indexModelToView(const QModelIndexList& index, const QAbstractItemView* view) +{ + QModelIndexList result; + for (auto& idx : index) { + result.append(indexModelToView(idx, view)); + } + return result; +} + +QModelIndex indexViewToModel(const QModelIndex& index, const QAbstractItemModel* model) +{ + if (index.model() == model) { + return index; + } + else if (auto* proxy = qobject_cast(index.model())) { + return indexViewToModel(proxy->mapToSource(index), model); + } + else { + return QModelIndex(); + } +} + +QModelIndexList indexViewToModel(const QModelIndexList& index, const QAbstractItemModel* model) +{ + QModelIndexList result; + for (auto& idx : index) { + result.append(indexViewToModel(idx, model)); + } + return result; +} diff --git a/src/modelutils.h b/src/modelutils.h new file mode 100644 index 00000000..f355c0d6 --- /dev/null +++ b/src/modelutils.h @@ -0,0 +1,14 @@ +#ifndef MODELUTILS_H +#define MODELUTILS_H + +#include +#include + +// convert back-and-forth through model proxies +QModelIndex indexModelToView(const QModelIndex& index, const QAbstractItemView* view); +QModelIndexList indexModelToView(const QModelIndexList& index, const QAbstractItemView* view); +QModelIndex indexViewToModel(const QModelIndex& index, const QAbstractItemModel* model); +QModelIndexList indexViewToModel(const QModelIndexList& index, const QAbstractItemModel* model); + + +#endif diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 99cc4c4c..082d947e 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -24,6 +24,8 @@ #include "genericicondelegate.h" #include "shared/directoryentry.h" #include "shared/filesorigin.h" +#include "mainwindow.h" +#include "modelutils.h" using namespace MOBase; @@ -197,61 +199,22 @@ bool ModListView::isModVisible(ModInfo::Ptr mod) const QModelIndex ModListView::indexModelToView(const QModelIndex& index) const { - if (index.model() != m_core->modList()) { - return QModelIndex(); - } - - // we need to stack the proxy - std::vector proxies; - { - auto* currentModel = model(); - while (auto* proxy = qobject_cast(currentModel)) { - proxies.push_back(proxy); - currentModel = proxy->sourceModel(); - } - } - - if (proxies.empty() || proxies.back()->sourceModel() != m_core->modList()) { - return QModelIndex(); - } - - auto qindex = index; - for (auto rit = proxies.rbegin(); rit != proxies.rend(); ++rit) { - qindex = (*rit)->mapFromSource(qindex); - } - - return qindex; + return ::indexModelToView(index, this); } QModelIndexList ModListView::indexModelToView(const QModelIndexList& index) const { - QModelIndexList result; - for (auto& idx : index) { - result.append(indexModelToView(idx)); - } - return result; + return ::indexModelToView(index, this); } QModelIndex ModListView::indexViewToModel(const QModelIndex& index) const { - if (index.model() == m_core->modList()) { - return index; - } - else if (auto* proxy = qobject_cast(index.model())) { - return indexViewToModel(proxy->mapToSource(index)); - } - else { - return QModelIndex(); - } + return ::indexViewToModel(index, m_core->modList()); } QModelIndexList ModListView::indexViewToModel(const QModelIndexList& index) const { - QModelIndexList result; - for (auto& idx : index) { - result.append(indexViewToModel(idx)); - } - return result; + return ::indexViewToModel(index, m_core->modList()); } QModelIndex ModListView::nextIndex(const QModelIndex& index) const @@ -625,7 +588,7 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo m_core = &core; m_filters.reset(new FilterList(mwui, core, factory)); m_categories = &factory; - m_actions = new ModListViewActions(core, *m_filters, factory, mw, this); + m_actions = new ModListViewActions(core, *m_filters, factory, this, mwui->espList, mw, mw); ui = { mwui->groupCombo, mwui->activeModsCounter, mwui->modFilterEdit, mwui->currentCategoryLabel, mwui->clearFiltersButton }; connect(m_core, &OrganizerCore::modInstalled, this, &ModListView::onModInstalled); diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index d9841517..f0d4c4c7 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -17,7 +17,6 @@ #include "modinfodialog.h" #include "modlist.h" #include "modlistview.h" -#include "mainwindow.h" #include "messagedialog.h" #include "nexusinterface.h" #include "nxmaccessmanager.h" @@ -25,6 +24,7 @@ #include "organizercore.h" #include "overwriteinfodialog.h" #include "csvbuilder.h" +#include "pluginlistview.h" #include "shared/filesorigin.h" #include "shared/directoryentry.h" #include "shared/fileregister.h" @@ -33,15 +33,18 @@ using namespace MOBase; using namespace MOShared; + ModListViewActions::ModListViewActions( - OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, MainWindow* mainWindow, ModListView* view) : - QObject(view) + OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, + ModListView* view, PluginListView* pluginView, QObject* nxmReceiver, QWidget* parent) : + QObject(parent) , m_core(core) , m_filters(filters) , m_categories(categoryFactory) , m_view(view) - , m_parent(mainWindow) - , m_main(mainWindow) + , m_pluginView(pluginView) + , m_parent(parent) + , m_receiver(nxmReceiver) { } @@ -157,9 +160,9 @@ void ModListViewActions::checkModsForUpdates() const { bool checkingModsForUpdate = false; if (NexusInterface::instance().getAccessManager()->validated()) { - checkingModsForUpdate = ModInfo::checkAllForUpdate(&m_core.pluginContainer(), m_main); - NexusInterface::instance().requestEndorsementInfo(m_main, QVariant(), QString()); - NexusInterface::instance().requestTrackingInfo(m_main, QVariant(), QString()); + checkingModsForUpdate = ModInfo::checkAllForUpdate(&m_core.pluginContainer(), m_receiver); + NexusInterface::instance().requestEndorsementInfo(m_receiver, QVariant(), QString()); + NexusInterface::instance().requestTrackingInfo(m_receiver, QVariant(), QString()); } else { QString apiKey; if (GlobalSettings::nexusApiKey(apiKey)) { @@ -201,7 +204,7 @@ void ModListViewActions::checkModsForUpdates(std::multimap const& } if (NexusInterface::instance().getAccessManager()->validated()) { - ModInfo::manualUpdateCheck(m_main, IDs); + ModInfo::manualUpdateCheck(m_receiver, IDs); } else { QString apiKey; @@ -596,7 +599,7 @@ void ModListViewActions::removeMods(const QModelIndexList& indices) const m_core.modList()->removeRow(indices[0].data(ModList::IndexRole).toInt(), QModelIndex()); } m_view->updateModCount(); - m_main->updatePluginCount(); + m_pluginView->updatePluginCount(); } catch (const std::exception& e) { reportError(tr("failed to remove mod: %1").arg(e.what())); diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 135b3035..cb7cdbda 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -11,6 +11,7 @@ class CategoryFactory; class FilterList; class MainWindow; class ModListView; +class PluginListView; class OrganizerCore; class ModListViewActions : public QObject @@ -26,8 +27,10 @@ public: OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, - MainWindow* mainWindow, - ModListView* view); + ModListView* view, + PluginListView* pluginView, + QObject* nxmReceiver, + QWidget* parent); // install the mod from the given archive // @@ -142,10 +145,9 @@ private: FilterList& m_filters; CategoryFactory& m_categories; ModListView* m_view; + PluginListView* m_pluginView; + QObject* m_receiver; QWidget* m_parent; - - // hope to get rid of this some day - MainWindow* m_main; }; #endif diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 17cd80c2..a8911d4f 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -11,7 +11,6 @@ #include "modrepositoryfileinfo.h" #include "nexusinterface.h" #include "plugincontainer.h" -#include "pluginlistsortproxy.h" #include "profile.h" #include "credentialsdialog.h" #include "filedialogmemory.h" @@ -1507,13 +1506,6 @@ void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply) } } -PluginListSortProxy *OrganizerCore::createPluginListProxyModel() -{ - PluginListSortProxy *result = new PluginListSortProxy(this); - result->setSourceModel(&m_PluginList); - return result; -} - PluginContainer& OrganizerCore::pluginContainer() const { return *m_PluginContainer; diff --git a/src/organizercore.h b/src/organizercore.h index e060fbcb..24de26ee 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -227,8 +227,6 @@ public: MOBase::VersionInfo getVersion() const { return m_Updater.getVersion(); } - PluginListSortProxy *createPluginListProxyModel(); - // return the plugin container // PluginContainer& pluginContainer() const; diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp index a265d5d4..a401ab06 100644 --- a/src/pluginlistview.cpp +++ b/src/pluginlistview.cpp @@ -1,58 +1,136 @@ #include "pluginlistview.h" -#include + #include #include -#include +#include -class PluginListViewStyle : public QProxyStyle { -public: - PluginListViewStyle(QStyle *style, int indentation); +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "organizercore.h" +#include "pluginlistsortproxy.h" +#include "genericicondelegate.h" +#include "modelutils.h" - void drawPrimitive(PrimitiveElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget = 0) const; -private: - int m_Indentation; -}; +PluginListView::PluginListView(QWidget *parent) + : QTreeView(parent) + , m_Scrollbar(new ViewMarkingScrollBar(this->model(), this)) +{ + setVerticalScrollBar(m_Scrollbar); + MOBase::setCustomizableColumns(this); +} -PluginListViewStyle::PluginListViewStyle(QStyle *style, int indentation) - : QProxyStyle(style), m_Indentation(indentation) +void PluginListView::setModel(QAbstractItemModel *model) { + QTreeView::setModel(model); + setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); } -void PluginListViewStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget) const +int PluginListView::sortColumn() const { - if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) { - QStyleOption opt(*option); - opt.rect.setLeft(m_Indentation); - if (widget) { - opt.rect.setRight(widget->width() - 5); // 5 is an arbitrary value that seems to work ok - } - QProxyStyle::drawPrimitive(element, &opt, painter, widget); - } - else { - QProxyStyle::drawPrimitive(element, option, painter, widget); - } + return m_sortProxy ? m_sortProxy->sortColumn() : -1; } -PluginListView::PluginListView(QWidget *parent) - : QTreeView(parent) - , m_Scrollbar(new ViewMarkingScrollBar(this->model(), this)) +QModelIndex PluginListView::indexModelToView(const QModelIndex& index) const { - setVerticalScrollBar(m_Scrollbar); - MOBase::setCustomizableColumns(this); + return ::indexModelToView(index, this); } -void PluginListView::dragEnterEvent(QDragEnterEvent *event) +QModelIndexList PluginListView::indexModelToView(const QModelIndexList& index) const { - emit dropModeUpdate(event->mimeData()->hasUrls()); + return ::indexModelToView(index, this); +} - QTreeView::dragEnterEvent(event); +QModelIndex PluginListView::indexViewToModel(const QModelIndex& index) const +{ + return ::indexViewToModel(index, m_core->pluginList()); } -void PluginListView::setModel(QAbstractItemModel *model) +QModelIndexList PluginListView::indexViewToModel(const QModelIndexList& index) const { - QTreeView::setModel(model); - setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); + return ::indexViewToModel(index, m_core->pluginList()); +} + +void PluginListView::updatePluginCount() +{ + int activeMasterCount = 0; + int activeLightMasterCount = 0; + int activeRegularCount = 0; + int masterCount = 0; + int lightMasterCount = 0; + int regularCount = 0; + int activeVisibleCount = 0; + + PluginList* list = m_core->pluginList(); + QString filter = ui.filter->text(); + + for (QString plugin : list->pluginNames()) { + bool active = list->isEnabled(plugin); + bool visible = m_sortProxy->filterMatchesPlugin(plugin); + if (list->isLight(plugin) || list->isLightFlagged(plugin)) { + lightMasterCount++; + activeLightMasterCount += active; + activeVisibleCount += visible && active; + } + else if (list->isMaster(plugin)) { + masterCount++; + activeMasterCount += active; + activeVisibleCount += visible && active; + } + else { + regularCount++; + activeRegularCount += active; + activeVisibleCount += visible && active; + } + } + + int activeCount = activeMasterCount + activeLightMasterCount + activeRegularCount; + int totalCount = masterCount + lightMasterCount + regularCount; + + ui.counter->display(activeVisibleCount); + ui.counter->setToolTip(tr("" + "" + "" + "" + "" + "" + "" + "
    TypeActive Total
    All plugins:%1 %2
    ESMs:%3 %4
    ESPs:%7 %8
    ESMs+ESPs:%9 %10
    ESLs:%5 %6
    ") + .arg(activeCount).arg(totalCount) + .arg(activeMasterCount).arg(masterCount) + .arg(activeLightMasterCount).arg(lightMasterCount) + .arg(activeRegularCount).arg(regularCount) + .arg(activeMasterCount + activeRegularCount).arg(masterCount + regularCount) + ); +} + +void PluginListView::onFilterChanged(const QString& filter) +{ + if (!filter.isEmpty()) { + setStyleSheet("QTreeView { border: 2px ridge #f00; }"); + ui.counter->setStyleSheet("QLCDNumber { border: 2px ridge #f00; }"); + } + else { + setStyleSheet(""); + ui.counter->setStyleSheet(""); + } + updatePluginCount(); +} + +void PluginListView::setup(OrganizerCore& core, MainWindow* mw, Ui::MainWindow* mwui) +{ + m_core = &core; + ui = { mwui->activePluginsCounter, mwui->espFilterEdit }; + + m_sortProxy = new PluginListSortProxy(&core); + m_sortProxy->setSourceModel(core.pluginList()); + setModel(m_sortProxy); + + sortByColumn(PluginList::COL_PRIORITY, Qt::AscendingOrder); + setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(this)); + installEventFilter(core.pluginList()); + + connect(ui.filter, &QLineEdit::textChanged, m_sortProxy, &PluginListSortProxy::updateFilter); + connect(ui.filter, &QLineEdit::textChanged, this, &PluginListView::onFilterChanged); + } diff --git a/src/pluginlistview.h b/src/pluginlistview.h index bdd4ee61..95450ffd 100644 --- a/src/pluginlistview.h +++ b/src/pluginlistview.h @@ -5,20 +5,61 @@ #include #include "viewmarkingscrollbar.h" +namespace Ui { + class MainWindow; +} + +class OrganizerCore; +class MainWindow; +class PluginListSortProxy; + class PluginListView : public QTreeView { Q_OBJECT public: - explicit PluginListView(QWidget *parent = 0); - virtual void dragEnterEvent(QDragEnterEvent *event); - virtual void setModel(QAbstractItemModel *model); -signals: - void dropModeUpdate(bool dropOnRows); + explicit PluginListView(QWidget* parent = nullptr); + void setModel(QAbstractItemModel* model) override; + + void setup(OrganizerCore& core, MainWindow* mw, Ui::MainWindow* mwui); + + // the column by which the plugin list is currently sorted + // + int sortColumn() const; + + // update the plugin counter + // + void updatePluginCount(); + + // TODO: Move these to private when possible. + // map from/to the view indexes to the model + // + QModelIndex indexModelToView(const QModelIndex& index) const; + QModelIndexList indexModelToView(const QModelIndexList& index) const; + QModelIndex indexViewToModel(const QModelIndex& index) const; + QModelIndexList indexViewToModel(const QModelIndexList& index) const; + + +protected slots: + + void onFilterChanged(const QString& filter); - public slots: private: - ViewMarkingScrollBar *m_Scrollbar; + struct PluginListViewUi + { + // the plguin counter + QLCDNumber* counter; + + // the filter + QLineEdit* filter; + }; + + OrganizerCore* m_core; + PluginListViewUi ui; + + PluginListSortProxy* m_sortProxy; + + ViewMarkingScrollBar* m_Scrollbar; }; #endif // PLUGINLISTVIEW_H -- cgit v1.3.1 From b5ce6eb8e7ba67f15dcffe0639d7012088c53ebe Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 22:30:19 +0100 Subject: Move open-explorer key combination to views. --- src/mainwindow.cpp | 42 ------------------------------------------ src/mainwindow.h | 3 +-- src/modlistview.cpp | 28 ++++++++++++++++++---------- src/modlistviewactions.cpp | 4 +++- src/pluginlistview.cpp | 17 ++++++++++++++++- 5 files changed, 38 insertions(+), 56 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a530fbf4..9b254250 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -450,9 +450,6 @@ MainWindow::MainWindow(Settings &settings connect(ui->espList->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(esplistSelectionsChanged(QItemSelection))); - new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Enter), this, SLOT(openExplorer_activated())); - new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return), this, SLOT(openExplorer_activated())); - setFilterShortcuts(ui->modList, ui->modFilterEdit); setFilterShortcuts(ui->espList, ui->espFilterEdit); setFilterShortcuts(ui->downloadView, ui->downloadFilterEdit); @@ -2340,45 +2337,6 @@ void MainWindow::setWindowEnabled(bool enabled) setEnabled(enabled); } -void MainWindow::openExplorer_activated() -{ - if (ui->modList->hasFocus()) { - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() == 1 ) { - - QModelIndex idx = selection->currentIndex(); - ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); - std::vector flags = modInfo->getFlags(); - - if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { - shell::Explore(modInfo->absolutePath()); - } - - } - } - - if (ui->espList->hasFocus()) { - QItemSelectionModel *selection = ui->espList->selectionModel(); - - if (selection->hasSelection() && selection->selectedRows().count() == 1) { - - QModelIndex idx = selection->currentIndex(); - QString fileName = idx.data().toString(); - - - unsigned int modInfoIndex = ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName)); - if (modInfoIndex != UINT_MAX) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(modInfoIndex); - std::vector flags = modInfo->getFlags(); - - if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { - shell::Explore(modInfo->absolutePath()); - } - } - } - } -} - void MainWindow::refreshProfile_activated() { m_OrganizerCore.profileRefresh(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 5e3cc798..404df36b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -316,8 +316,7 @@ private slots: void tutorialTriggered(); void extractBSATriggered(QTreeWidgetItem* item); - //modlist shortcuts - void openExplorer_activated(); + // modlist shortcuts void refreshProfile_activated(); void linkToolbar(); diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 082d947e..22a673d1 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -964,20 +964,28 @@ void ModListView::timerEvent(QTimerEvent* event) bool ModListView::event(QEvent* event) { - Profile* profile = m_core->currentProfile(); - if (event->type() == QEvent::KeyPress && profile) { + if (event->type() == QEvent::KeyPress) { QKeyEvent* keyEvent = static_cast(event); if (keyEvent->modifiers() == Qt::ControlModifier - && sortColumn() == ModList::COL_PRIORITY - && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) { - return moveSelection(keyEvent->key()); - } - else if (keyEvent->key() == Qt::Key_Delete) { - return removeSelection(); + && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter)) { + if (selectionModel()->hasSelection() && selectionModel()->selectedRows().count() == 1) { + m_actions->openExplorer({ indexViewToModel(selectionModel()->currentIndex()) }); + return true; + } } - else if (keyEvent->key() == Qt::Key_Space) { - return toggleSelectionState(); + else if (m_core->currentProfile()) { + if (keyEvent->modifiers() == Qt::ControlModifier + && sortColumn() == ModList::COL_PRIORITY + && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) { + return moveSelection(keyEvent->key()); + } + else if (keyEvent->key() == Qt::Key_Delete) { + return removeSelection(); + } + else if (keyEvent->key() == Qt::Key_Space) { + return toggleSelectionState(); + } } return QTreeView::event(event); } diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index f0d4c4c7..83133404 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -986,7 +986,9 @@ void ModListViewActions::openExplorer(const QModelIndexList& index) const { for (auto& idx : index) { ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); - shell::Explore(info->absolutePath()); + if (!info->isForeign()) { + shell::Explore(info->absolutePath()); + } } } diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp index 16e3dac0..4bf91c0a 100644 --- a/src/pluginlistview.cpp +++ b/src/pluginlistview.cpp @@ -247,11 +247,26 @@ bool PluginListView::toggleSelectionState() bool PluginListView::event(QEvent* event) { - Profile* profile = m_core->currentProfile(); + auto* profile = m_core->currentProfile(); if (event->type() == QEvent::KeyPress && profile) { QKeyEvent* keyEvent = static_cast(event); if (keyEvent->modifiers() == Qt::ControlModifier + && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter)) { + if (selectionModel()->hasSelection() && selectionModel()->selectedRows().count() == 1) { + QModelIndex idx = selectionModel()->currentIndex(); + QString fileName = idx.data().toString(); + + if (ModInfo::getIndex(m_core->pluginList()->origin(fileName)) == UINT_MAX) { + return false; + } + + auto modIndex = ModInfo::getIndex(m_core->pluginList()->origin(fileName)); + m_modActions->openExplorer({ m_core->modList()->index(modIndex, 0) }); + return true; + } + } + else if (keyEvent->modifiers() == Qt::ControlModifier && (sortColumn() == PluginList::COL_PRIORITY || sortColumn() == PluginList::COL_MODINDEX) && (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down)) { return moveSelection(keyEvent->key()); -- cgit v1.3.1 From 73b4a590ca4403b0c07590f426f9b962c4c0ec6a Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Thu, 31 Dec 2020 23:45:03 +0100 Subject: Use topLevelWidget() instead of custom parent in mod list actions. --- src/modlistview.cpp | 2 +- src/modlistviewactions.cpp | 6 +++--- src/modlistviewactions.h | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/modlistview.cpp b/src/modlistview.cpp index c92b65ef..f62089b5 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -577,7 +577,7 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo m_core = &core; m_filters.reset(new FilterList(mwui, core, factory)); m_categories = &factory; - m_actions = new ModListViewActions(core, *m_filters, factory, this, mwui->espList, mw, mw); + m_actions = new ModListViewActions(core, *m_filters, factory, this, mwui->espList, mw); ui = { mwui->groupCombo, mwui->activeModsCounter, mwui->modFilterEdit, mwui->currentCategoryLabel, mwui->clearFiltersButton }; connect(m_core, &OrganizerCore::modInstalled, this, &ModListView::onModInstalled); diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 83133404..8d24a8d5 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -36,14 +36,14 @@ using namespace MOShared; ModListViewActions::ModListViewActions( OrganizerCore& core, FilterList& filters, CategoryFactory& categoryFactory, - ModListView* view, PluginListView* pluginView, QObject* nxmReceiver, QWidget* parent) : - QObject(parent) + ModListView* view, PluginListView* pluginView, QObject* nxmReceiver) : + QObject(view) , m_core(core) , m_filters(filters) , m_categories(categoryFactory) , m_view(view) , m_pluginView(pluginView) - , m_parent(parent) + , m_parent(view->topLevelWidget()) , m_receiver(nxmReceiver) { diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index cb7cdbda..65f323d9 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -29,8 +29,7 @@ public: CategoryFactory& categoryFactory, ModListView* view, PluginListView* pluginView, - QObject* nxmReceiver, - QWidget* parent); + QObject* nxmReceiver); // install the mod from the given archive // -- cgit v1.3.1 From 011e4d522ed7b0df52cdeb014608e9881f8bc76b Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 Jan 2021 17:17:56 +0100 Subject: Fix setting the primary category of multiple mods at once. --- src/modlistcontextmenu.cpp | 32 +++++++++++++++++++++++++++----- src/modlistcontextmenu.h | 4 ++++ src/modlistviewactions.cpp | 18 +++++++++++++++++- src/modlistviewactions.h | 8 +++++++- 4 files changed, 55 insertions(+), 7 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 01c4b471..19b5fba4 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -126,13 +126,9 @@ void ModListPrimaryCategoryMenu::populate(const CategoryFactory& factory, ModInf QRadioButton* categoryBox = new QRadioButton( factory.getCategoryName(catIdx).replace('&', "&&"), this); - connect(categoryBox, &QRadioButton::toggled, [mod, categoryID](bool enable) { - if (enable) { - mod->setPrimaryCategory(categoryID); - } - }); categoryBox->setChecked(categoryID == mod->primaryCategory()); action->setDefaultWidget(categoryBox); + action->setData(categoryID); } catch (const std::exception& e) { log::error("failed to create category checkbox: {}", e.what()); @@ -143,6 +139,20 @@ void ModListPrimaryCategoryMenu::populate(const CategoryFactory& factory, ModInf } } +int ModListPrimaryCategoryMenu::primaryCategory() const +{ + for (QAction* action : actions()) { + QWidgetAction* widgetAction = qobject_cast(action); + if (widgetAction) { + QRadioButton* button = qobject_cast(widgetAction->defaultWidget()); + if (button && button->isChecked()) { + return widgetAction->data().toInt(); + } + } + } + return -1; +} + ModListContextMenu::ModListContextMenu( const QModelIndex& index, OrganizerCore& core, CategoryFactory& categories, ModListView* view) : QMenu(view) @@ -245,6 +255,12 @@ void ModListContextMenu::addSeparatorActions(ModInfo::Ptr mod) addMenuAsPushButton(categoriesMenu); ModListPrimaryCategoryMenu* primaryCategoryMenu = new ModListPrimaryCategoryMenu(m_categories, mod, this); + connect(primaryCategoryMenu, &QMenu::aboutToHide, [=]() { + int category = primaryCategoryMenu->primaryCategory(); + if (category != -1) { + m_actions.setPrimaryCategory(m_selected, category); + } + }); addMenuAsPushButton(primaryCategoryMenu); addSeparator(); @@ -310,6 +326,12 @@ void ModListContextMenu::addRegularActions(ModInfo::Ptr mod) addMenuAsPushButton(categoriesMenu); ModListPrimaryCategoryMenu* primaryCategoryMenu = new ModListPrimaryCategoryMenu(m_categories, mod, this); + connect(primaryCategoryMenu, &QMenu::aboutToHide, [=]() { + int category = primaryCategoryMenu->primaryCategory(); + if (category != -1) { + m_actions.setPrimaryCategory(m_selected, category); + } + }); addMenuAsPushButton(primaryCategoryMenu); addSeparator(); diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index d009c9d8..aa9d4c2b 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -55,6 +55,10 @@ public: ModListPrimaryCategoryMenu(CategoryFactory& categories, ModInfo::Ptr mod, QMenu* parent = nullptr); + // return the selected primary category + // + int primaryCategory() const; + private: // populate the categories diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 8d24a8d5..cb212e0b 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -956,7 +956,6 @@ void ModListViewActions::setCategories(const QModelIndexList& selected, const QM { ModInfo::Ptr refMod = ModInfo::getByIndex(ref.data(ModList::IndexRole).toInt()); if (selected.size() > 1) { - for (auto& idx : selected) { if (idx.row() != ref.row()) { setCategoriesIf( @@ -982,6 +981,23 @@ void ModListViewActions::setCategories(const QModelIndexList& selected, const QM } } +void ModListViewActions::setPrimaryCategory(const QModelIndexList& selected, int category, bool force) +{ + for (auto& idx : selected) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt()); + if (force || info->categorySet(category)) { + info->setCategory(category, true); + info->setPrimaryCategory(category); + } + } + + // reset the selection manually - still needed + auto viewIndices = m_view->indexModelToView(selected); + for (auto& idx : viewIndices) { + m_view->selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); + } +} + void ModListViewActions::openExplorer(const QModelIndexList& index) const { for (auto& idx : index) { diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 65f323d9..2850d30a 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -87,7 +87,7 @@ public: void setColor(const QModelIndexList& indices, const QModelIndex& refIndex) const; void resetColor(const QModelIndexList& indices) const; - // set the category of the mod in the given list, using the given index as reference + // set the category of the mods in the given list, using the given index as reference // - the categories are set as-is on the refernce mod // - for the other mods, the category is only set if the current state of the category // on the reference is different @@ -95,6 +95,12 @@ public: void setCategories(const QModelIndexList& selected, const QModelIndex& ref, const std::vector>& categories) const; + // set the primary category of the mods in the given list, adding the appropriate + // category to the mods unless force is false, in which case the primary category + // is set only on mods that have this category + // + void setPrimaryCategory(const QModelIndexList& selected, int category, bool force = true); + // open the Windows explorer for the specified mods // void openExplorer(const QModelIndexList& index) const; -- cgit v1.3.1 From c0ac46d5c020bfb9292a812b2c52dc3c6236c7b3 Mon Sep 17 00:00:00 2001 From: Mikaël Capelle Date: Fri, 1 Jan 2021 20:59:30 +0100 Subject: Fix create empty mod/separator position. --- src/mainwindow.cpp | 2 +- src/modlistcontextmenu.cpp | 15 ++++++++++----- src/modlistcontextmenu.h | 10 ++++++++-- src/modlistviewactions.cpp | 25 ++++++++++--------------- src/modlistviewactions.h | 6 +++--- 5 files changed, 32 insertions(+), 26 deletions(-) (limited to 'src/modlistviewactions.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 10de1b84..23050467 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -366,7 +366,7 @@ MainWindow::MainWindow(Settings &settings m_LinkStartMenu = linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Start Menu"), this, SLOT(linkMenu())); ui->linkButton->setMenu(linkMenu); - ui->listOptionsBtn->setMenu(new ModListGlobalContextMenu(m_OrganizerCore, ui->modList, ui->listOptionsBtn)); + ui->listOptionsBtn->setMenu(new ModListGlobalContextMenu(m_OrganizerCore, ui->modList, this)); ui->openFolderMenu->setMenu(openFolderMenu()); diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index e557d7a0..f20586c4 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -10,11 +10,16 @@ using namespace MOBase; ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, QWidget* parent) + : ModListGlobalContextMenu(core, view, QModelIndex(), parent) +{ +} + +ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, const QModelIndex& index, QWidget* parent) : QMenu(parent) { addAction(tr("Install Mod..."), [=]() { view->actions().installMod(); }); - addAction(tr("Create empty mod"), [=]() { view->actions().createEmptyMod(-1); }); - addAction(tr("Create Separator"), [=]() { view->actions().createSeparator(-1); }); + addAction(tr("Create empty mod"), [=]() { view->actions().createEmptyMod(index); }); + addAction(tr("Create Separator"), [=]() { view->actions().createSeparator(index); }); if (view->hasCollapsibleSeparators()) { addSeparator(); @@ -24,14 +29,14 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV addSeparator(); - addAction(tr("Enable all visible"), [=]() { + addAction(tr("Enable all parent"), [=]() { if (QMessageBox::question(view, tr("Confirm"), tr("Really enable all visible mods?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { view->enableAllVisible(); } }); addAction(tr("Disable all visible"), [=]() { - if (QMessageBox::question(view, tr("Confirm"), tr("Really disable all visible mods?"), + if (QMessageBox::question(parent, tr("Confirm"), tr("Really disable all visible mods?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { view->disableAllVisible(); } @@ -171,7 +176,7 @@ ModListContextMenu::ModListContextMenu( ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt()); - QMenu* allMods = new ModListGlobalContextMenu(core, view, view); + QMenu* allMods = new ModListGlobalContextMenu(core, view, m_index, view->topLevelWidget()); allMods->setTitle(tr("All Mods")); addMenu(allMods); diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 565aac97..2b3f9dcd 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -19,8 +19,14 @@ class ModListGlobalContextMenu : public QMenu Q_OBJECT public: - ModListGlobalContextMenu( - OrganizerCore& core, ModListView* modListView, QWidget* parent = nullptr); + ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, QWidget* parent = nullptr); + +protected: + + friend class ModListContextMenu; + + // creates a "All mods" context menu for the given index (can be invalid). + ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, const QModelIndex& index, QWidget* parent = nullptr); }; diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index cb212e0b..9957618a 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -75,7 +75,7 @@ void ModListViewActions::installMod(const QString& archivePath) const } } -void ModListViewActions::createEmptyMod(int modIndex) const +void ModListViewActions::createEmptyMod(const QModelIndex& index) const { GuessedValue name; name.setFilter(&fixDirectoryName); @@ -97,8 +97,8 @@ void ModListViewActions::createEmptyMod(int modIndex) const } int newPriority = -1; - if (modIndex >= 0 && m_view->sortColumn() == ModList::COL_PRIORITY) { - newPriority = m_core.currentProfile()->getModPriority(modIndex); + if (index.isValid() && m_view->sortColumn() == ModList::COL_PRIORITY) { + newPriority = m_core.currentProfile()->getModPriority(index.data(ModList::IndexRole).toInt()); } IModInterface* newMod = m_core.createMod(name); @@ -113,12 +113,11 @@ void ModListViewActions::createEmptyMod(int modIndex) const } } -void ModListViewActions::createSeparator(int modIndex) const +void ModListViewActions::createSeparator(const QModelIndex& index) const { GuessedValue name; name.setFilter(&fixDirectoryName); - while (name->isEmpty()) - { + while (name->isEmpty()) { bool ok; name.update(QInputDialog::getText(m_parent, tr("Create Separator..."), tr("This will create a new separator.\n" @@ -126,28 +125,24 @@ void ModListViewActions::createSeparator(int modIndex) const GUESS_USER); if (!ok) { return; } } - if (m_core.modList()->getMod(name) != nullptr) - { + if (m_core.modList()->getMod(name) != nullptr) { reportError(tr("A separator with this name already exists")); return; } name->append("_separator"); - if (m_core.modList()->getMod(name) != nullptr) - { + if (m_core.modList()->getMod(name) != nullptr) { return; } int newPriority = -1; - if (modIndex >= 0 && m_view->sortColumn() == ModList::COL_PRIORITY) - { - newPriority = m_core.currentProfile()->getModPriority(modIndex); + if (index.isValid() && m_view->sortColumn() == ModList::COL_PRIORITY) { + newPriority = m_core.currentProfile()->getModPriority(index.data(ModList::IndexRole).toInt()); } if (m_core.createMod(name) == nullptr) { return; } m_core.refresh(); - if (newPriority >= 0) - { + if (newPriority >= 0) { m_core.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); } diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index 2850d30a..f1215cec 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -36,10 +36,10 @@ public: void installMod(const QString& archivePath = "") const; // create an empty mod/a separator before the given mod or at - // the end of the list if the index is -1 + // the end of the list if the index is invalid // - void createEmptyMod(int modIndex) const; - void createSeparator(int modIndex) const; + void createEmptyMod(const QModelIndex& index = QModelIndex()) const; + void createSeparator(const QModelIndex& index = QModelIndex()) const; // check all mods for update // -- cgit v1.3.1