From c576f1ff40d81553e26c9f8fcfc01dc675fc9c1a Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sun, 26 Aug 2018 18:39:30 -0500 Subject: Refresh save game list when local game settings may have changed --- src/mainwindow.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3c749bf5..5908f9e4 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -84,6 +84,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include "localsavegames.h" #include #include @@ -1132,6 +1133,11 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) } else { activateSelectedProfile(); } + + LocalSavegames *saveGames = m_OrganizerCore.managedGame()->feature(); + if (saveGames != nullptr && saveGames->updateSaveGames(m_OrganizerCore.currentProfile())) { + refreshSaveList(); + } } } @@ -1973,6 +1979,12 @@ void MainWindow::on_actionAdd_Profile_triggered() break; } } + + LocalSavegames *saveGames = m_OrganizerCore.managedGame()->feature(); + if (saveGames != nullptr && saveGames->updateSaveGames(m_OrganizerCore.currentProfile())) { + refreshSaveList(); + } + // addProfile(); } -- cgit v1.3.1 From 138154aca0fcb27e3e09e2de3f895c5e1de99e3f Mon Sep 17 00:00:00 2001 From: Al12rs Date: Fri, 31 Aug 2018 22:16:13 +0200 Subject: Added "Refresh" button to downloads tab. --- src/mainwindow.cpp | 5 + src/mainwindow.h | 1 + src/mainwindow.ui | 14 ++ src/organizer_en.ts | 554 ++++++++++++++++++++++++++-------------------------- 4 files changed, 300 insertions(+), 274 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5908f9e4..dd8b1447 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1764,6 +1764,11 @@ void MainWindow::on_btnRefreshData_clicked() m_OrganizerCore.refreshDirectoryStructure(); } +void MainWindow::on_btnRefreshDownloads_clicked() +{ + m_OrganizerCore.downloadManager()->refreshList(); +} + void MainWindow::on_tabWidget_currentChanged(int index) { if (index == 0) { diff --git a/src/mainwindow.h b/src/mainwindow.h index ad0972da..b3d179e8 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -575,6 +575,7 @@ private slots: // ui slots void on_bsaList_customContextMenuRequested(const QPoint &pos); void on_clearFiltersButton_clicked(); void on_btnRefreshData_clicked(); + void on_btnRefreshDownloads_clicked(); void on_categoriesList_customContextMenuRequested(const QPoint &pos); void on_conflictsCheckBox_toggled(bool checked); void on_dataTree_customContextMenuRequested(const QPoint &pos); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index cf0f2aa4..75be7536 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1218,6 +1218,20 @@ p, li { white-space: pre-wrap; } 2 + + + Refresh downloads view + + + Refresh + + + + :/MO/gui/resources/view-refresh.png:/MO/gui/resources/view-refresh.png + + + + diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 6a045733..09d1fe79 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -1643,7 +1643,7 @@ p, li { white-space: pre-wrap; } - + Namefilter @@ -1769,8 +1769,9 @@ p, li { white-space: pre-wrap; } - - + + + Refresh @@ -1822,150 +1823,155 @@ p, li { white-space: pre-wrap; } - + + Refresh downloads view + + + + This is a list of mods you downloaded from Nexus. Double click one to install it. You can also drag an archive into here. - + Show Hidden - + Tool Bar - + Install Mod - + Install &Mod - + Install a new mod from an archive - + Ctrl+M - + Profiles - + &Profiles - + Configure Profiles - + Ctrl+P - + Executables - + &Executables - + Configure the executables that can be started through Mod Organizer - + Ctrl+E - - + + Tools - + &Tools - + Ctrl+I - + Settings - + &Settings - + Configure settings and workarounds - + Ctrl+S - + Nexus - + Search nexus network for more mods - + Ctrl+N - - + + Update - + Mod Organizer is up-to-date - - + + No Problems - + This button will be highlighted if MO discovered potential problems in your setup and provide tips on how to fix them. !Work in progress! @@ -1973,747 +1979,747 @@ Right now this has very limited functionality - - + + Help - + Ctrl+H - + Endorse MO - - + + Endorse Mod Organizer - + Copy Log to Clipboard - + Change Game - + Open the Instance selection dialog to manage a different Game - + Toolbar - + Desktop - + Start Menu - + There is no supported sort mechanism for this game. You will probably have to use a third-party tool. - + Problems - + There are potential problems with your setup - + Everything seems to be in order - + Help on UI - + Documentation Wiki - + Report Issue - + Tutorials - + About - + About Qt - + Name - + Please enter a name for the new profile - + failed to create profile: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress - + There are still downloads in progress, do you really want to quit? - + Plugin "%1" failed: %2 - + Plugin "%1" failed - + Browse Mod Page - + Also in: <br> - + No conflict - + <Edit...> - + This bsa is enabled in the ini file so it may be required! - + Activating Network Proxy - + Notice: Your current MO version (%1) is lower than the previous version (%2).<br>The GUI may not downgrade gracefully, so you may experience oddities.<br>However, there should be no serious issues. - + Choose Mod - + Mod Archive - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - + failed to spawn notepad.exe: %1 - + failed to change origin name: %1 - + failed to move "%1" from mod "%2" to "%3": %4 - + <Contains %1> - + <Checked> - + <Unchecked> - + <Update> - + <Managed by MO> - + <Managed outside MO> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - + You need to be logged in with Nexus to resume a download - - + + You need to be logged in with Nexus to endorse - + Failed to display overwrite dialog: %1 - + Nexus ID for this Mod is unknown - + Web page for this mod is unknown - - - + + + Create Mod... - + This will create an empty mod. Please enter a name: - - + + A mod with this name already exists - + This will move all files from overwrite into a new, regular mod. Please enter a name: - - + + Are you sure? - + About to recursively delete: - + Not logged in, endorsement information will be wrong - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Export to csv - + CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet. You can also use online editors and converters instead. - + Select what mods you want export: - + All installed mods - + Only active (checked) mods from your current profile - + All currently visible mods in the mod list - + Choose what Columns to export: - + Mod_Priority - + Mod_Name - + Mod_Status - + Primary_Category - + Nexus_ID - + Mod_Nexus_URL - + Mod_Version - + Install_Date - + Download_File_Name - + export failed: %1 - + Open Game folder - + Open MyGames folder - + Open Instance folder - + Open Mods folder - + Open Profile folder - + Open Downloads folder - + Open MO2 Install folder - + Open MO2 Plugins folder - + Open MO2 Logs folder - + Install Mod... - + Create empty mod - + Enable all visible - + Disable all visible - + Check all for update - + Export to csv... - + All Mods - + Sync to Mods... - + Clear Overwrite... - - + + Open in Explorer - + Restore Backup - + Remove Backup... - + Change Categories - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - - + + Enable selected - - + + Disable selected - + Rename Mod... - + Reinstall Mod - + Remove Mod... - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Mark as converted/working - + Visit on Nexus - + Visit web page - + Information... - - + + Exception: - - + + Unknown exception - + <All> - + <Multiple> - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -2721,12 +2727,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -2734,319 +2740,319 @@ You can also use online editors and converters instead. - + failed to remove %1 - + failed to create %1 - + Can't change download directory while downloads are in progress! - + failed to write to file %1 - + %1 written - + Select binary - + Binary - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Thank you! - + Thank you for your endorsement! - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - - + + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Extract... - + This will restart MO, continue? - + Edit Categories... - + Deselect filter - + Remove - + Enable all - + Disable all - + Unlock load order - + Lock load order - + depends on missing "%1" - + incompatible with "%1" - + Please wait while LOOT is running - + loot failed. Exit code was: %1 - + failed to start loot - + failed to run loot: %1 - + Errors occured - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of modlist created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file @@ -5294,18 +5300,18 @@ If the folder was still in use, restart MO and try again. - + Please use "Help" from the toolbar to get usage instructions to all elements - - + + <Manage...> - + failed to parse profile %1: %2 -- cgit v1.3.1 From 979206228d6bcfd71fb22c6a9b8dd4f54f7b5a75 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Fri, 7 Sep 2018 20:53:42 +0200 Subject: Added Mod Backups filter for modlist. --- src/categories.h | 6 ++++-- src/mainwindow.cpp | 1 + src/modlistsortproxy.cpp | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/categories.h b/src/categories.h index 474a1440..48e0b44b 100644 --- a/src/categories.h +++ b/src/categories.h @@ -47,8 +47,10 @@ public: static const int CATEGORY_SPECIAL_NOCATEGORY = 10003; static const int CATEGORY_SPECIAL_CONFLICT = 10004; static const int CATEGORY_SPECIAL_NOTENDORSED = 10005; - static const int CATEGORY_SPECIAL_MANAGED = 10006; - static const int CATEGORY_SPECIAL_UNMANAGED = 10007; + static const int CATEGORY_SPECIAL_BACKUP = 10006; + static const int CATEGORY_SPECIAL_MANAGED = 10007; + static const int CATEGORY_SPECIAL_UNMANAGED = 10008; + public: diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index dd8b1447..28d561b9 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2223,6 +2223,7 @@ void MainWindow::refreshFilters() addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_CHECKED, ModListSortProxy::TYPE_SPECIAL); addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_UNCHECKED, ModListSortProxy::TYPE_SPECIAL); addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE, ModListSortProxy::TYPE_SPECIAL); + addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_BACKUP, ModListSortProxy::TYPE_SPECIAL); addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_MANAGED, ModListSortProxy::TYPE_SPECIAL); addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_UNMANAGED, ModListSortProxy::TYPE_SPECIAL); addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY, ModListSortProxy::TYPE_SPECIAL); diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 88084ae1..59784642 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -270,6 +270,9 @@ bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) cons case CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED: { ModInfo::EEndorsedState state = info->endorsedState(); if (state != ModInfo::ENDORSED_FALSE) return false; + } break; + case CategoryFactory::CATEGORY_SPECIAL_BACKUP: { + if (!info->hasFlag(ModInfo::FLAG_BACKUP)) return false; } break; case CategoryFactory::CATEGORY_SPECIAL_MANAGED: { if (info->hasFlag(ModInfo::FLAG_FOREIGN)) return false; @@ -312,6 +315,9 @@ bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const case CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED: { ModInfo::EEndorsedState state = info->endorsedState(); if ((state == ModInfo::ENDORSED_FALSE) || (state == ModInfo::ENDORSED_NEVER)) return true; + } break; + case CategoryFactory::CATEGORY_SPECIAL_BACKUP: { + if (info->hasFlag(ModInfo::FLAG_BACKUP)) return true; } break; case CategoryFactory::CATEGORY_SPECIAL_MANAGED: { if (!info->hasFlag(ModInfo::FLAG_FOREIGN)) return true; -- cgit v1.3.1 From 67fb7e56d3a080844927d4d41764da1e9bb39795 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Fri, 7 Sep 2018 22:04:03 +0200 Subject: Add option to create a Mod backup from the modlist context menu. --- src/installationmanager.h | 4 +++- src/mainwindow.cpp | 12 ++++++++++++ src/mainwindow.h | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src/mainwindow.cpp') diff --git a/src/installationmanager.h b/src/installationmanager.h index e17aee17..e66184d3 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -147,6 +147,8 @@ public: */ virtual bool testOverwrite(MOBase::GuessedValue &modName, bool *merge = nullptr) const; + QString generateBackupName(const QString &directoryName) const; + private: void queryPassword(QString *password); @@ -171,7 +173,7 @@ private: bool doInstall(MOBase::GuessedValue &modName, QString gameName, int modID, const QString &version, const QString &newestVersion, int categoryID, const QString &repository); - QString generateBackupName(const QString &directoryName) const; + //QString generateBackupName(const QString &directoryName) const; bool ensureValidModName(MOBase::GuessedValue &name) const; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 28d561b9..57988663 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2409,6 +2409,16 @@ void MainWindow::reinstallMod_clicked() } } +void MainWindow::backupMod_clicked() +{ + ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); + QString backupDirectory = m_OrganizerCore.installationManager()->generateBackupName(modInfo->absolutePath()); + if (!copyDir(modInfo->absolutePath(), backupDirectory, false)) { + QMessageBox::information(this, tr("Failed"), + tr("Failed to create backup.")); + } + m_OrganizerCore.refreshModList(); +} void MainWindow::resumeDownload(int downloadIndex) { @@ -3626,6 +3636,8 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addAction(tr("Rename Mod..."), this, SLOT(renameMod_clicked())); menu->addAction(tr("Reinstall Mod"), this, SLOT(reinstallMod_clicked())); menu->addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked())); + menu->addAction(tr("Create Backup"), this, SLOT(backupMod_clicked())); + menu->addSeparator(); diff --git a/src/mainwindow.h b/src/mainwindow.h index b3d179e8..0bf3d3c8 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -407,6 +407,7 @@ private slots: void restoreBackup_clicked(); void renameMod_clicked(); void removeMod_clicked(); + void backupMod_clicked(); void reinstallMod_clicked(); void endorse_clicked(); void dontendorse_clicked(); -- cgit v1.3.1 From a3cb4001521fa4b29252ce6f9c652fb07b335bf4 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sat, 15 Sep 2018 14:12:24 +0200 Subject: Store and load major dialogs geometry information. --- src/mainwindow.cpp | 42 +++++++++++++++++++++++++++++++++++++++++- src/settings.cpp | 8 ++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 57988663..0a4ab912 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1906,10 +1906,16 @@ bool MainWindow::modifyExecutablesDialog() EditExecutablesDialog dialog(*m_OrganizerCore.executablesList(), *m_OrganizerCore.modList(), m_OrganizerCore.currentProfile()); + QSettings &settings = m_OrganizerCore.settings().directInterface(); + QString key = QString("geometry/%1").arg(dialog.objectName()); + if (settings.contains(key)) { + dialog.restoreGeometry(settings.value(key).toByteArray()); + } if (dialog.exec() == QDialog::Accepted) { m_OrganizerCore.setExecutablesList(dialog.getExecutablesList()); result = true; } + settings.setValue(key, dialog.saveGeometry()); refreshExecutablesList(); } catch (const std::exception &e) { reportError(e.what()); @@ -1975,10 +1981,16 @@ void MainWindow::on_actionAdd_Profile_triggered() ProfilesDialog profilesDialog(m_OrganizerCore.currentProfile()->name(), m_OrganizerCore.managedGame(), this); + QSettings &settings = m_OrganizerCore.settings().directInterface(); + QString key = QString("geometry/%1").arg(profilesDialog.objectName()); + if (settings.contains(key)) { + profilesDialog.restoreGeometry(settings.value(key).toByteArray()); + } // workaround: need to disable monitoring of the saves directory, otherwise the active // profile directory is locked stopMonitorSaves(); profilesDialog.exec(); + settings.setValue(key, profilesDialog.saveGeometry()); refreshSaveList(); // since the save list may now be outdated we have to refresh it completely if (refreshProfiles() && !profilesDialog.failed()) { break; @@ -2496,6 +2508,9 @@ void MainWindow::overwriteClosed(int) OverwriteInfoDialog *dialog = this->findChild("__overwriteDialog"); if (dialog != nullptr) { m_OrganizerCore.modList()->modInfoChanged(dialog->modInfo()); + QSettings &settings = m_OrganizerCore.settings().directInterface(); + QString key = QString("geometry/%1").arg(dialog->objectName()); + settings.setValue(key, dialog->saveGeometry()); dialog->deleteLater(); } m_OrganizerCore.refreshDirectoryStructure(); @@ -2518,6 +2533,11 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, } else { qobject_cast(dialog)->setModInfo(modInfo); } + QSettings &settings = m_OrganizerCore.settings().directInterface(); + QString key = QString("geometry/%1").arg(dialog->objectName()); + if (settings.contains(key)) { + dialog->restoreGeometry(settings.value(key).toByteArray()); + } dialog->show(); dialog->raise(); dialog->activateWindow(); @@ -2541,7 +2561,12 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, dialog.openTab(tab); } - dialog.restoreTabState(m_OrganizerCore.settings().directInterface().value("mod_info_tabs").toByteArray()); + dialog.restoreTabState(m_OrganizerCore.settings().directInterface().value("mod_info_tabs").toByteArray()); + QSettings &settings = m_OrganizerCore.settings().directInterface(); + QString key = QString("geometry/%1").arg(dialog.objectName()); + if (settings.contains(key)) { + dialog.restoreGeometry(settings.value(key).toByteArray()); + } //If no tab was specified use the first tab from the left based on the user order. if (tab == -1) { @@ -2555,6 +2580,8 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, dialog.exec(); m_OrganizerCore.settings().directInterface().setValue("mod_info_tabs", dialog.saveTabState()); + settings.setValue(key, dialog.saveGeometry()); + modInfo->saveMeta(); emit modInfoDisplayed(); @@ -4285,7 +4312,13 @@ void MainWindow::previewDataFile() addFunc(alt.first); } if (preview.numVariants() > 0) { + QSettings &settings = m_OrganizerCore.settings().directInterface(); + QString key = QString("geometry/%1").arg(preview.objectName()); + if (settings.contains(key)) { + preview.restoreGeometry(settings.value(key).toByteArray()); + } preview.exec(); + settings.setValue(key, preview.saveGeometry()); } else { QMessageBox::information(this, tr("Sorry"), tr("Sorry, can't preview anything. This function currently does not support extracting from bsas.")); } @@ -4746,9 +4779,16 @@ void MainWindow::on_displayCategoriesBtn_toggled(bool checked) void MainWindow::editCategories() { CategoriesDialog dialog(this); + QSettings &settings = m_OrganizerCore.settings().directInterface(); + QString key = QString("geometry/%1").arg(dialog.objectName()); + if (settings.contains(key)) { + dialog.restoreGeometry(settings.value(key).toByteArray()); + } if (dialog.exec() == QDialog::Accepted) { dialog.commitChanges(); } + settings.setValue(key, dialog.saveGeometry()); + } void MainWindow::deselectFilters() diff --git a/src/settings.cpp b/src/settings.cpp index db4ea565..6ef43ace 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -604,6 +604,12 @@ void Settings::query(PluginContainer *pluginContainer, QWidget *parent) tabs.push_back(std::unique_ptr(new PluginsTab(this, dialog))); tabs.push_back(std::unique_ptr(new WorkaroundsTab(this, dialog))); + + QString key = QString("geometry/%1").arg(dialog.objectName()); + if (m_Settings.contains(key)) { + dialog.restoreGeometry(m_Settings.value(key).toByteArray()); + } + if (dialog.exec() == QDialog::Accepted) { // remember settings before change QMap before; @@ -631,6 +637,8 @@ void Settings::query(PluginContainer *pluginContainer, QWidget *parent) } m_Settings.endGroup(); } + m_Settings.setValue(key, dialog.saveGeometry()); + } Settings::SettingsTab::SettingsTab(Settings *m_parent, SettingsDialog &m_dialog) -- cgit v1.3.1 From b1a3bdd6e8dba5df77f25b3813bcb73043b14bbc Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sun, 16 Sep 2018 23:44:09 -0500 Subject: Automatically set CDN as a preferred server This also removes the (free) and (premium) labels from the servers as they aren't useful anymore. Premium users get premium speeds on all servers. Added (automatic) tag to the CDN server to indicate it will automatically pick servers based on geography. --- src/mainwindow.cpp | 2 +- src/settings.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0a4ab912..04491c3e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4580,7 +4580,7 @@ void MainWindow::nxmDownloadURLs(QString, int, int, QVariant, QVariant resultDat info.name = serverInfo["Name"].toString(); info.premium = serverInfo["IsPremium"].toBool(); info.lastSeen = QDate::currentDate(); - info.preferred = 0; + info.preferred = !info.name.compare("CDN", Qt::CaseInsensitive); // other keys: ConnectedUsers, Country, URI servers.append(info); } diff --git a/src/settings.cpp b/src/settings.cpp index 6ef43ace..2dbca0eb 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -604,7 +604,7 @@ void Settings::query(PluginContainer *pluginContainer, QWidget *parent) tabs.push_back(std::unique_ptr(new PluginsTab(this, dialog))); tabs.push_back(std::unique_ptr(new WorkaroundsTab(this, dialog))); - + QString key = QString("geometry/%1").arg(dialog.objectName()); if (m_Settings.contains(key)) { dialog.restoreGeometry(m_Settings.value(key).toByteArray()); @@ -838,9 +838,10 @@ Settings::NexusTab::NexusTab(Settings *parent, SettingsDialog &dialog) m_Settings.beginGroup("Servers"); for (const QString &key : m_Settings.childKeys()) { QVariantMap val = m_Settings.value(key).toMap(); - QString type = val["premium"].toBool() ? "(premium)" : "(free)"; - - QString descriptor = key + " " + type; + QString descriptor = key; + if (!descriptor.compare("CDN", Qt::CaseInsensitive)) { + descriptor += QStringLiteral(" (automatic)"); + } if (val.contains("downloadSpeed") && val.contains("downloadCount") && (val["downloadCount"].toInt() > 0)) { int bps = static_cast(val["downloadSpeed"].toDouble() / val["downloadCount"].toInt()); descriptor += QString(" (%1 kbps)").arg(bps / 1024); -- cgit v1.3.1 From 15ab8d5c5e9b5a06ad61247aa81f4673a42595e6 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sat, 29 Sep 2018 15:07:24 +0200 Subject: Changed "ignore missing game data" to not create an empty Texture folder and instead save the choice in meta.ini --- src/mainwindow.cpp | 6 ++++-- src/modinfo.h | 5 +++++ src/modinforegular.cpp | 16 +++++++++++++--- src/modinforegular.h | 7 +++++++ src/modlist.cpp | 4 +++- 5 files changed, 32 insertions(+), 6 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 04491c3e..9d0c37c0 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2684,16 +2684,18 @@ void MainWindow::ignoreMissingData_clicked() for (QModelIndex idx : selection->selectedRows()) { int row_idx = idx.data(Qt::UserRole + 1).toInt(); ModInfo::Ptr info = ModInfo::getByIndex(row_idx); - QDir(info->absolutePath()).mkdir("textures"); + //QDir(info->absolutePath()).mkdir("textures"); info->testValid(); + info->markValidated(true); connect(this, SIGNAL(modListDataChanged(QModelIndex, QModelIndex)), m_OrganizerCore.modList(), SIGNAL(dataChanged(QModelIndex, QModelIndex))); emit modListDataChanged(m_OrganizerCore.modList()->index(row_idx, 0), m_OrganizerCore.modList()->index(row_idx, m_OrganizerCore.modList()->columnCount() - 1)); } } else { ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); - QDir(info->absolutePath()).mkdir("textures"); + //QDir(info->absolutePath()).mkdir("textures"); info->testValid(); + info->markValidated(true); connect(this, SIGNAL(modListDataChanged(QModelIndex, QModelIndex)), m_OrganizerCore.modList(), SIGNAL(dataChanged(QModelIndex, QModelIndex))); emit modListDataChanged(m_OrganizerCore.modList()->index(m_ContextRow, 0), m_OrganizerCore.modList()->index(m_ContextRow, m_OrganizerCore.modList()->columnCount() - 1)); diff --git a/src/modinfo.h b/src/modinfo.h index 919d1a5b..895105df 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -577,6 +577,11 @@ public: */ virtual void markConverted(bool converted) {} + /** + * @brief updates the mod to flag it as valid in order to ignore the invalid game data flag + */ + virtual void markValidated(bool validated) {} + /** * @brief reads meta information from disk */ diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index 1e77e050..1d84e2c8 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -31,6 +31,7 @@ ModInfoRegular::ModInfoRegular(PluginContainer *pluginContainer, const IPluginGa , m_GameName(game->gameShortName()) , m_IsAlternate(false) , m_Converted(false) + , m_Validated(false) , m_MetaInfoChanged(false) , m_EndorsedState(ENDORSED_UNKNOWN) , m_NexusBridge(pluginContainer) @@ -86,6 +87,7 @@ void ModInfoRegular::readMeta() m_NexusDescription = metaFile.value("nexusDescription", "").toString(); m_Repository = metaFile.value("repository", "Nexus").toString(); m_Converted = metaFile.value("converted", false).toBool(); + m_Validated = metaFile.value("validated", false).toBool(); m_URL = metaFile.value("url", "").toString(); m_LastNexusQuery = QDateTime::fromString(metaFile.value("lastNexusQuery", "").toString(), Qt::ISODate); if (metaFile.contains("endorsed")) { @@ -150,6 +152,7 @@ void ModInfoRegular::saveMeta() metaFile.setValue("url", m_URL); metaFile.setValue("lastNexusQuery", m_LastNexusQuery.toString(Qt::ISODate)); metaFile.setValue("converted", m_Converted); + metaFile.setValue("validated", m_Validated); if (m_EndorsedState != ENDORSED_UNKNOWN) { metaFile.setValue("endorsed", m_EndorsedState); } @@ -415,6 +418,13 @@ void ModInfoRegular::markConverted(bool converted) emit modDetailsUpdated(true); } +void ModInfoRegular::markValidated(bool validated) +{ + m_Validated = validated; + m_MetaInfoChanged = true; + saveMeta(); + emit modDetailsUpdated(true); +} QString ModInfoRegular::absolutePath() const { @@ -438,7 +448,7 @@ std::vector ModInfoRegular::getFlags() const if ((m_NexusID > 0) && (endorsedState() == ENDORSED_FALSE)) { result.push_back(ModInfo::FLAG_NOTENDORSED); } - if (!isValid()) { + if (!isValid() && !m_Validated) { result.push_back(ModInfo::FLAG_INVALID); } if (m_Notes.length() != 0) { @@ -514,7 +524,7 @@ std::vector ModInfoRegular::getContents() const int ModInfoRegular::getHighlight() const { - if (!isValid()) + if (!isValid() && !m_Validated) return HIGHLIGHT_INVALID; auto flags = getFlags(); if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_PLUGIN_SELECTED) != flags.end()) @@ -525,7 +535,7 @@ int ModInfoRegular::getHighlight() const QString ModInfoRegular::getDescription() const { - if (!isValid()) { + if (!isValid() && !m_Validated) { return tr("%1 contains no esp/esm/esl and no asset (textures, meshes, interface, ...) directory").arg(name()); } else { const std::set &categories = getCategories(); diff --git a/src/modinforegular.h b/src/modinforegular.h index 3abaded4..2f3eb941 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -28,6 +28,7 @@ public: bool isAlternate() { return m_IsAlternate; } bool isConverted() { return m_Converted; } + bool isValidated() { return m_Validated; } /** * @brief test if there is a newer version of the mod @@ -188,6 +189,11 @@ public: */ virtual void markConverted(bool converted) override; + /** + * @brief updates the mod to flag it as valid in order to ignore the invalid game data flag + */ + virtual void markValidated(bool validated) override; + /** * @brief getter for the mod name * @@ -358,6 +364,7 @@ private: bool m_MetaInfoChanged; bool m_IsAlternate; bool m_Converted; + bool m_Validated; MOBase::VersionInfo m_NewestVersion; MOBase::VersionInfo m_IgnoredVersion; diff --git a/src/modlist.cpp b/src/modlist.cpp index bcd087bc..c7f6a53b 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -768,6 +768,8 @@ IModList::ModStates ModList::state(unsigned int modIndex) const QSharedPointer modInfoRegular = modInfo.staticCast(); if (modInfoRegular->isAlternate() && !modInfoRegular->isConverted()) result |= IModList::STATE_ALTERNATE; + if (!modInfo->isValid() && modInfoRegular->isValidated()) + result |= IModList::STATE_VALID; } if (modInfo->canBeEnabled()) { if (m_Profile->modEnabled(modIndex)) { @@ -1263,4 +1265,4 @@ void ModList::disableSelected(const QItemSelectionModel *selectionModel) } } } -} \ No newline at end of file +} -- cgit v1.3.1 From 9c35443780832d6876c63b42bcf216958f02068c Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sat, 13 Oct 2018 10:42:12 +0200 Subject: Fixed UI file to not break each time it's opened in creator. --- src/mainwindow.cpp | 2 +- src/mainwindow.ui | 27 +-- src/organizer_en.ts | 522 ++++++++++++++++++++++++++-------------------------- 3 files changed, 278 insertions(+), 273 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9d0c37c0..04bd96c7 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -307,7 +307,7 @@ MainWindow::MainWindow(QSettings &initSettings ui->espList->setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(ui->espList)); ui->espList->installEventFilter(m_OrganizerCore.pluginList()); - ui->bsaList->setLocalMoveOnly(true); + //ui->bsaList->setLocalMoveOnly(true); bool pluginListAdjusted = registerWidgetState(ui->espList->objectName(), ui->espList->header(), "plugin_list_state"); registerWidgetState(ui->dataTree->objectName(), ui->dataTree->header()); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 75be7536..f271f817 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -826,7 +826,7 @@ p, li { white-space: pre-wrap; } - true + false Sort @@ -1042,7 +1042,7 @@ p, li { white-space: pre-wrap; } - + Qt::CustomContextMenu @@ -1058,21 +1058,26 @@ p, li { white-space: pre-wrap; } false - + false - + false - + 20 - + true - + 1 + + + Archives + + @@ -1218,7 +1223,7 @@ p, li { white-space: pre-wrap; } 2 - + Refresh downloads view @@ -1231,7 +1236,7 @@ p, li { white-space: pre-wrap; } - + @@ -1257,7 +1262,7 @@ p, li { white-space: pre-wrap; } Qt::ScrollBarAlwaysOn - Qt::ScrollBarAsNeeded + Qt::ScrollBarAsNeeded true @@ -1292,7 +1297,7 @@ p, li { white-space: pre-wrap; } 50 - + 15 diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 2490f390..9a7e0713 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -1607,7 +1607,7 @@ p, li { white-space: pre-wrap; } - + Create Backup @@ -1644,7 +1644,7 @@ p, li { white-space: pre-wrap; } - + Namefilter @@ -1754,61 +1754,61 @@ p, li { white-space: pre-wrap; } - + Data - + refresh data-directory overview - + Refresh the overview. This may take a moment. - - - - + + + + Refresh - + This is an overview of your data directory as visible to the game (and tools). - + File - + Mod - - + + Filter the above list so that only conflicts are displayed. - + Show only conflicts - + Saves - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1819,160 +1819,160 @@ p, li { white-space: pre-wrap; } - + Downloads - + Refresh downloads view - + This is a list of mods you downloaded from Nexus. Double click one to install it. You can also drag an archive into here. - + Show Hidden - + Tool Bar - + Install Mod - + Install &Mod - + Install a new mod from an archive - + Ctrl+M - + Profiles - + &Profiles - + Configure Profiles - + Ctrl+P - + Executables - + &Executables - + Configure the executables that can be started through Mod Organizer - + Ctrl+E - - + + Tools - + &Tools - + Ctrl+I - + Settings - + &Settings - + Configure settings and workarounds - + Ctrl+S - + Nexus - + Search nexus network for more mods - + Ctrl+N - - + + Update - + Mod Organizer is up-to-date - + No Problems - + This button will be highlighted if MO discovered potential problems in your setup and provide tips on how to fix them. !Work in progress! @@ -1980,39 +1980,39 @@ Right now this has very limited functionality - - + + Help - + Ctrl+H - + Endorse MO - - + + Endorse Mod Organizer - + Copy Log to Clipboard - + Change Game - + Open the Instance selection dialog to manage a different Game @@ -2268,16 +2268,16 @@ Right now this has very limited functionality - - + + failed to rename "%1" to "%2" - - - + + + Confirm @@ -2330,408 +2330,408 @@ Right now this has very limited functionality - + Nexus ID for this Mod is unknown - + Web page for this mod is unknown - - - + + + Create Mod... - + This will create an empty mod. Please enter a name: - - + + A mod with this name already exists - + This will move all files from overwrite into a new, regular mod. Please enter a name: - - + + Are you sure? - + About to recursively delete: - + Not logged in, endorsement information will be wrong - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Export to csv - + CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet. You can also use online editors and converters instead. - + Select what mods you want export: - + All installed mods - + Only active (checked) mods from your current profile - + All currently visible mods in the mod list - + Choose what Columns to export: - + Mod_Priority - + Mod_Name - + Mod_Status - + Primary_Category - + Nexus_ID - + Mod_Nexus_URL - + Mod_Version - + Install_Date - + Download_File_Name - + export failed: %1 - + Open Game folder - + Open MyGames folder - + Open Instance folder - + Open Mods folder - + Open Profile folder - + Open Downloads folder - + Open MO2 Install folder - + Open MO2 Plugins folder - + Open MO2 Logs folder - + Install Mod... - + Create empty mod - + Enable all visible - + Disable all visible - + Check all for update - + Export to csv... - + All Mods - + Sync to Mods... - + Clear Overwrite... - - + + Open in Explorer - + Restore Backup - + Remove Backup... - + Change Categories - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - - + + Enable selected - - + + Disable selected - + Rename Mod... - + Reinstall Mod - + Remove Mod... - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Mark as converted/working - + Visit on Nexus - + Visit web page - + Information... - - + + Exception: - - + + Unknown exception - + <All> - + <Multiple> - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -2739,12 +2739,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -2752,319 +2752,319 @@ You can also use online editors and converters instead. - + failed to remove %1 - + failed to create %1 - + Can't change download directory while downloads are in progress! - + failed to write to file %1 - + %1 written - + Select binary - + Binary - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Thank you! - + Thank you for your endorsement! - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - - + + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Extract... - + This will restart MO, continue? - + Edit Categories... - + Deselect filter - + Remove - + Enable all - + Disable all - + Unlock load order - + Lock load order - + depends on missing "%1" - + incompatible with "%1" - + Please wait while LOOT is running - + loot failed. Exit code was: %1 - + failed to start loot - + failed to run loot: %1 - + Errors occured - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of modlist created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file @@ -3426,37 +3426,37 @@ p, li { white-space: pre-wrap; } - + about:blank - + Endorse - + Notes - + Filetree - + Open Mod in Explorer - + A directory view of this mod - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -3466,17 +3466,17 @@ p, li { white-space: pre-wrap; } - + Previous - + Next - + Close @@ -3786,18 +3786,18 @@ p, li { white-space: pre-wrap; } ModInfoRegular - + failed to write %1/meta.ini: error %2 - + %1 contains no esp/esm/esl and no asset (textures, meshes, interface, ...) directory - + Categories: <br> @@ -3945,113 +3945,113 @@ p, li { white-space: pre-wrap; } - + drag&drop failed: %1 - + Confirm - + Are you sure you want to remove "%1"? - + Flags - + Content - + Mod Name - + Version - + Priority - + Category - + Source Game - + Nexus ID - + Installation - - + + unknown - + Name of your mods - + Version of the mod (if available) - + Installation priority of your mod. The higher, the more "important" it is and thus overwrites files from mods with lower priority. - + Category of the mod. - + The source game which was the origin of this mod. - + Id of the mod as used on Nexus. - + Emblemes to highlight things that might require attention. - + Depicts the content of the mod:<br><table cellspacing=7><tr><td><img src=":/MO/gui/content/plugin" width=32/></td><td>Game plugins (esp/esm/esl)</td></tr><tr><td><img src=":/MO/gui/content/interface" width=32/></td><td>Interface</td></tr><tr><td><img src=":/MO/gui/content/mesh" width=32/></td><td>Meshes</td></tr><tr><td><img src=":/MO/gui/content/bsa" width=32/></td><td>BSA</td></tr><tr><td><img src=":/MO/gui/content/texture" width=32/></td><td>Textures</td></tr><tr><td><img src=":/MO/gui/content/sound" width=32/></td><td>Sounds</td></tr><tr><td><img src=":/MO/gui/content/music" width=32/></td><td>Music</td></tr><tr><td><img src=":/MO/gui/content/string" width=32/></td><td>Strings</td></tr><tr><td><img src=":/MO/gui/content/script" width=32/></td><td>Scripts (Papyrus)</td></tr><tr><td><img src=":/MO/gui/content/skse" width=32/></td><td>Script Extender plugins</td></tr><tr><td><img src=":/MO/gui/content/skyproc" width=32/></td><td>SkyProc Patcher</td></tr><tr><td><img src=":/MO/gui/content/menu" width=32/></td><td>Mod Configuration Menu</td></tr><tr><td><img src=":/MO/gui/content/inifile" width=32/></td><td>INI files</td></tr><tr><td><img src=":/MO/gui/content/modgroup" width=32/></td><td>ModGroup files</td></tr></table> - + Time this mod was installed @@ -5323,7 +5323,7 @@ If the folder was still in use, restart MO and try again. - + <Manage...> -- cgit v1.3.1 From 42d2344f0b81cb1cf8dbe83e8f480b84ed0c3114 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 28 Oct 2018 02:16:20 +0200 Subject: Added modlist Separators feature: *New modType separator. *Bold, italic and centralized name without _separator at the end. *Added "create separator" option to context menu. *Added "rename separator" option. *Fixed rename separator issue with priority resetting. --- src/mainwindow.cpp | 42 +++++++++++++++++++++++--- src/mainwindow.h | 1 + src/modinfo.cpp | 8 +++-- src/modinfo.h | 3 +- src/modinfodialog.cpp | 17 ++++++++++- src/modinfoseparator.cpp | 36 ++++++++++++++++++++++ src/modinfoseparator.h | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ src/modlist.cpp | 29 ++++++++++++++++-- 8 files changed, 202 insertions(+), 11 deletions(-) create mode 100644 src/modinfoseparator.cpp create mode 100644 src/modinfoseparator.h (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 04bd96c7..bc25ab70 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2629,8 +2629,9 @@ void MainWindow::modOpenNext(int tab) ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow); std::vector flags = mod->getFlags(); if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) || - (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end())) { - // skip overwrite and backups + (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) || + (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end())) { + // skip overwrite and backups and separators modOpenNext(tab); } else { displayModInformation(m_ContextRow,tab); @@ -2649,8 +2650,9 @@ void MainWindow::modOpenPrev(int tab) ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow); std::vector flags = mod->getFlags(); if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) || - (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end())) { - // skip overwrite and backups + (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) || + (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end())) { + // skip overwrite and backups and separators modOpenPrev(tab); } else { displayModInformation(m_ContextRow,tab); @@ -2839,6 +2841,33 @@ void MainWindow::createEmptyMod_clicked() m_OrganizerCore.refreshModList(); } +void MainWindow::createSeparator_clicked() +{ + GuessedValue name; + name.setFilter(&fixDirectoryName); + while (name->isEmpty()) + { + bool ok; + name.update(QInputDialog::getText(this, 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_OrganizerCore.getMod(name) != nullptr) + { + reportError(tr("A separator with this name already exists")); + return; + } + name->append("_separator"); + if (m_OrganizerCore.getMod(name) != nullptr) + { + return; + } + if (m_OrganizerCore.createMod(name) == nullptr) { return; } + m_OrganizerCore.refreshModList(); +} + void MainWindow::createModFromOverwrite() { GuessedValue name; @@ -3579,6 +3608,8 @@ QMenu *MainWindow::modListContextMenu() menu->addAction(tr("Create empty mod"), this, SLOT(createEmptyMod_clicked())); + menu->addAction(tr("Create separator"), this, SLOT(createSeparator_clicked())); + menu->addSeparator(); menu->addAction(tr("Enable all visible"), this, SLOT(enableVisibleMods())); @@ -3623,6 +3654,9 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) { menu->addAction(tr("Restore Backup"), this, SLOT(restoreBackup_clicked())); menu->addAction(tr("Remove Backup..."), this, SLOT(removeMod_clicked())); + } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()){ + menu->addAction(tr("Rename Separator..."), this, SLOT(renameMod_clicked())); + menu->addAction(tr("Remove Separator..."), this, SLOT(removeMod_clicked())); } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { // nop, nothing to do with this mod } else { diff --git a/src/mainwindow.h b/src/mainwindow.h index 0bf3d3c8..78d374e6 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -404,6 +404,7 @@ private slots: // modlist context menu void installMod_clicked(); void createEmptyMod_clicked(); + void createSeparator_clicked(); void restoreBackup_clicked(); void renameMod_clicked(); void removeMod_clicked(); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 8ce40da3..4c41a114 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "modinforegular.h" #include "modinfoforeign.h" #include "modinfooverwrite.h" +#include "modinfoseparator.h" #include "installationtester.h" #include "categories.h" @@ -64,11 +65,14 @@ bool ModInfo::ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS) ModInfo::Ptr ModInfo::createFrom(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &dir, DirectoryEntry **directoryStructure) { QMutexLocker locker(&s_Mutex); -// int id = s_NextID++; + // int id = s_NextID++; static QRegExp backupExp(".*backup[0-9]*"); + static QRegExp separatorExp(".*_separator"); ModInfo::Ptr result; if (backupExp.exactMatch(dir.dirName())) { result = ModInfo::Ptr(new ModInfoBackup(pluginContainer, game, dir, directoryStructure)); + } else if(separatorExp.exactMatch(dir.dirName())){ + result = Ptr(new ModInfoSeparator(pluginContainer, game, dir, directoryStructure)); } else { result = ModInfo::Ptr(new ModInfoRegular(pluginContainer, game, dir, directoryStructure)); } @@ -418,4 +422,4 @@ void ModInfo::testValid() while (dirIter.hasNext()) { dirIter.next(); } -} \ No newline at end of file +} diff --git a/src/modinfo.h b/src/modinfo.h index 895105df..9f6c2fbc 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -62,6 +62,7 @@ public: enum EFlag { FLAG_INVALID, FLAG_BACKUP, + FLAG_SEPARATOR, FLAG_OVERWRITE, FLAG_FOREIGN, FLAG_NOTENDORSED, @@ -560,7 +561,7 @@ public: /** * @return true if this mod is considered "valid", that is: it contains data used by the game **/ - bool isValid() const { return m_Valid; } + virtual bool isValid() const { return m_Valid; } /** * @return true if the file has been endorsed on nexus diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index fedddd6c..54910e41 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -134,7 +134,22 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo refreshLists(); - if (unmanaged) { + if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) + { + ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); + ui->tabWidget->setTabEnabled(TAB_INIFILES, false); + ui->tabWidget->setTabEnabled(TAB_IMAGES, false); + ui->tabWidget->setTabEnabled(TAB_ESPS, false); + ui->tabWidget->setTabEnabled(TAB_CONFLICTS, false); + //ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); + addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); + refreshPrimaryCategoriesBox(); + ui->tabWidget->setTabEnabled(TAB_NEXUS, false); + //ui->tabWidget->setTabEnabled(TAB_NOTES, false); + ui->tabWidget->setTabEnabled(TAB_FILETREE, false); + } + else if (unmanaged) + { ui->tabWidget->setTabEnabled(TAB_INIFILES, false); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); diff --git a/src/modinfoseparator.cpp b/src/modinfoseparator.cpp new file mode 100644 index 00000000..60014f66 --- /dev/null +++ b/src/modinfoseparator.cpp @@ -0,0 +1,36 @@ +#include "modinfoseparator.h" + +bool ModInfoSeparator::setName(const QString& name) +{ + return ModInfoRegular::setName(name); +} + +std::vector ModInfoSeparator::getFlags() const +{ + { + auto result = ModInfoRegular::getFlags(); + result.insert(result.begin(), FLAG_SEPARATOR); + return result; + } +} + +int ModInfoSeparator::getHighlight() const +{ + return HIGHLIGHT_CENTER; +} + +QString ModInfoSeparator::getDescription() const +{ + return tr("This is a Separator"); +} + +QString ModInfoSeparator::name() const +{ + return ModInfoRegular::name(); +} + + +ModInfoSeparator::ModInfoSeparator(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure) + : ModInfoRegular(pluginContainer, game, path, directoryStructure) +{ +} diff --git a/src/modinfoseparator.h b/src/modinfoseparator.h new file mode 100644 index 00000000..f215fb17 --- /dev/null +++ b/src/modinfoseparator.h @@ -0,0 +1,77 @@ +#ifndef MODINFOSEPARATOR_H +#define MODINFOSEPARATOR_H + +#include "modinforegular.h" + +class ModInfoSeparator: + public ModInfoRegular +{ + friend class ModInfo; + +public: + + virtual bool updateAvailable() const { return false; } + virtual bool updateIgnored() const { return false; } + virtual bool downgradeAvailable() const { return false; } + virtual bool updateNXMInfo() { return false; } + virtual bool isValid() const { return true; } + //TODO: Fix renaming method to avoid priority reset + virtual bool setName(const QString& name); + + virtual int getNexusID() const { return -1; } + + virtual void setGameName(QString /*gameName*/) + { + } + + virtual void setNexusID(int /*modID*/) + { + } + + virtual void endorse(bool /*doEndorse*/) + { + } + + virtual void ignoreUpdate(bool /*ignore*/) + { + } + + virtual bool canBeUpdated() const { return false; } + virtual bool canBeEnabled() const { return false; } + virtual std::vector getIniTweaks() const { return std::vector(); } + + virtual std::vector getFlags() const; + virtual int getHighlight() const; + + virtual QString getDescription() const; + virtual QString name() const; + virtual QString getGameName() const { return ""; } + virtual QString getInstallationFile() const { return ""; } + virtual QString getURL() const { return ""; } + virtual QString repository() const { return ""; } + + virtual QDateTime getLastNexusQuery() const { return QDateTime(); } + virtual QDateTime creationTime() const { return QDateTime(); } + + virtual void getNexusFiles + ( + QList::const_iterator& /*unused*/, + QList::const_iterator& /*unused*/) + { + } + + virtual QString getNexusDescription() const { return QString(); } + + virtual void addInstalledFile(int /*modId*/, int /*fileId*/) + { + } + +private: + + ModInfoSeparator + ( + PluginContainer* pluginContainer, const MOBase::IPluginGame* game, const QDir& path, + MOShared::DirectoryEntry** directoryStructure); +}; + +#endif diff --git a/src/modlist.cpp b/src/modlist.cpp index c7f6a53b..31ef9ddc 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -45,6 +45,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include @@ -145,6 +146,7 @@ QString ModList::getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const { switch (flag) { case ModInfo::FLAG_BACKUP: return tr("Backup"); + case ModInfo::FLAG_SEPARATOR: return tr("Separator"); case ModInfo::FLAG_INVALID: return tr("No valid game data"); case ModInfo::FLAG_NOTENDORSED: return tr("Not endorsed yet"); case ModInfo::FLAG_NOTES: return QString("%1").arg(modInfo->notes().replace("\n", "
")); @@ -203,7 +205,13 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const || (column == COL_CONTENT)) { return QVariant(); } else if (column == COL_NAME) { - return modInfo->name(); + auto flags = modInfo->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) + { + return modInfo->name().replace("_separator", ""); + } + else + return modInfo->name(); } else if (column == COL_VERSION) { VersionInfo verInfo = modInfo->getVersion(); QString version = verInfo.displayString(); @@ -277,6 +285,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QVariant(); } } else if (role == Qt::TextAlignmentRole) { + auto flags = modInfo->getFlags(); if (column == COL_NAME) { if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_CENTER) { return QVariant(Qt::AlignCenter | Qt::AlignVCenter); @@ -327,8 +336,15 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return modInfo->getGameName(); } else if (role == Qt::FontRole) { QFont result; + auto flags = modInfo->getFlags(); if (column == COL_NAME) { - if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_INVALID) { + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) + { + //result.setCapitalization(QFont::AllUppercase); + result.setItalic(true); + //result.setUnderline(true); + result.setBold(true); + } else if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_INVALID) { result.setItalic(true); } } else if ((column == COL_CATEGORY) && (modInfo->hasFlag(ModInfo::FLAG_FOREIGN))) { @@ -476,6 +492,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) ModInfo::Ptr info = ModInfo::getByIndex(modID); IModList::ModStates oldState = state(modID); + bool result = false; emit aboutToChangeData(); @@ -493,7 +510,13 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) } else if (role == Qt::EditRole) { switch (index.column()) { case COL_NAME: { - result = renameMod(modID, value.toString()); + auto flags = info->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) + { + result = renameMod(modID, value.toString() + "_separator"); + } + else + result = renameMod(modID, value.toString()); } break; case COL_PRIORITY: { bool ok = false; -- cgit v1.3.1 From dce6507dad1ff7d76a5c0497d516c79bacd08477 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 28 Oct 2018 15:04:50 +0100 Subject: Added Color costumization for Separators. --- src/mainwindow.cpp | 28 +++++++++++++++++++++++++++- src/mainwindow.h | 1 + src/modinfo.h | 11 +++++++++++ src/modinforegular.cpp | 13 +++++++++++++ src/modinforegular.h | 7 +++++++ src/modlist.cpp | 6 +++++- 6 files changed, 64 insertions(+), 2 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index bc25ab70..438ba037 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -154,6 +154,8 @@ along with Mod Organizer. If not, see . #include #include #include +#include +#include #include #include @@ -2868,6 +2870,26 @@ void MainWindow::createSeparator_clicked() m_OrganizerCore.refreshModList(); } +void MainWindow::setColor_clicked() +{ + ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); + QColor color = QColorDialog::getColor(modInfo->getColor(),this); + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { + for (QModelIndex idx : selection->selectedRows()) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + auto flags = info->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) + { + info->setColor(color); + } + } + } + else { + modInfo->setColor(color); + } +} + void MainWindow::createModFromOverwrite() { GuessedValue name; @@ -3608,7 +3630,7 @@ QMenu *MainWindow::modListContextMenu() menu->addAction(tr("Create empty mod"), this, SLOT(createEmptyMod_clicked())); - menu->addAction(tr("Create separator"), this, SLOT(createSeparator_clicked())); + menu->addAction(tr("Create Separator"), this, SLOT(createSeparator_clicked())); menu->addSeparator(); @@ -3655,8 +3677,12 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addAction(tr("Restore Backup"), this, SLOT(restoreBackup_clicked())); menu->addAction(tr("Remove Backup..."), this, SLOT(removeMod_clicked())); } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()){ + menu->addSeparator(); menu->addAction(tr("Rename Separator..."), this, SLOT(renameMod_clicked())); menu->addAction(tr("Remove Separator..."), this, SLOT(removeMod_clicked())); + menu->addSeparator(); + menu->addAction(tr("Select Color..."), this, SLOT(setColor_clicked())); + menu->addSeparator(); } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { // nop, nothing to do with this mod } else { diff --git a/src/mainwindow.h b/src/mainwindow.h index 78d374e6..02e368d7 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -408,6 +408,7 @@ private slots: void restoreBackup_clicked(); void renameMod_clicked(); void removeMod_clicked(); + void setColor_clicked(); void backupMod_clicked(); void reinstallMod_clicked(); void endorse_clicked(); diff --git a/src/modinfo.h b/src/modinfo.h index 9f6c2fbc..235fb50c 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -31,6 +31,7 @@ class QDir; #include #include #include +#include #include @@ -524,6 +525,16 @@ public: */ virtual QStringList archives() const = 0; + /* + *@return the color choosen by the user for the mod/separator + */ + virtual QColor getColor() { return QColor(); } + + /* + *@return true if the color has been set successfully. + */ + virtual void setColor(QColor color) { } + /** * @brief adds the information that a file has been installed into this mod * @param modId id of the mod installed diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index 1d84e2c8..d0f56a52 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -90,6 +90,7 @@ void ModInfoRegular::readMeta() m_Validated = metaFile.value("validated", false).toBool(); m_URL = metaFile.value("url", "").toString(); m_LastNexusQuery = QDateTime::fromString(metaFile.value("lastNexusQuery", "").toString(), Qt::ISODate); + m_Color = metaFile.value("color",QColor()).value(); if (metaFile.contains("endorsed")) { if (metaFile.value("endorsed").canConvert()) { switch (metaFile.value("endorsed").toInt()) { @@ -153,6 +154,7 @@ void ModInfoRegular::saveMeta() metaFile.setValue("lastNexusQuery", m_LastNexusQuery.toString(Qt::ISODate)); metaFile.setValue("converted", m_Converted); metaFile.setValue("validated", m_Validated); + metaFile.setValue("color", m_Color); if (m_EndorsedState != ENDORSED_UNKNOWN) { metaFile.setValue("endorsed", m_EndorsedState); } @@ -397,6 +399,17 @@ void ModInfoRegular::setNeverEndorse() } +void ModInfoRegular::setColor(QColor color) +{ + m_Color = color; + m_MetaInfoChanged = true; +} + +QColor ModInfoRegular::getColor() +{ + return m_Color; +} + bool ModInfoRegular::remove() { m_MetaInfoChanged = false; diff --git a/src/modinforegular.h b/src/modinforegular.h index 2f3eb941..78a6e58a 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -311,6 +311,10 @@ public: virtual QStringList archives() const; + virtual void setColor(QColor color); + + virtual QColor getColor(); + virtual void addInstalledFile(int modId, int fileId); /** @@ -355,9 +359,12 @@ private: QString m_URL; QString m_GameName; + QDateTime m_CreationTime; QDateTime m_LastNexusQuery; + QColor m_Color; + int m_NexusID; std::set> m_InstalledFileIDs; diff --git a/src/modlist.cpp b/src/modlist.cpp index 31ef9ddc..6caafeb2 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -383,12 +383,16 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QVariant(); } else if ((role == Qt::BackgroundRole) || (role == ViewMarkingScrollBar::DEFAULT_ROLE)) { + auto flags = modInfo->getFlags(); if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_PLUGIN) { return QColor(0, 0, 255, 64); } else if (m_Overwrite.find(modIndex) != m_Overwrite.end()) { return QColor(0, 255, 0, 64); - } else if (m_Overwritten.find(modIndex) != m_Overwritten.end()) { + } + else if (m_Overwritten.find(modIndex) != m_Overwritten.end()) { return QColor(255, 0, 0, 64); + } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end() && modInfo->getColor().isValid()) { + return modInfo->getColor(); } else { return QVariant(); } -- cgit v1.3.1 From 9e66799abf1e99cb507966e1780dd25fd7887df4 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 28 Oct 2018 16:15:27 +0100 Subject: Fixed Separators not getting deleted while deleting multiple selected items. --- src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 438ba037..3b2dfbb2 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2367,7 +2367,7 @@ void MainWindow::removeMod_clicked() continue; } mods += "
  • " + name + "
  • "; - modNames.append(name); + modNames.append(ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->name()); } if (QMessageBox::question(this, tr("Confirm"), tr("Remove the following mods?
      %1
    ").arg(mods), -- cgit v1.3.1 From ab95f78689e159722b79f991c7ef07c31cb3f958 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 28 Oct 2018 17:27:34 +0100 Subject: Added Reset color option for separators and changed the bahavior of "cancel" of the color selector dialog to keep previous color. --- src/mainwindow.cpp | 26 +++++++++++++++++++++++++- src/mainwindow.h | 1 + 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3b2dfbb2..74eecf06 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2873,7 +2873,29 @@ void MainWindow::createSeparator_clicked() void MainWindow::setColor_clicked() { ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); - QColor color = QColorDialog::getColor(modInfo->getColor(),this); + QColor color = QColorDialog::getColor(modInfo->getColor(), this); + if (!color.isValid()) + return; + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { + for (QModelIndex idx : selection->selectedRows()) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + auto flags = info->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) + { + info->setColor(color); + } + } + } + else { + modInfo->setColor(color); + } +} + +void MainWindow::resetColor_clicked() +{ + ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); + QColor color = QColor(); QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { @@ -3682,6 +3704,8 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addAction(tr("Remove Separator..."), this, SLOT(removeMod_clicked())); menu->addSeparator(); menu->addAction(tr("Select Color..."), this, SLOT(setColor_clicked())); + if(info->getColor().isValid()) + menu->addAction(tr("Reset Color"), this, SLOT(resetColor_clicked())); menu->addSeparator(); } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { // nop, nothing to do with this mod diff --git a/src/mainwindow.h b/src/mainwindow.h index 02e368d7..53db447a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -409,6 +409,7 @@ private slots: void renameMod_clicked(); void removeMod_clicked(); void setColor_clicked(); + void resetColor_clicked(); void backupMod_clicked(); void reinstallMod_clicked(); void endorse_clicked(); -- cgit v1.3.1 From eb26120332433c73716531e6eaae798aeae33c1b Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Wed, 31 Oct 2018 14:53:24 -0500 Subject: Add send to top/bottom options for the mod list --- src/mainwindow.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++---------- src/mainwindow.h | 2 ++ src/modlist.h | 3 +-- 3 files changed, 51 insertions(+), 12 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 74eecf06..24def4ef 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2652,7 +2652,7 @@ void MainWindow::modOpenPrev(int tab) ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow); std::vector flags = mod->getFlags(); if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) || - (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) || + (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end())) { // skip overwrite and backups and separators modOpenPrev(tab); @@ -3686,6 +3686,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu = new QMenu(this); allMods->setTitle(tr("All Mods")); menu->addMenu(allMods); + menu->addSeparator(); ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); std::vector flags = info->getFlags(); if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { @@ -3699,38 +3700,43 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addAction(tr("Restore Backup"), this, SLOT(restoreBackup_clicked())); menu->addAction(tr("Remove Backup..."), this, SLOT(removeMod_clicked())); } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()){ - menu->addSeparator(); menu->addAction(tr("Rename Separator..."), this, SLOT(renameMod_clicked())); menu->addAction(tr("Remove Separator..."), this, SLOT(removeMod_clicked())); menu->addSeparator(); + menu->addAction(tr("Send to Top"), this, SLOT(sendModToTop_clicked())); + menu->addAction(tr("Send to Bottom"), this, SLOT(sendModToBottom_clicked())); + menu->addSeparator(); menu->addAction(tr("Select Color..."), this, SLOT(setColor_clicked())); if(info->getColor().isValid()) menu->addAction(tr("Reset Color"), this, SLOT(resetColor_clicked())); menu->addSeparator(); } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - // nop, nothing to do with this mod + menu->addAction(tr("Send to Top"), this, SLOT(sendModToTop_clicked())); + menu->addAction(tr("Send to Bottom"), this, SLOT(sendModToBottom_clicked())); } else { QMenu *addRemoveCategoriesMenu = new QMenu(tr("Change Categories")); populateMenuCategories(addRemoveCategoriesMenu, 0); connect(addRemoveCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(addRemoveCategories_MenuHandler())); addMenuAsPushButton(menu, addRemoveCategoriesMenu); - //Removed as it was redundant, just making the categories look more complicated. - /* + //Removed as it was redundant, just making the categories look more complicated. + /* QMenu *replaceCategoriesMenu = new QMenu(tr("Replace Categories")); populateMenuCategories(replaceCategoriesMenu, 0); connect(replaceCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(replaceCategories_MenuHandler())); addMenuAsPushButton(menu, replaceCategoriesMenu); - */ + */ QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category")); connect(primaryCategoryMenu, SIGNAL(aboutToShow()), this, SLOT(addPrimaryCategoryCandidates())); addMenuAsPushButton(menu, primaryCategoryMenu); menu->addSeparator(); + if (info->downgradeAvailable()) { menu->addAction(tr("Change versioning scheme"), this, SLOT(changeVersioningScheme())); } + if (info->updateAvailable() || info->downgradeAvailable()) { if (info->updateIgnored()) { menu->addAction(tr("Un-ignore update"), this, SLOT(unignoreUpdate())); @@ -3746,13 +3752,17 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addSeparator(); + menu->addAction(tr("Send to Top"), this, SLOT(sendModToTop_clicked())); + menu->addAction(tr("Send to Bottom"), this, SLOT(sendModToBottom_clicked())); + + menu->addSeparator(); + menu->addAction(tr("Rename Mod..."), this, SLOT(renameMod_clicked())); menu->addAction(tr("Reinstall Mod"), this, SLOT(reinstallMod_clicked())); - menu->addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked())); + menu->addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked())); menu->addAction(tr("Create Backup"), this, SLOT(backupMod_clicked())); - - menu->addSeparator(); + menu->addSeparator(); if (info->getNexusID() > 0) { switch (info->endorsedState()) { @@ -3774,7 +3784,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) } } - menu->addSeparator(); + menu->addSeparator(); std::vector flags = info->getFlags(); if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) { @@ -5566,3 +5576,31 @@ void MainWindow::on_clearFiltersButton_clicked() { deselectFilters(); } + +void MainWindow::sendModToTop_clicked() +{ + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { + std::vector modsToMove; + for (QModelIndex idx : selection->selectedRows()) { + modsToMove.push_back(idx.data(Qt::UserRole + 1).toInt()); + } + m_OrganizerCore.modList()->changeModPriority(modsToMove, 0); + } else { + m_OrganizerCore.modList()->changeModPriority(m_ContextRow, 0); + } +} + +void MainWindow::sendModToBottom_clicked() +{ + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { + std::vector modsToMove; + for (QModelIndex idx : selection->selectedRows()) { + modsToMove.push_back(idx.data(Qt::UserRole + 1).toInt()); + } + m_OrganizerCore.modList()->changeModPriority(modsToMove, INT_MAX); + } else { + m_OrganizerCore.modList()->changeModPriority(m_ContextRow, INT_MAX); + } +} diff --git a/src/mainwindow.h b/src/mainwindow.h index 53db447a..923a8330 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -423,6 +423,8 @@ private slots: void information_clicked(); void enableSelectedMods_clicked(); void disableSelectedMods_clicked(); + void sendModToTop_clicked(); + void sendModToBottom_clicked(); // savegame context menu void deleteSavegame_clicked(); void fixMods_clicked(SaveGameInfo::MissingAssets const &missingAssets); diff --git a/src/modlist.h b/src/modlist.h index 15376ed5..caad8c61 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -106,6 +106,7 @@ public: static QString getColumnName(int column); void changeModPriority(int sourceIndex, int newPriority); + void changeModPriority(std::vector sourceIndices, int newPriority); void setOverwriteMarkers(const std::set &overwrite, const std::set &overwritten); void setPluginContainer(PluginContainer *pluginContainer); @@ -266,8 +267,6 @@ private: bool testValid(const QString &modDir); - void changeModPriority(std::vector sourceIndices, int newPriority); - QVariant getOverwriteData(int column, int role) const; QString getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const; -- cgit v1.3.1 From 5de173ba1ae9531aaca0c0f022a2b0dae1c09250 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Wed, 31 Oct 2018 17:05:05 -0500 Subject: Add send to top/bottom options for the plugin list --- src/mainwindow.cpp | 15 ++++++++++ src/mainwindow.h | 2 ++ src/pluginlist.cpp | 88 +++++++++++++++++++++++++++++++++++++----------------- src/pluginlist.h | 12 ++++++++ 4 files changed, 90 insertions(+), 27 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 24def4ef..d279da91 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4341,6 +4341,16 @@ void MainWindow::disableSelectedPlugins_clicked() m_OrganizerCore.pluginList()->disableSelected(ui->espList->selectionModel()); } +void MainWindow::sendSelectedPluginsToTop_clicked() +{ + m_OrganizerCore.pluginList()->sendToTop(ui->espList->selectionModel()); +} + +void MainWindow::sendSelectedPluginsToBottom_clicked() +{ + m_OrganizerCore.pluginList()->sendToBottom(ui->espList->selectionModel()); +} + void MainWindow::enableSelectedMods_clicked() { @@ -4966,6 +4976,11 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Enable all"), m_OrganizerCore.pluginList(), SLOT(enableAll())); menu.addAction(tr("Disable all"), m_OrganizerCore.pluginList(), SLOT(disableAll())); + menu.addSeparator(); + + menu.addAction(tr("Send to top"), this, SLOT(sendSelectedPluginsToTop_clicked())); + menu.addAction(tr("Send to bottom"), this, SLOT(sendSelectedPluginsToBottom_clicked())); + QItemSelection currentSelection = ui->espList->selectionModel()->selection(); bool hasLocked = false; bool hasUnlocked = false; diff --git a/src/mainwindow.h b/src/mainwindow.h index 923a8330..9dee2953 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -439,6 +439,8 @@ private slots: // pluginlist context menu void enableSelectedPlugins_clicked(); void disableSelectedPlugins_clicked(); + void sendSelectedPluginsToTop_clicked(); + void sendSelectedPluginsToBottom_clicked(); void linkToolbar(); void linkDesktop(); diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 18e40a6e..4097ba98 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -289,21 +289,26 @@ void PluginList::enableESP(const QString &name, bool enable) } } +int PluginList::findPluginByPriority(int priority) +{ + for (int i = 0; i < m_ESPs.size(); i++ ) { + if (m_ESPs[i].m_Priority == priority) { + return i; + } + } + qCritical(QString("No plugin with priority %1").arg(priority).toLocal8Bit()); + return -1; +} + void PluginList::enableSelected(const QItemSelectionModel *selectionModel) { - if (selectionModel->hasSelection()){ + if (selectionModel->hasSelection()) { bool dirty = false; for (auto row : selectionModel->selectedRows(COL_PRIORITY)) { - int rowPriority = row.data().toInt(); - for (int i = 0; i < m_ESPs.size(); i++) { - if (m_ESPs[i].m_Priority == rowPriority) { - if (!m_ESPs[i].m_Enabled) { - m_ESPs[i].m_Enabled = true; - dirty = true; - } - - break; - } + int rowIndex = findPluginByPriority(row.data().toInt()); + if (!m_ESPs[rowIndex].m_Enabled) { + m_ESPs[rowIndex].m_Enabled = true; + dirty = true; } } if (dirty) emit writePluginsList(); @@ -312,18 +317,13 @@ void PluginList::enableSelected(const QItemSelectionModel *selectionModel) void PluginList::disableSelected(const QItemSelectionModel *selectionModel) { - if (selectionModel->hasSelection()){ + if (selectionModel->hasSelection()) { bool dirty = false; for (auto row : selectionModel->selectedRows(COL_PRIORITY)) { - int rowPriority = row.data().toInt(); - for (int i = 0; i < m_ESPs.size(); i++) { - if (m_ESPs[i].m_Priority == rowPriority) { - if (!m_ESPs[i].m_ForceEnabled && m_ESPs[i].m_Enabled) { - m_ESPs[i].m_Enabled = false; - dirty = true; - } - break; - } + int rowIndex = findPluginByPriority(row.data().toInt()); + if (!m_ESPs[rowIndex].m_ForceEnabled && m_ESPs[rowIndex].m_Enabled) { + m_ESPs[rowIndex].m_Enabled = false; + dirty = true; } } if (dirty) emit writePluginsList(); @@ -356,6 +356,40 @@ void PluginList::disableAll() } } +void PluginList::sendToTop(const QItemSelectionModel *selectionModel) +{ + if (selectionModel->hasSelection()) { + std::vector pluginsToMove; + for (auto row: selectionModel->selectedRows(COL_PRIORITY)) { + int rowIndex = findPluginByPriority(row.data().toInt()); + if (!m_ESPs[rowIndex].m_ForceEnabled) { + pluginsToMove.push_back(rowIndex); + } + } + if (pluginsToMove.size()) { + changePluginPriority(pluginsToMove, 0); + emit SIGNAL(displayPlugin(0)); + } + } +} + +void PluginList::sendToBottom(const QItemSelectionModel *selectionModel) +{ + if (selectionModel->hasSelection()) { + std::vector pluginsToMove; + for (auto row: selectionModel->selectedRows(COL_PRIORITY)) { + int rowIndex = findPluginByPriority(row.data().toInt()); + if (!m_ESPs[rowIndex].m_ForceEnabled) { + pluginsToMove.push_back(rowIndex); + } + } + if (pluginsToMove.size()) { + changePluginPriority(pluginsToMove, INT_MAX); + emit SIGNAL(displayPlugin(INT_MAX)); + } + } +} + bool PluginList::isEnabled(const QString &name) { @@ -1092,6 +1126,12 @@ void PluginList::setPluginPriority(int row, int &newPriority) { int newPriorityTemp = newPriority; + // enforce valid range + if (newPriorityTemp < 0) + newPriorityTemp = 0; + else if (newPriorityTemp >= static_cast(m_ESPsByPriority.size())) + newPriorityTemp = static_cast(m_ESPsByPriority.size()) - 1; + if (!m_ESPs[row].m_IsMaster && !m_ESPs[row].m_IsLight) { // don't allow esps to be moved above esms while ((newPriorityTemp < static_cast(m_ESPsByPriority.size() - 1)) && @@ -1113,12 +1153,6 @@ void PluginList::setPluginPriority(int row, int &newPriority) } } - // enforce valid range - if (newPriorityTemp < 0) - newPriorityTemp = 0; - else if (newPriorityTemp >= static_cast(m_ESPsByPriority.size())) - newPriorityTemp = static_cast(m_ESPsByPriority.size()) - 1; - try { int oldPriority = m_ESPs.at(row).m_Priority; if (newPriorityTemp > oldPriority) { diff --git a/src/pluginlist.h b/src/pluginlist.h index 03d6426a..009c9153 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -264,6 +264,16 @@ public slots: **/ void disableAll(); + /** + * @brief moves selected plugins to the top (lowest priority) + **/ + void sendToTop(const QItemSelectionModel *selectionModel); + + /** + * @brief moves selected plugins to the bottom (highest priority) + **/ + void sendToBottom(const QItemSelectionModel *selectionModel); + /** * @brief The currently managed game has changed * @param gamePlugin @@ -340,6 +350,8 @@ private: void fixPriorities(); + int findPluginByPriority(int priority); + private: std::vector m_ESPs; -- cgit v1.3.1 From 6323a348da0e34a86d2a10c45fe9c77daafb17b9 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Thu, 1 Nov 2018 16:29:38 +0100 Subject: Fixed missing category selection for separators. --- src/mainwindow.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d279da91..5e4bb86f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3700,6 +3700,15 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addAction(tr("Restore Backup"), this, SLOT(restoreBackup_clicked())); menu->addAction(tr("Remove Backup..."), this, SLOT(removeMod_clicked())); } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()){ + menu->addSeparator(); + QMenu *addRemoveCategoriesMenu = new QMenu(tr("Change Categories")); + populateMenuCategories(addRemoveCategoriesMenu, 0); + connect(addRemoveCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(addRemoveCategories_MenuHandler())); + addMenuAsPushButton(menu, addRemoveCategoriesMenu); + QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category")); + connect(primaryCategoryMenu, SIGNAL(aboutToShow()), this, SLOT(addPrimaryCategoryCandidates())); + addMenuAsPushButton(menu, primaryCategoryMenu); + menu->addSeparator(); menu->addAction(tr("Rename Separator..."), this, SLOT(renameMod_clicked())); menu->addAction(tr("Remove Separator..."), this, SLOT(removeMod_clicked())); menu->addSeparator(); @@ -3719,14 +3728,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) connect(addRemoveCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(addRemoveCategories_MenuHandler())); addMenuAsPushButton(menu, addRemoveCategoriesMenu); - //Removed as it was redundant, just making the categories look more complicated. - /* - QMenu *replaceCategoriesMenu = new QMenu(tr("Replace Categories")); - populateMenuCategories(replaceCategoriesMenu, 0); - connect(replaceCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(replaceCategories_MenuHandler())); - addMenuAsPushButton(menu, replaceCategoriesMenu); - */ - QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category")); connect(primaryCategoryMenu, SIGNAL(aboutToShow()), this, SLOT(addPrimaryCategoryCandidates())); addMenuAsPushButton(menu, primaryCategoryMenu); -- cgit v1.3.1 From 3dbfba91e1618e6cdf3ed5ce7ff7955376df03eb Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Thu, 1 Nov 2018 14:51:14 -0500 Subject: Add separators according to context location of context menu --- src/mainwindow.cpp | 14 ++++++++++++++ src/mainwindow.h | 1 + src/organizercore.cpp | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5e4bb86f..5b257977 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -204,6 +204,7 @@ MainWindow::MainWindow(QSettings &initSettings , m_CategoryFactory(CategoryFactory::instance()) , m_ContextItem(nullptr) , m_ContextAction(nullptr) + , m_ContextRow(-1) , m_CurrentSaveView(nullptr) , m_OrganizerCore(organizerCore) , m_PluginContainer(pluginContainer) @@ -328,6 +329,7 @@ MainWindow::MainWindow(QSettings &initSettings ui->linkButton->setMenu(linkMenu); ui->listOptionsBtn->setMenu(modListContextMenu()); + connect(ui->listOptionsBtn, SIGNAL(pressed()), this, SLOT(on_listOptionsBtn_pressed())); ui->openFolderMenu->setMenu(openFolderMenu()); @@ -2866,8 +2868,15 @@ void MainWindow::createSeparator_clicked() { return; } + int newPriority; + if (m_ContextRow >= 0) { + newPriority = m_OrganizerCore.currentProfile()->getModPriority(m_ContextRow); + } if (m_OrganizerCore.createMod(name) == nullptr) { return; } m_OrganizerCore.refreshModList(); + if (m_ContextRow >= 0) { + m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); + } } void MainWindow::setColor_clicked() @@ -3023,6 +3032,11 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index) } } +void MainWindow::on_listOptionsBtn_pressed() +{ + m_ContextRow = -1; +} + void MainWindow::on_espList_doubleClicked(const QModelIndex &index) { if (!index.isValid()) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 9dee2953..c595575e 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -590,6 +590,7 @@ private slots: // ui slots void on_executablesListBox_currentIndexChanged(int index); void on_modList_customContextMenuRequested(const QPoint &pos); void on_modList_doubleClicked(const QModelIndex &index); + void on_listOptionsBtn_pressed(); void on_espList_doubleClicked(const QModelIndex &index); void on_profileBox_currentIndexChanged(int index); void on_savegameList_customContextMenuRequested(const QPoint &pos); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 1fafd4c1..fe720a0c 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1184,7 +1184,7 @@ QStringList OrganizerCore::modsSortedByProfilePriority() const { QStringList res; for (int i = currentProfile()->getPriorityMinimum(); - i < currentProfile()->getPriorityMinimum() + currentProfile()->numRegularMods(); + i < currentProfile()->getPriorityMinimum() + (int)currentProfile()->numRegularMods(); ++i) { int modIndex = currentProfile()->modIndexByPriority(i); res.push_back(ModInfo::getByIndex(modIndex)->name()); -- cgit v1.3.1 From 2c57899cf611056a8c5bae1f7f27c9a157df6f41 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Thu, 1 Nov 2018 17:58:49 -0500 Subject: Fix matching highlights for mod and plugin selections The selectionChanged event used before requires subtractive/additive logic to work correctly. A separate event is fired for each row and each column for each row that is selected or deselected. The previous logic assumed all of the selection was passed in at once. The logic has been changed to recalculate all highlights each time the event is fired. This is poorly optimized in regards to performance but makes the function work. --- src/mainwindow.cpp | 4 ++-- src/modlist.cpp | 5 +++-- src/modlist.h | 2 +- src/pluginlist.cpp | 11 +++++++---- src/pluginlist.h | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5b257977..752c5c76 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2341,13 +2341,13 @@ void MainWindow::modlistSelectionChanged(const QModelIndex ¤t, const QMode void MainWindow::modlistSelectionsChanged(const QItemSelection &selected) { - m_OrganizerCore.pluginList()->highlightPlugins(selected, *m_OrganizerCore.directoryStructure(), *m_OrganizerCore.currentProfile()); + m_OrganizerCore.pluginList()->highlightPlugins(ui->modList->selectionModel(), *m_OrganizerCore.directoryStructure(), *m_OrganizerCore.currentProfile()); ui->espList->verticalScrollBar()->repaint(); } void MainWindow::esplistSelectionsChanged(const QItemSelection &selected) { - m_OrganizerCore.modList()->highlightMods(selected, *m_OrganizerCore.directoryStructure()); + m_OrganizerCore.modList()->highlightMods(ui->espList->selectionModel(), *m_OrganizerCore.directoryStructure()); ui->modList->verticalScrollBar()->repaint(); } diff --git a/src/modlist.cpp b/src/modlist.cpp index 3f8fa0c0..539ba6fc 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -24,6 +24,7 @@ along with Mod Organizer. If not, see . #include "qtgroupingproxy.h" #include "viewmarkingscrollbar.h" #include "modlistsortproxy.h" +#include "pluginlist.h" #include "settings.h" #include "modinforegular.h" #include @@ -768,12 +769,12 @@ int ModList::timeElapsedSinceLastChecked() const return m_LastCheck.elapsed(); } -void ModList::highlightMods(const QItemSelection &selected, const MOShared::DirectoryEntry &directoryEntry) +void ModList::highlightMods(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry) { for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) { ModInfo::getByIndex(i)->setPluginSelected(false); } - for (QModelIndex idx : selected.indexes()) { + for (QModelIndex idx : selection->selectedRows(PluginList::COL_NAME)) { QString modName = idx.data().toString(); const MOShared::FileEntry::Ptr fileEntry = directoryEntry.findFile(modName.toStdWString()); diff --git a/src/modlist.h b/src/modlist.h index caad8c61..8de08da3 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -118,7 +118,7 @@ public: int timeElapsedSinceLastChecked() const; - void highlightMods(const QItemSelection &selected, const MOShared::DirectoryEntry &directoryEntry); + void highlightMods(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry); public: diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index d56a4bde..21e716d3 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see . #include "settings.h" #include "scopeguard.h" #include "modinfo.h" +#include "modlist.h" #include "viewmarkingscrollbar.h" #include #include @@ -119,14 +120,16 @@ QString PluginList::getColumnToolTip(int column) } } -void PluginList::highlightPlugins(const QItemSelection &selected, const MOShared::DirectoryEntry &directoryEntry, const Profile &profile) +void PluginList::highlightPlugins(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry, const Profile &profile) { for (auto &esp : m_ESPs) { esp.m_ModSelected = false; } - for (QModelIndex idx : selected.indexes()) { - ModInfo::Ptr selectedMod = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - if (!selectedMod.isNull() && profile.modEnabled(idx.data(Qt::UserRole + 1).toInt())) { + for (QModelIndex idx : selection->selectedRows(ModList::COL_PRIORITY)) { + int modPriority = idx.data().toInt(); + int modIndex = profile.modIndexByPriority(modPriority); + ModInfo::Ptr selectedMod = ModInfo::getByIndex(modIndex); + if (!selectedMod.isNull() && profile.modEnabled(modIndex)) { QDir dir(selectedMod->absolutePath()); QStringList plugins = dir.entryList(QStringList() << "*.esp" << "*.esm" << "*.esl"); MOShared::FilesOrigin origin = directoryEntry.getOriginByName(selectedMod->internalName().toStdWString()); diff --git a/src/pluginlist.h b/src/pluginlist.h index 009c9153..f7817c37 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -206,7 +206,7 @@ public: static QString getColumnName(int column); static QString getColumnToolTip(int column); - void highlightPlugins(const QItemSelection &selected, const MOShared::DirectoryEntry &directoryEntry, const Profile &profile); + void highlightPlugins(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry, const Profile &profile); void refreshLoadOrder(); -- cgit v1.3.1 From 2e09e633bd40f9844d0346ae86b19b9709d8a735 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sun, 4 Nov 2018 12:32:32 -0600 Subject: Fix creating separators when right-clicking overwrite --- src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 752c5c76..7c7d9c95 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2874,7 +2874,7 @@ void MainWindow::createSeparator_clicked() } if (m_OrganizerCore.createMod(name) == nullptr) { return; } m_OrganizerCore.refreshModList(); - if (m_ContextRow >= 0) { + if (m_ContextRow >= 0 && newPriority >= 0) { m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); } } -- cgit v1.3.1 From a48a20f13fd20e599c2055752bae5065bce43700 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sun, 4 Nov 2018 13:10:41 -0600 Subject: Add context menu option to send to a specific priority --- src/mainwindow.cpp | 84 ++++++++++++++++++++++++++++++++++++++---------------- src/mainwindow.h | 11 +++++-- src/pluginlist.cpp | 23 ++------------- src/pluginlist.h | 9 ++---- 4 files changed, 72 insertions(+), 55 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7c7d9c95..3d7cc13b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3683,6 +3683,26 @@ QMenu *MainWindow::modListContextMenu() return menu; } +QMenu *MainWindow::modSendToContextMenu() +{ + QMenu *menu = new QMenu(this); + menu->setTitle(tr("Send to")); + menu->addAction(tr("Top"), this, SLOT(sendSelectedModsToTop_clicked())); + menu->addAction(tr("Bottom"), this, SLOT(sendSelectedModsToBottom_clicked())); + menu->addAction(tr("Priority..."), this, SLOT(sendSelectedModsToPriority_clicked())); + return menu; +} + +QMenu *MainWindow::pluginSendToContextMenu() +{ + QMenu *menu = new QMenu(this); + menu->setTitle(tr("Send to")); + menu->addAction(tr("Top"), this, SLOT(sendSelectedPluginsToTop_clicked())); + menu->addAction(tr("Bottom"), this, SLOT(sendSelectedPluginsToBottom_clicked())); + menu->addAction(tr("Priority..."), this, SLOT(sendSelectedPluginsToPriority_clicked())); + return menu; +} + void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) { try { @@ -3726,16 +3746,14 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addAction(tr("Rename Separator..."), this, SLOT(renameMod_clicked())); menu->addAction(tr("Remove Separator..."), this, SLOT(removeMod_clicked())); menu->addSeparator(); - menu->addAction(tr("Send to Top"), this, SLOT(sendModToTop_clicked())); - menu->addAction(tr("Send to Bottom"), this, SLOT(sendModToBottom_clicked())); + menu->addMenu(modSendToContextMenu()); menu->addSeparator(); menu->addAction(tr("Select Color..."), this, SLOT(setColor_clicked())); if(info->getColor().isValid()) menu->addAction(tr("Reset Color"), this, SLOT(resetColor_clicked())); menu->addSeparator(); } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - menu->addAction(tr("Send to Top"), this, SLOT(sendModToTop_clicked())); - menu->addAction(tr("Send to Bottom"), this, SLOT(sendModToBottom_clicked())); + menu->addMenu(modSendToContextMenu()); } else { QMenu *addRemoveCategoriesMenu = new QMenu(tr("Change Categories")); populateMenuCategories(addRemoveCategoriesMenu, 0); @@ -3767,8 +3785,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addSeparator(); - menu->addAction(tr("Send to Top"), this, SLOT(sendModToTop_clicked())); - menu->addAction(tr("Send to Bottom"), this, SLOT(sendModToBottom_clicked())); + menu->addMenu(modSendToContextMenu()); menu->addSeparator(); @@ -4358,12 +4375,23 @@ void MainWindow::disableSelectedPlugins_clicked() void MainWindow::sendSelectedPluginsToTop_clicked() { - m_OrganizerCore.pluginList()->sendToTop(ui->espList->selectionModel()); + m_OrganizerCore.pluginList()->sendToPriority(ui->espList->selectionModel(), 0); } void MainWindow::sendSelectedPluginsToBottom_clicked() { - m_OrganizerCore.pluginList()->sendToBottom(ui->espList->selectionModel()); + m_OrganizerCore.pluginList()->sendToPriority(ui->espList->selectionModel(), INT_MAX); +} + +void MainWindow::sendSelectedPluginsToPriority_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; + + m_OrganizerCore.pluginList()->sendToPriority(ui->espList->selectionModel(), newPriority); } @@ -4993,8 +5021,7 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) menu.addSeparator(); - menu.addAction(tr("Send to top"), this, SLOT(sendSelectedPluginsToTop_clicked())); - menu.addAction(tr("Send to bottom"), this, SLOT(sendSelectedPluginsToBottom_clicked())); + menu.addMenu(pluginSendToContextMenu()); QItemSelection currentSelection = ui->espList->selectionModel()->selection(); bool hasLocked = false; @@ -5607,30 +5634,37 @@ void MainWindow::on_clearFiltersButton_clicked() deselectFilters(); } -void MainWindow::sendModToTop_clicked() +void MainWindow::sendSelectedModsToPriority(int newPriority) { QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { std::vector modsToMove; for (QModelIndex idx : selection->selectedRows()) { - modsToMove.push_back(idx.data(Qt::UserRole + 1).toInt()); + modsToMove.push_back(idx.data().toInt()); } - m_OrganizerCore.modList()->changeModPriority(modsToMove, 0); + m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority); } else { - m_OrganizerCore.modList()->changeModPriority(m_ContextRow, 0); + m_OrganizerCore.modList()->changeModPriority(m_ContextRow, newPriority); } } -void MainWindow::sendModToBottom_clicked() +void MainWindow::sendSelectedModsToTop_clicked() { - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - std::vector modsToMove; - for (QModelIndex idx : selection->selectedRows()) { - modsToMove.push_back(idx.data(Qt::UserRole + 1).toInt()); - } - m_OrganizerCore.modList()->changeModPriority(modsToMove, INT_MAX); - } else { - m_OrganizerCore.modList()->changeModPriority(m_ContextRow, INT_MAX); - } + 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); } diff --git a/src/mainwindow.h b/src/mainwindow.h index c595575e..85b3fc2d 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -278,6 +278,8 @@ private: QString queryRestore(const QString &filePath); QMenu *modListContextMenu(); + QMenu *modSendToContextMenu(); + QMenu *pluginSendToContextMenu(); QMenu *openFolderMenu(); @@ -294,6 +296,9 @@ private: bool registerWidgetState(const QString &name, QHeaderView *view, const char *oldSettingName = nullptr); + void sendSelectedModsToPriority(int newPriority); + void sendSelectedPluginsToPriority(int newPriority); + private: static const char *PATTERN_BACKUP_GLOB; @@ -423,8 +428,9 @@ private slots: void information_clicked(); void enableSelectedMods_clicked(); void disableSelectedMods_clicked(); - void sendModToTop_clicked(); - void sendModToBottom_clicked(); + void sendSelectedModsToTop_clicked(); + void sendSelectedModsToBottom_clicked(); + void sendSelectedModsToPriority_clicked(); // savegame context menu void deleteSavegame_clicked(); void fixMods_clicked(SaveGameInfo::MissingAssets const &missingAssets); @@ -441,6 +447,7 @@ private slots: void disableSelectedPlugins_clicked(); void sendSelectedPluginsToTop_clicked(); void sendSelectedPluginsToBottom_clicked(); + void sendSelectedPluginsToPriority_clicked(); void linkToolbar(); void linkDesktop(); diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 21e716d3..480cb014 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -359,7 +359,7 @@ void PluginList::disableAll() } } -void PluginList::sendToTop(const QItemSelectionModel *selectionModel) +void PluginList::sendToPriority(const QItemSelectionModel *selectionModel, int newPriority) { if (selectionModel->hasSelection()) { std::vector pluginsToMove; @@ -370,30 +370,11 @@ void PluginList::sendToTop(const QItemSelectionModel *selectionModel) } } if (pluginsToMove.size()) { - changePluginPriority(pluginsToMove, 0); - emit SIGNAL(displayPlugin(0)); + changePluginPriority(pluginsToMove, newPriority); } } } -void PluginList::sendToBottom(const QItemSelectionModel *selectionModel) -{ - if (selectionModel->hasSelection()) { - std::vector pluginsToMove; - for (auto row: selectionModel->selectedRows(COL_PRIORITY)) { - int rowIndex = findPluginByPriority(row.data().toInt()); - if (!m_ESPs[rowIndex].m_ForceEnabled) { - pluginsToMove.push_back(rowIndex); - } - } - if (pluginsToMove.size()) { - changePluginPriority(pluginsToMove, INT_MAX); - emit SIGNAL(displayPlugin(INT_MAX)); - } - } -} - - bool PluginList::isEnabled(const QString &name) { std::map::iterator iter = m_ESPsByName.find(name.toLower()); diff --git a/src/pluginlist.h b/src/pluginlist.h index f7817c37..228ccdec 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -265,14 +265,9 @@ public slots: void disableAll(); /** - * @brief moves selected plugins to the top (lowest priority) + * @brief moves selected plugins to specified priority **/ - void sendToTop(const QItemSelectionModel *selectionModel); - - /** - * @brief moves selected plugins to the bottom (highest priority) - **/ - void sendToBottom(const QItemSelectionModel *selectionModel); + void sendToPriority(const QItemSelectionModel *selectionModel, int priority); /** * @brief The currently managed game has changed -- cgit v1.3.1 From 21d61e617bb03bcb1f73b16d42e52742a3b0ae67 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sun, 4 Nov 2018 13:39:22 -0600 Subject: Hide "send to" context menu options when not sorting by priority --- src/mainwindow.cpp | 51 ++++++++++++++++++++++++++++----------------------- src/mainwindow.h | 4 ++-- 2 files changed, 30 insertions(+), 25 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3d7cc13b..92127e0a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3683,24 +3683,34 @@ QMenu *MainWindow::modListContextMenu() return menu; } -QMenu *MainWindow::modSendToContextMenu() +void MainWindow::addModSendToContextMenu(QMenu *menu) { - QMenu *menu = new QMenu(this); - menu->setTitle(tr("Send to")); - menu->addAction(tr("Top"), this, SLOT(sendSelectedModsToTop_clicked())); - menu->addAction(tr("Bottom"), this, SLOT(sendSelectedModsToBottom_clicked())); - menu->addAction(tr("Priority..."), this, SLOT(sendSelectedModsToPriority_clicked())); - return menu; + if (m_ModListSortProxy->sortColumn() != ModList::COL_PRIORITY) + return; + + QMenu *sub_menu = new QMenu(this); + sub_menu->setTitle(tr("Send to")); + sub_menu->addAction(tr("Top"), this, SLOT(sendSelectedModsToTop_clicked())); + sub_menu->addAction(tr("Bottom"), this, SLOT(sendSelectedModsToBottom_clicked())); + sub_menu->addAction(tr("Priority..."), this, SLOT(sendSelectedModsToPriority_clicked())); + + menu->addMenu(sub_menu); + menu->addSeparator(); } -QMenu *MainWindow::pluginSendToContextMenu() +void MainWindow::addPluginSendToContextMenu(QMenu *menu) { - QMenu *menu = new QMenu(this); - menu->setTitle(tr("Send to")); - menu->addAction(tr("Top"), this, SLOT(sendSelectedPluginsToTop_clicked())); - menu->addAction(tr("Bottom"), this, SLOT(sendSelectedPluginsToBottom_clicked())); - menu->addAction(tr("Priority..."), this, SLOT(sendSelectedPluginsToPriority_clicked())); - return menu; + if (m_PluginListSortProxy->sortColumn() != PluginList::COL_PRIORITY) + return; + + QMenu *sub_menu = new QMenu(this); + sub_menu->setTitle(tr("Send to")); + sub_menu->addAction(tr("Top"), this, SLOT(sendSelectedPluginsToTop_clicked())); + sub_menu->addAction(tr("Bottom"), this, SLOT(sendSelectedPluginsToBottom_clicked())); + sub_menu->addAction(tr("Priority..."), this, SLOT(sendSelectedPluginsToPriority_clicked())); + + menu->addMenu(sub_menu); + menu->addSeparator(); } void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) @@ -3746,14 +3756,13 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addAction(tr("Rename Separator..."), this, SLOT(renameMod_clicked())); menu->addAction(tr("Remove Separator..."), this, SLOT(removeMod_clicked())); menu->addSeparator(); - menu->addMenu(modSendToContextMenu()); - menu->addSeparator(); + addModSendToContextMenu(menu); menu->addAction(tr("Select Color..."), this, SLOT(setColor_clicked())); if(info->getColor().isValid()) menu->addAction(tr("Reset Color"), this, SLOT(resetColor_clicked())); menu->addSeparator(); } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) { - menu->addMenu(modSendToContextMenu()); + addModSendToContextMenu(menu); } else { QMenu *addRemoveCategoriesMenu = new QMenu(tr("Change Categories")); populateMenuCategories(addRemoveCategoriesMenu, 0); @@ -3785,9 +3794,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addSeparator(); - menu->addMenu(modSendToContextMenu()); - - menu->addSeparator(); + addModSendToContextMenu(menu); menu->addAction(tr("Rename Mod..."), this, SLOT(renameMod_clicked())); menu->addAction(tr("Reinstall Mod"), this, SLOT(reinstallMod_clicked())); @@ -5021,7 +5028,7 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) menu.addSeparator(); - menu.addMenu(pluginSendToContextMenu()); + addPluginSendToContextMenu(&menu); QItemSelection currentSelection = ui->espList->selectionModel()->selection(); bool hasLocked = false; @@ -5037,8 +5044,6 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) } } - menu.addSeparator(); - if (hasLocked) { menu.addAction(tr("Unlock load order"), this, SLOT(unlockESPIndex())); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 85b3fc2d..06219a5a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -278,8 +278,8 @@ private: QString queryRestore(const QString &filePath); QMenu *modListContextMenu(); - QMenu *modSendToContextMenu(); - QMenu *pluginSendToContextMenu(); + void addModSendToContextMenu(QMenu *menu); + void addPluginSendToContextMenu(QMenu *menu); QMenu *openFolderMenu(); -- cgit v1.3.1 From d48392c89fa21b29e2bdc008c033ff3064ee49fc Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sun, 4 Nov 2018 13:45:42 -0600 Subject: Don't change priority of new separators when not sorting by priority --- src/mainwindow.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 92127e0a..4294904e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2868,13 +2868,18 @@ void MainWindow::createSeparator_clicked() { return; } - int newPriority; - if (m_ContextRow >= 0) { + + int newPriority = -1; + if (m_ContextRow >= 0 && m_ModListSortProxy->sortColumn() == ModList::COL_PRIORITY) + { newPriority = m_OrganizerCore.currentProfile()->getModPriority(m_ContextRow); } + if (m_OrganizerCore.createMod(name) == nullptr) { return; } m_OrganizerCore.refreshModList(); - if (m_ContextRow >= 0 && newPriority >= 0) { + + if (newPriority >= 0) + { m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); } } -- cgit v1.3.1 From 5588b8b756ea224b1469049a338628787aff80c0 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sun, 4 Nov 2018 13:47:39 -0600 Subject: Change "mods" to "plugins" in "send to priority..." option for plugins --- src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4294904e..f0aca1af 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4399,7 +4399,7 @@ void MainWindow::sendSelectedPluginsToPriority_clicked() { bool ok; int newPriority = QInputDialog::getInt(this, - tr("Set Priority"), tr("Set the priority of the selected mods"), + tr("Set Priority"), tr("Set the priority of the selected plugins"), 0, 0, INT_MAX, 1, &ok); if (!ok) return; -- cgit v1.3.1 From 9abb3ca1627ba535ecbacf3daa0d1bc468ed6db5 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Mon, 5 Nov 2018 21:43:50 -0600 Subject: Prevent user from clicking the run button more than once --- src/mainwindow.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f0aca1af..d0591d6e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1811,14 +1811,21 @@ void MainWindow::installMod(QString fileName) } void MainWindow::on_startButton_clicked() { - const Executable &selectedExecutable(getSelectedExecutable()); - QString customOverwrite = m_OrganizerCore.currentProfile()->setting("custom_overwrites", selectedExecutable.m_Title).toString(); - m_OrganizerCore.spawnBinary( - selectedExecutable.m_BinaryInfo, selectedExecutable.m_Arguments, - selectedExecutable.m_WorkingDirectory.length() != 0 - ? selectedExecutable.m_WorkingDirectory - : selectedExecutable.m_BinaryInfo.absolutePath(), - selectedExecutable.m_SteamAppID, customOverwrite); + ui->startButton->setEnabled(false); + try { + const Executable &selectedExecutable(getSelectedExecutable()); + QString customOverwrite = m_OrganizerCore.currentProfile()->setting("custom_overwrites", selectedExecutable.m_Title).toString(); + m_OrganizerCore.spawnBinary( + selectedExecutable.m_BinaryInfo, selectedExecutable.m_Arguments, + selectedExecutable.m_WorkingDirectory.length() != 0 + ? selectedExecutable.m_WorkingDirectory + : selectedExecutable.m_BinaryInfo.absolutePath(), + selectedExecutable.m_SteamAppID, customOverwrite); + } catch (...) { + ui->startButton->setEnabled(true); + throw; + } + ui->startButton->setEnabled(true); } static HRESULT CreateShortcut(LPCWSTR targetFileName, LPCWSTR arguments, -- cgit v1.3.1 From 2212ad6f1c0bbaff7025cdb7227023661e399596 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Thu, 29 Nov 2018 08:17:28 -0600 Subject: Add executable blacklisting --- src/mainwindow.cpp | 14 +-- src/organizer_en.ts | 263 +++++++++++++++++++++++++++++-------------------- src/organizercore.cpp | 20 +++- src/organizercore.h | 2 +- src/settings.cpp | 18 ++++ src/settings.h | 7 +- src/settingsdialog.cpp | 27 +++++ src/settingsdialog.h | 13 ++- src/settingsdialog.ui | 22 ++++- src/usvfsconnector.cpp | 13 ++- src/usvfsconnector.h | 2 +- 11 files changed, 272 insertions(+), 129 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d0591d6e..9a0e02aa 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2877,7 +2877,7 @@ void MainWindow::createSeparator_clicked() } int newPriority = -1; - if (m_ContextRow >= 0 && m_ModListSortProxy->sortColumn() == ModList::COL_PRIORITY) + if (m_ContextRow >= 0 && m_ModListSortProxy->sortColumn() == ModList::COL_PRIORITY) { newPriority = m_OrganizerCore.currentProfile()->getModPriority(m_ContextRow); } @@ -2885,7 +2885,7 @@ void MainWindow::createSeparator_clicked() if (m_OrganizerCore.createMod(name) == nullptr) { return; } m_OrganizerCore.refreshModList(); - if (newPriority >= 0) + if (newPriority >= 0) { m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); } @@ -4129,7 +4129,7 @@ void MainWindow::on_actionSettings_triggered() updateDownloadListDelegate(); - m_OrganizerCore.updateVFSParams(settings.logLevel(), settings.crashDumpsType()); + m_OrganizerCore.updateVFSParams(settings.logLevel(), settings.crashDumpsType(), settings.executablesBlacklist()); m_OrganizerCore.cycleDiagnostics(); } @@ -4405,8 +4405,8 @@ void MainWindow::sendSelectedPluginsToBottom_clicked() void MainWindow::sendSelectedPluginsToPriority_clicked() { bool ok; - int newPriority = QInputDialog::getInt(this, - tr("Set Priority"), tr("Set the priority of the selected plugins"), + int newPriority = QInputDialog::getInt(this, + tr("Set Priority"), tr("Set the priority of the selected plugins"), 0, 0, INT_MAX, 1, &ok); if (!ok) return; @@ -5678,8 +5678,8 @@ void MainWindow::sendSelectedModsToBottom_clicked() void MainWindow::sendSelectedModsToPriority_clicked() { bool ok; - int newPriority = QInputDialog::getInt(this, - tr("Set Priority"), tr("Set the priority of the selected mods"), + 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; diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 9b1294ac..518e49bc 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -4041,113 +4041,113 @@ p, li { white-space: pre-wrap; } - + drag&drop failed: %1 - + Confirm - + Are you sure you want to remove "%1"? - + Flags - + Content - + Mod Name - + Version - + Priority - + Category - + Source Game - + Nexus ID - + Installation - - + + unknown - + Name of your mods - + Version of the mod (if available) - + Installation priority of your mod. The higher, the more "important" it is and thus overwrites files from mods with lower priority. - + Category of the mod. - + The source game which was the origin of this mod. - + Id of the mod as used on Nexus. - + Emblemes to highlight things that might require attention. - + Depicts the content of the mod:<br><table cellspacing=7><tr><td><img src=":/MO/gui/content/plugin" width=32/></td><td>Game plugins (esp/esm/esl)</td></tr><tr><td><img src=":/MO/gui/content/interface" width=32/></td><td>Interface</td></tr><tr><td><img src=":/MO/gui/content/mesh" width=32/></td><td>Meshes</td></tr><tr><td><img src=":/MO/gui/content/bsa" width=32/></td><td>BSA</td></tr><tr><td><img src=":/MO/gui/content/texture" width=32/></td><td>Textures</td></tr><tr><td><img src=":/MO/gui/content/sound" width=32/></td><td>Sounds</td></tr><tr><td><img src=":/MO/gui/content/music" width=32/></td><td>Music</td></tr><tr><td><img src=":/MO/gui/content/string" width=32/></td><td>Strings</td></tr><tr><td><img src=":/MO/gui/content/script" width=32/></td><td>Scripts (Papyrus)</td></tr><tr><td><img src=":/MO/gui/content/skse" width=32/></td><td>Script Extender plugins</td></tr><tr><td><img src=":/MO/gui/content/skyproc" width=32/></td><td>SkyProc Patcher</td></tr><tr><td><img src=":/MO/gui/content/menu" width=32/></td><td>Mod Configuration Menu</td></tr><tr><td><img src=":/MO/gui/content/inifile" width=32/></td><td>INI files</td></tr><tr><td><img src=":/MO/gui/content/modgroup" width=32/></td><td>ModGroup files</td></tr></table> - + Time this mod was installed @@ -4353,91 +4353,103 @@ Continue launching %1? - + + Blacklisted Executable + + + + + The executable you are attempted to launch is blacklisted in the virtual file system. This will likely prevent the executable, and any executables that are launched by this one, from seeing any mods. This could extend to INI files, save games and any other virtualized files. + +Continue launching %1? + + + + No profile set - + Failed to refresh list of esps: %1 - + Multiple esps/esls activated, please check that they don't conflict. - + Download? - + A download has been started but no installed page plugin recognizes it. If you download anyway no information (i.e. version) will be associated with the download. Continue? - + failed to update mod list: %1 - - + + login successful - + Login failed - + Login failed, try again? - + login failed: %1. Download will not be associated with an account - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + MO1 "Script Extender" load mechanism has left hook.dll in your game folder - - + + Description missing - + <a href="%1">hook.dll</a> has been found in your game folder (right click to copy the full path). This is most likely a leftover of setting the ModOrganizer 1 load mechanism to "Script Extender", in which case you must remove this file either by changing the load mechanism in ModOrganizer 1 or manually removing the file, otherwise the game is likely to crash and burn. - + failed to save load order: %1 - + The designated write target "%1" is not enabled. @@ -5398,7 +5410,7 @@ If the folder was still in use, restart MO and try again. - + Mod Organizer @@ -5475,12 +5487,12 @@ If the folder was still in use, restart MO and try again. - + Script Extender - + Proxy DLL @@ -5694,18 +5706,18 @@ Select Show Details option to see the full change-log. - - + + attempt to store setting for unknown plugin "%1" - + Error - + Failed to create "%1", you may not have the necessary permission. path remains unchanged. @@ -6042,52 +6054,52 @@ p, li { white-space: pre-wrap; } - + Author: - + Version: - + Description: - + Key - + Value - + Blacklisted Plugins (use <del> to remove): - + Workarounds - + Steam App ID - + The Steam AppID for your game - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -6103,17 +6115,17 @@ p, li { white-space: pre-wrap; } - + Load Mechanism - + Select loading mechanism. See help for details. - + Mod Organizer needs a dll to be injected into the game so all mods are visible to it. There are several means to do this: *Mod Organizer* (default) In this mode the Mod Organizer itself injects the dll. The disadvantage is that you always have to start the game through MO or a link created by it. @@ -6124,17 +6136,17 @@ If you use the Steam version of Oblivion the default will NOT work. In this case - + NMM Version - + The Version of Nexus Mod Manager to impersonate. - + Mod Organizer uses an API provided by the Nexus to provide features like checking for updates and downloading files. Unfortunately this API has not been made available officially to third party tools like MO so we have to impersonate the Nexus Mod Manager to be allowed in. On top of this Nexus has used the client identification to lock out outdated versions of NMM to force users to update. This means that MO also needs to impersonate the new version of NMM even if MO doesn't need an update. Therefore you can configure the version to identify as here. Please note that MO does identify itself as MO to the webserver, it's not lying about what it is. It is merely adding a "compatible" NMM version to the user agent. @@ -6143,44 +6155,44 @@ tl;dr-version: If Nexus-features don't work, insert the current version num - + Enforces that inactive ESPs and ESMs are never loaded. - + It seems that the Games occasionally load ESP or ESM files even if they haven't been activated as plugins. I don't yet know what the circumstances are, but user reports imply it is in some cases unwanted. If this is checked, ESPs and ESMs not checked in the List are invisible to the game and can not be loaded. - + Hide inactive ESPs/ESMs - + If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) - + If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) Uncheck this if you want to use Mod Organizer with total conversions (like Nehrim) but be aware that the game will crash if required files are not enabled. - + Force-enable game files - + Disable this to no longer display mods installed outside MO in the mod list (left pane). Assets from those mods will then be treated as having lowest mod priority together with the original game content. - + By default Mod Organizer will display esp+bsa bundles installed with foreign tools as mods (left pane). This allows you to control their priority in relation to other mods. This is particularly useful if you also use Steam Workshop to install mods. However, if you installed loose file mods outside MO which conflict with BSAs also installed outside MO those conflicts can't be resolved correctly. @@ -6188,44 +6200,63 @@ If you disable this feature, MO will only display official DLCs this way. Please - + Display mods installed outside MO - - + + For Skyrim, this can be used instead of Archive Invalidation. It should make AI redundant for all Profiles. For the other games this is not a sufficient replacement for AI! - + Back-date BSAs + + + Add executables to the blacklist to prevent them from +accessing the virtual file system. This is useful to prevent +unintended programs from being hooked. Hooking unintended +programs may affect the execution of these programs or the +programs you are intentionally running. + + + + + Add executables to the blacklist to prevent them from accessing the virtual file system. This is useful to prevent unintended programs from being hooked. Hooking unintended programs may affect the execution of these programs or the programs you are intentionally running. + + + Configure Executables Blacklist + + + + These are workarounds for problems with Mod Organizer. Please make sure you read the help text before changing anything here. - + Diagnostics - + Log Level - + Decides the amount of data printed to "ModOrganizer.log" - + Decides the amount of data printed to "ModOrganizer.log". "Debug" produces very useful information for finding problems. There is usually no noteworthy performance impact but the file may become rather large. If this is a problem you may prefer the "Info" level for regluar use. On the "Error" level the log file usually remains empty. @@ -6233,37 +6264,37 @@ For the other games this is not a sufficient replacement for AI! - + Debug - + Info (recommended) - + Warning - + Error - + Crash Dumps - + Decides which type of crash dumps are collected when injected processes crash. - + Decides which type of crash dumps are collected when injected processes crash. "None" Disables the generation of crash dumps by MO. @@ -6274,37 +6305,37 @@ For the other games this is not a sufficient replacement for AI! - + None - + Mini (recommended) - + Data - + Full - + Max Dumps To Keep - + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. - + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. Set "Crash Dumps" above to None to disable crash dump collection. @@ -6312,12 +6343,12 @@ For the other games this is not a sufficient replacement for AI! - + Hint: right click link and copy link location - + Logs and crash dumps are stored under your current instance in the <a href="LOGS_FULL_PATH">LOGS_DIR</a> and <a href="DUMPS_FULL_PATH">DUMPS_DIR</a> folders. @@ -6327,52 +6358,68 @@ For the other games this is not a sufficient replacement for AI! - + Confirm - + Changing the mod directory affects all your profiles! Mods not present (or named differently) in the new location will be disabled in all profiles. There is no way to undo this unless you backed up your profiles manually. Proceed? - + + Executables Blacklist + + + + + Enter one executable per line to be blacklisted from the virtual file system. +Mods and other virtualized files will not be visible to these executables and +any executables launched by them. + +Example: + Chrome.exe + Firefox.exe + + + + Select base directory - + Select download directory - + Select mod directory - + Select cache directory - + Select profiles directory - + Select overwrite directory - + Confirm? - + This will make all dialogs show up again where you checked the "Remember selection"-box. Continue? @@ -6576,7 +6623,7 @@ On Windows XP: UsvfsConnector - + Preparing vfs diff --git a/src/organizercore.cpp b/src/organizercore.cpp index b66895db..9ac3cb63 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -762,9 +762,9 @@ void OrganizerCore::prepareVFS() m_USVFS.updateMapping(fileMapping(m_CurrentProfile->name(), QString())); } -void OrganizerCore::updateVFSParams(int logLevel, int crashDumpsType) { +void OrganizerCore::updateVFSParams(int logLevel, int crashDumpsType, QString executableBlacklist) { setGlobalCrashDumpsType(crashDumpsType); - m_USVFS.updateParams(logLevel, crashDumpsType); + m_USVFS.updateParams(logLevel, crashDumpsType, executableBlacklist); } bool OrganizerCore::cycleDiagnostics() { @@ -1339,10 +1339,24 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, } } + for (auto exec : settings().executablesBlacklist().split(";")) { + if (exec.compare(binary.fileName(), Qt::CaseInsensitive)) { + if (QuestionBoxMemory::query(window, QString("blacklistedExecutable"), binary.fileName(), + tr("Blacklisted Executable"), + tr("The executable you are attempted to launch is blacklisted in the virtual file" + " system. This will likely prevent the executable, and any executables that are" + " launched by this one, from seeing any mods. This could extend to INI files, save" + " games and any other virtualized files.\n\nContinue launching %1?").arg(binary.fileName()), + QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::No) { + return INVALID_HANDLE_VALUE; + } + } + } + QString modsPath = settings().getModDirectory(); // Check if this a request with either an executable or a working directory under our mods folder - // then will start the processs in a virtualized "environment" with the appropriate paths fixed: + // then will start the process in a virtualized "environment" with the appropriate paths fixed: // (i.e. mods\FNIS\path\exe => game\data\path\exe) QString cwdPath = currentDirectory.absolutePath(); bool virtualizedCwd = cwdPath.startsWith(modsPath, Qt::CaseInsensitive); diff --git a/src/organizercore.h b/src/organizercore.h index 28ecef48..086fa11d 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -167,7 +167,7 @@ public: void prepareVFS(); - void updateVFSParams(int logLevel, int crashDumpsType); + void updateVFSParams(int logLevel, int crashDumpsType, QString executableBlacklist); bool cycleDiagnostics(); diff --git a/src/settings.cpp b/src/settings.cpp index ce3a68ff..c311ef29 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -386,6 +386,20 @@ QColor Settings::pluginListContainedColor() const return m_Settings.value("Settings/containedColor", QColor(0, 0, 255, 64)).value(); } +QString Settings::executablesBlacklist() const +{ + return m_Settings.value("Settings/executable_blacklist", ( + QStringList() + << "Chrome.exe" + << "Firefox.exe" + << "TSVNCache.exe" + << "TGitCache.exe" + << "Steam.exe" + << "GameOverlayUI.exe" + ).join(";") + ).toString(); +} + void Settings::setNexusLogin(QString username, QString password) { m_Settings.setValue("Settings/nexus_login", true); @@ -1080,6 +1094,8 @@ Settings::WorkaroundsTab::WorkaroundsTab(Settings *m_parent, m_forceEnableBox->setChecked(m_parent->forceEnableCoreFiles()); m_displayForeignBox->setChecked(m_parent->displayForeign()); + m_dialog.setExecutableBlacklist(m_parent->executablesBlacklist()); + } void Settings::WorkaroundsTab::update() @@ -1094,4 +1110,6 @@ void Settings::WorkaroundsTab::update() m_Settings.setValue("Settings/hide_unchecked_plugins", m_hideUncheckedBox->isChecked()); m_Settings.setValue("Settings/force_enable_core_files", m_forceEnableBox->isChecked()); m_Settings.setValue("Settings/display_foreign", m_displayForeignBox->isChecked()); + + m_Settings.setValue("Settings/executable_blacklist", m_dialog.getExecutableBlacklist()); } diff --git a/src/settings.h b/src/settings.h index 8fe053e8..667b1b4c 100644 --- a/src/settings.h +++ b/src/settings.h @@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#ifndef WORKAROUNDS_H -#define WORKAROUNDS_H +#ifndef SETTINGS_H +#define SETTINGS_H #include "loadmechanism.h" @@ -227,6 +227,7 @@ public: QColor pluginListContainedColor() const; + QString executablesBlacklist() const; /** * @brief set the nexus login information @@ -525,4 +526,4 @@ private: }; -#endif // WORKAROUNDS_H +#endif // SETTINGS_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index de18a850..e1c4b10d 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -34,6 +34,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #define WIN32_LEAN_AND_MEAN #include @@ -115,6 +116,32 @@ void SettingsDialog::on_categoriesBtn_clicked() } } +void SettingsDialog::on_execBlacklistBtn_clicked() +{ + bool ok = false; + QString result = QInputDialog::getMultiLineText( + this, + tr("Executables Blacklist"), + tr("Enter one executable per line to be blacklisted from the virtual file system.\n" + "Mods and other virtualized files will not be visible to these executables and\n" + "any executables launched by them.\n\n" + "Example:\n" + " Chrome.exe\n" + " Firefox.exe"), + m_ExecutableBlacklist.split(";").join("\n"), + &ok + ); + if (ok) { + QStringList blacklist; + for (auto exec : result.split("\n")) { + if (exec.trimmed().endsWith(".exe", Qt::CaseInsensitive)) { + blacklist << exec.trimmed(); + } + } + m_ExecutableBlacklist = blacklist.join(";"); + } +} + void SettingsDialog::on_bsaDateBtn_clicked() { IPluginGame const *game diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 386cd24e..911f910d 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#ifndef WORKAROUNDDIALOG_H -#define WORKAROUNDDIALOG_H +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H #include "tutorabledialog.h" #include @@ -69,17 +69,22 @@ public: QColor getOverwrittenColor() { return m_OverwrittenColor; } QColor getContainsColor() { return m_ContainsColor; } QColor getContainedColor() { return m_ContainedColor; } + QString getExecutableBlacklist() { return m_ExecutableBlacklist; } void setOverwritingColor(QColor col) { m_OverwritingColor = col; } void setOverwrittenColor(QColor col) { m_OverwrittenColor = col; } void setContainsColor(QColor col) { m_ContainsColor = col; } void setContainedColor(QColor col) { m_ContainedColor = col; } + void setExecutableBlacklist(QString blacklist) { m_ExecutableBlacklist = blacklist; } + private slots: void on_loginCheckBox_toggled(bool checked); void on_categoriesBtn_clicked(); + void on_execBlacklistBtn_clicked(); + void on_bsaDateBtn_clicked(); void on_browseDownloadDirBtn_clicked(); @@ -134,8 +139,10 @@ private: QColor m_OverwrittenColor; QColor m_ContainsColor; QColor m_ContainedColor; + + QString m_ExecutableBlacklist; }; -#endif // WORKAROUNDDIALOG_H +#endif // SETTINGSDIALOG_H diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 555180b5..ae13b9f2 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -17,7 +17,7 @@ - 4 + 5 @@ -1037,6 +1037,26 @@ For the other games this is not a sufficient replacement for AI!
    + + + + Add executables to the blacklist to prevent them from +accessing the virtual file system. This is useful to prevent +unintended programs from being hooked. Hooking unintended +programs may affect the execution of these programs or the +programs you are intentionally running. + + + Add executables to the blacklist to prevent them from accessing the virtual file system. This is useful to prevent unintended programs from being hooked. Hooking unintended programs may affect the execution of these programs or the programs you are intentionally running. + + + Configure Executables Blacklist + + + false + + + diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp index 4ffc81c0..9c81d0d9 100644 --- a/src/usvfsconnector.cpp +++ b/src/usvfsconnector.cpp @@ -132,7 +132,11 @@ UsvfsConnector::UsvfsConnector() CreateVFS(¶ms); - BlacklistExecutable(L"TSVNCache.exe"); + ClearExecutableBlacklist(); + for (auto exec : Settings::instance().executablesBlacklist().split(";")) { + std::wstring buf = exec.toStdWString(); + BlacklistExecutable(buf.data()); + } m_LogWorker.moveToThread(&m_WorkerThread); @@ -199,6 +203,11 @@ void UsvfsConnector::updateMapping(const MappingType &mapping) */ } -void UsvfsConnector::updateParams(int logLevel, int crashDumpsType) { +void UsvfsConnector::updateParams(int logLevel, int crashDumpsType, QString executableBlacklist) { USVFSUpdateParams(::logLevel(logLevel), ::crashDumpsType(crashDumpsType)); + ClearExecutableBlacklist(); + for (auto exec : executableBlacklist.split(";")) { + std::wstring buf = exec.toStdWString(); + BlacklistExecutable(buf.data()); + } } diff --git a/src/usvfsconnector.h b/src/usvfsconnector.h index 40f8857b..3aefb703 100644 --- a/src/usvfsconnector.h +++ b/src/usvfsconnector.h @@ -82,7 +82,7 @@ public: ~UsvfsConnector(); void updateMapping(const MappingType &mapping); - void updateParams(int logLevel, int crashDumpsType); + void updateParams(int logLevel, int crashDumpsType, QString executableBlacklist); private: -- cgit v1.3.1 From c9658e1a65097434867819a2b9c18531e32d7d75 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sat, 1 Dec 2018 23:23:28 +0100 Subject: * Added Folders and Files icons to Data tab. * Fixed empty folders not showing up in Data tab. --- src/mainwindow.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9a0e02aa..e0064ce2 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -106,6 +106,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -1147,7 +1148,9 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const DirectoryEntry &directoryEntry, bool conflictsOnly) { + QFileIconProvider *iconProvider = new QFileIconProvider(); { + for (const FileEntry::Ptr current : directoryEntry.getFiles()) { if (conflictsOnly && (current->getAlternatives().size() == 0)) { continue; @@ -1183,6 +1186,7 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director fileChild->setFont(1, font); } fileChild->setData(0, Qt::UserRole, ToQString(current->getFullPath())); + fileChild->setData(0, Qt::DecorationRole, iconProvider->icon(QFileIconProvider::File)); fileChild->setData(0, Qt::UserRole + 1, isArchive); fileChild->setData(1, Qt::UserRole, source); fileChild->setData(1, Qt::UserRole + 1, originID); @@ -1219,6 +1223,7 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director columns.append(""); if (!(*current)->isEmpty()) { QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); + directoryChild->setData(0, Qt::DecorationRole, iconProvider->icon(QFileIconProvider::Folder)); if (conflictsOnly) { updateTo(directoryChild, temp.str(), **current, conflictsOnly); if (directoryChild->childCount() != 0) { @@ -1235,6 +1240,11 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director subTree->addChild(directoryChild); } } + else { + QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); + directoryChild->setData(0, Qt::DecorationRole, iconProvider->icon(QFileIconProvider::Folder)); + subTree->addChild(directoryChild); + } } } @@ -1339,6 +1349,7 @@ void MainWindow::refreshDataTree() QStringList columns("data"); columns.append(""); QTreeWidgetItem *subTree = new QTreeWidgetItem(columns); + subTree->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked()); tree->insertTopLevelItem(0, subTree); subTree->setExpanded(true); @@ -1363,6 +1374,7 @@ void MainWindow::refreshDataTreeKeepExpandedNodes() QStringList columns("data"); columns.append(""); QTreeWidgetItem *subTree = new QTreeWidgetItem(columns); + subTree->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked()); tree->insertTopLevelItem(0, subTree); subTree->setExpanded(true); -- cgit v1.3.1 From f1384cdfa7f6affc5a2a66e78e7514662ee6c4ea Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 2 Dec 2018 12:48:16 +0100 Subject: Fixed context menu for Data tab showing the File version for empty folders. --- src/mainwindow.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e0064ce2..704ce838 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1148,9 +1148,8 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const DirectoryEntry &directoryEntry, bool conflictsOnly) { - QFileIconProvider *iconProvider = new QFileIconProvider(); - { - + bool isDirectory = false; + { for (const FileEntry::Ptr current : directoryEntry.getFiles()) { if (conflictsOnly && (current->getAlternatives().size() == 0)) { continue; @@ -1186,7 +1185,8 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director fileChild->setFont(1, font); } fileChild->setData(0, Qt::UserRole, ToQString(current->getFullPath())); - fileChild->setData(0, Qt::DecorationRole, iconProvider->icon(QFileIconProvider::File)); + fileChild->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::File)); + fileChild->setData(0, Qt::UserRole + 3, isDirectory); fileChild->setData(0, Qt::UserRole + 1, isArchive); fileChild->setData(1, Qt::UserRole, source); fileChild->setData(1, Qt::UserRole + 1, originID); @@ -1215,6 +1215,7 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director std::wostringstream temp; temp << directorySoFar << "\\" << directoryEntry.getName(); { + isDirectory = true; std::vector::const_iterator current, end; directoryEntry.getSubDirectories(current, end); for (; current != end; ++current) { @@ -1223,7 +1224,9 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director columns.append(""); if (!(*current)->isEmpty()) { QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - directoryChild->setData(0, Qt::DecorationRole, iconProvider->icon(QFileIconProvider::Folder)); + directoryChild->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); + directoryChild->setData(0, Qt::UserRole + 3, isDirectory); + if (conflictsOnly) { updateTo(directoryChild, temp.str(), **current, conflictsOnly); if (directoryChild->childCount() != 0) { @@ -1242,7 +1245,8 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director } else { QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - directoryChild->setData(0, Qt::DecorationRole, iconProvider->icon(QFileIconProvider::Folder)); + directoryChild->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); + directoryChild->setData(0, Qt::UserRole + 3, isDirectory); subTree->addChild(directoryChild); } } @@ -4570,7 +4574,8 @@ void MainWindow::on_dataTree_customContextMenuRequested(const QPoint &pos) m_ContextItem = dataTree->itemAt(pos.x(), pos.y()); QMenu menu; - if ((m_ContextItem != nullptr) && (m_ContextItem->childCount() == 0)) { + if ((m_ContextItem != nullptr) && (m_ContextItem->childCount() == 0) + && (m_ContextItem->data(0, Qt::UserRole + 3).toBool() != true)) { menu.addAction(tr("Open/Execute"), this, SLOT(openDataFile())); menu.addAction(tr("Add as Executable"), this, SLOT(addAsExecutable())); -- cgit v1.3.1 From c0b55690bc47102459ef7b1720d69945d6523d08 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sun, 2 Dec 2018 20:26:26 -0600 Subject: Add "send to separator..." option for mods --- src/CMakeLists.txt | 3 ++ src/listdialog.cpp | 46 ++++++++++++++++++++++++++ src/listdialog.h | 25 ++++++++++++++ src/listdialog.ui | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/mainwindow.cpp | 57 +++++++++++++++++++++++++++++--- src/mainwindow.h | 1 + src/organizer.pro | 9 +++-- 7 files changed, 230 insertions(+), 7 deletions(-) create mode 100644 src/listdialog.cpp create mode 100644 src/listdialog.h create mode 100644 src/listdialog.ui (limited to 'src/mainwindow.cpp') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 372b9519..3f004ddc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,6 +91,7 @@ SET(organizer_SRCS usvfsconnector.cpp eventfilter.cpp moshortcut.cpp + listdialog.cpp shared/windows_error.cpp shared/error_report.cpp @@ -185,6 +186,7 @@ SET(organizer_HDRS eventfilter.h descriptionpage.h moshortcut.h + listdialog.h shared/windows_error.h shared/error_report.h @@ -225,6 +227,7 @@ SET(organizer_UIS previewdialog.ui browserdialog.ui aboutdialog.ui + listdialog.ui ) SET(organizer_QRCS diff --git a/src/listdialog.cpp b/src/listdialog.cpp new file mode 100644 index 00000000..4acfa5de --- /dev/null +++ b/src/listdialog.cpp @@ -0,0 +1,46 @@ +/* +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#include "listdialog.h" +#include "ui_listdialog.h" + +ListDialog::ListDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ListDialog) +{ + ui->setupUi(this); +} + +ListDialog::~ListDialog() +{ + delete ui; +} + +void ListDialog::setChoices(QStringList choices) +{ + ui->listWidget->clear(); + ui->listWidget->addItems(choices); +} + +QString ListDialog::getChoice() const +{ + if (ui->listWidget->selectedItems().length()) { + return ui->listWidget->currentItem()->text(); + } else { + return ""; + } +} diff --git a/src/listdialog.h b/src/listdialog.h new file mode 100644 index 00000000..5e826a30 --- /dev/null +++ b/src/listdialog.h @@ -0,0 +1,25 @@ +#ifndef LISTDIALOG_H +#define LISTDIALOG_H + +#include + +namespace Ui { +class ListDialog; +} + +class ListDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ListDialog(QWidget *parent = nullptr); + ~ListDialog(); + + void setChoices(QStringList choices); + QString getChoice() const; + +private: + Ui::ListDialog *ui; +}; + +#endif // LISTDIALOG_H diff --git a/src/listdialog.ui b/src/listdialog.ui new file mode 100644 index 00000000..38819c87 --- /dev/null +++ b/src/listdialog.ui @@ -0,0 +1,96 @@ + + + ListDialog + + + + 0 + 0 + 260 + 340 + + + + Dialog + + + + + 90 + 299 + 161 + 32 + + + + Qt::LeftToRight + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 10 + 10 + 240 + 280 + + + + + 0 + 0 + + + + + 240 + 280 + + + + QAbstractItemView::ScrollPerPixel + + + + + + + buttonBox + accepted() + ListDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ListDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 704ce838..44fc0bf3 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -85,6 +85,7 @@ along with Mod Organizer. If not, see . #include #include #include "localsavegames.h" +#include "listdialog.h" #include #include @@ -1149,7 +1150,7 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const DirectoryEntry &directoryEntry, bool conflictsOnly) { bool isDirectory = false; - { + { for (const FileEntry::Ptr current : directoryEntry.getFiles()) { if (conflictsOnly && (current->getAlternatives().size() == 0)) { continue; @@ -3721,6 +3722,7 @@ void MainWindow::addModSendToContextMenu(QMenu *menu) sub_menu->addAction(tr("Top"), this, SLOT(sendSelectedModsToTop_clicked())); sub_menu->addAction(tr("Bottom"), this, SLOT(sendSelectedModsToBottom_clicked())); sub_menu->addAction(tr("Priority..."), this, SLOT(sendSelectedModsToPriority_clicked())); + sub_menu->addAction(tr("Separator..."), this, SLOT(sendSelectedModsToSeparator_clicked())); menu->addMenu(sub_menu); menu->addSeparator(); @@ -4574,7 +4576,7 @@ void MainWindow::on_dataTree_customContextMenuRequested(const QPoint &pos) m_ContextItem = dataTree->itemAt(pos.x(), pos.y()); QMenu menu; - if ((m_ContextItem != nullptr) && (m_ContextItem->childCount() == 0) + if ((m_ContextItem != nullptr) && (m_ContextItem->childCount() == 0) && (m_ContextItem->data(0, Qt::UserRole + 3).toBool() != true)) { menu.addAction(tr("Open/Execute"), this, SLOT(openDataFile())); menu.addAction(tr("Add as Executable"), this, SLOT(addAsExecutable())); @@ -5673,8 +5675,8 @@ void MainWindow::sendSelectedModsToPriority(int newPriority) QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { std::vector modsToMove; - for (QModelIndex idx : selection->selectedRows()) { - modsToMove.push_back(idx.data().toInt()); + for (auto idx : selection->selectedRows(ModList::COL_PRIORITY)) { + modsToMove.push_back(m_OrganizerCore.currentProfile()->modIndexByPriority(idx.data().toInt())); } m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority); } else { @@ -5702,3 +5704,50 @@ void MainWindow::sendSelectedModsToPriority_clicked() sendSelectedModsToPriority(newPriority); } + +void MainWindow::sendSelectedModsToSeparator_clicked() +{ + QStringList separators; + for (auto mod : m_OrganizerCore.modList()->allMods()) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(mod)); + if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) { + separators << mod.chopped(10); + } + } + + ListDialog dialog(this); + 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()) { + 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() && selection->selectedRows().count() > 1) { + std::vector modsToMove; + for (QModelIndex idx : selection->selectedRows(ModList::COL_PRIORITY)) { + modsToMove.push_back(m_OrganizerCore.currentProfile()->modIndexByPriority(idx.data().toInt())); + } + m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority); + } else { + int oldPriority = m_OrganizerCore.currentProfile()->getModPriority(m_ContextRow); + if (oldPriority < newPriority) + --newPriority; + m_OrganizerCore.modList()->changeModPriority(m_ContextRow, newPriority); + } + } + } +} diff --git a/src/mainwindow.h b/src/mainwindow.h index 06219a5a..62ffe3de 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -431,6 +431,7 @@ private slots: void sendSelectedModsToTop_clicked(); void sendSelectedModsToBottom_clicked(); void sendSelectedModsToPriority_clicked(); + void sendSelectedModsToSeparator_clicked(); // savegame context menu void deleteSavegame_clicked(); void fixMods_clicked(SaveGameInfo::MissingAssets const &missingAssets); diff --git a/src/organizer.pro b/src/organizer.pro index 0ef2638b..582fde9f 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -91,7 +91,8 @@ SOURCES += \ modinforegular.cpp \ modinfobackup.cpp \ modinfooverwrite.cpp \ - modinfoforeign.cpp + modinfoforeign.cpp \ + listdialog.cpp HEADERS += \ @@ -167,7 +168,8 @@ HEADERS += \ modinforegular.h \ modinfobackup.h \ modinfooverwrite.h \ - modinfoforeign.h + modinfoforeign.h \ + listdialog.h FORMS += \ transfersavesdialog.ui \ @@ -196,7 +198,8 @@ FORMS += \ problemsdialog.ui \ previewdialog.ui \ browserdialog.ui \ - aboutdialog.ui + aboutdialog.ui \ + listdialog.ui RESOURCES += \ resources.qrc \ -- cgit v1.3.1 From 79c451bb170c15d19f0d0f71a9e1d86fb843b641 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Mon, 3 Dec 2018 06:04:16 -0600 Subject: Improvements to "send to separator..." dialog * Scale contents with dialog size * Save and restore dialog geometry * Add a better window title --- src/listdialog.ui | 104 +++++++++++++++++++++++------------------------------ src/mainwindow.cpp | 6 ++++ 2 files changed, 50 insertions(+), 60 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/listdialog.ui b/src/listdialog.ui index 032bc131..7aa3eba0 100644 --- a/src/listdialog.ui +++ b/src/listdialog.ui @@ -13,66 +13,50 @@ Select an item... - - - - 10 - 345 - 240 - 32 - - - - Qt::LeftToRight - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 10 - 10 - 240 - 300 - - - - - 0 - 0 - - - - - 240 - 280 - - - - QAbstractItemView::ScrollPerPixel - - - - - - 10 - 320 - 240 - 20 - - - - - - - Filter - - + + + + + + 0 + 0 + + + + + 240 + 280 + + + + QAbstractItemView::ScrollPerPixel + + + + + + + + + + Filter + + + + + + + Qt::LeftToRight + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 44fc0bf3..f68d9846 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5716,7 +5716,12 @@ void MainWindow::sendSelectedModsToSeparator_clicked() } ListDialog dialog(this); + QSettings &settings = m_OrganizerCore.settings().directInterface(); + QString key = QString("geometry/%1").arg(dialog.objectName()); + + dialog.setWindowTitle("Select a separator..."); dialog.setChoices(separators); + dialog.restoreGeometry(settings.value(key).toByteArray()); if (dialog.exec() == QDialog::Accepted) { QString result = dialog.getChoice(); if (!result.isEmpty()) { @@ -5750,4 +5755,5 @@ void MainWindow::sendSelectedModsToSeparator_clicked() } } } + settings.setValue(key, dialog.saveGeometry()); } -- cgit v1.3.1 From 40919c15203acd106e6451de14bf1cd6c9a162fc Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Tue, 4 Dec 2018 18:08:39 -0600 Subject: Create empty mods above the selected mod --- src/mainwindow.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f68d9846..3f88f5ad 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2861,12 +2861,21 @@ void MainWindow::createEmptyMod_clicked() return; } + int newPriority = -1; + if (m_ContextRow >= 0 && m_ModListSortProxy->sortColumn() == ModList::COL_PRIORITY) { + newPriority = m_OrganizerCore.currentProfile()->getModPriority(m_ContextRow); + } + IModInterface *newMod = m_OrganizerCore.createMod(name); if (newMod == nullptr) { return; } m_OrganizerCore.refreshModList(); + + if (newPriority >= 0) { + m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); + } } void MainWindow::createSeparator_clicked() -- cgit v1.3.1 From 02fe0d3384c60b96e85274e94f4c3b344780a0ca Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Fri, 7 Dec 2018 12:52:20 -0600 Subject: Improve refresh of data when changing profile settings --- src/mainwindow.cpp | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3f88f5ad..de015c54 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1141,8 +1141,15 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) } LocalSavegames *saveGames = m_OrganizerCore.managedGame()->feature(); - if (saveGames != nullptr && saveGames->updateSaveGames(m_OrganizerCore.currentProfile())) { - refreshSaveList(); + if (saveGames != nullptr) { + if (saveGames->prepareProfile(m_OrganizerCore.currentProfile())) + refreshSaveList(); + } + + BSAInvalidation *invalidation = m_OrganizerCore.managedGame()->feature(); + if (invalidation != nullptr) { + if (invalidation->prepareProfile(m_OrganizerCore.currentProfile())) + QTimer::singleShot(5, &m_OrganizerCore, SLOT(profileRefresh())); } } } @@ -1407,12 +1414,17 @@ QDir MainWindow::currentSavesDir() const if (m_OrganizerCore.currentProfile()->localSavesEnabled()) { savesDir.setPath(m_OrganizerCore.currentProfile()->savePath()); } else { + QString iniPath = m_OrganizerCore.currentProfile()->localSettingsEnabled() + ? m_OrganizerCore.currentProfile()->absolutePath() + : m_OrganizerCore.managedGame()->documentsDirectory().absolutePath(); + iniPath += "/" + m_OrganizerCore.managedGame()->iniFiles()[0]; + wchar_t path[MAX_PATH]; ::GetPrivateProfileStringW( L"General", L"SLocalSavePath", L"Saves", path, MAX_PATH, - ToWString(m_OrganizerCore.currentProfile()->absolutePath() + "/" + - m_OrganizerCore.managedGame()->iniFiles()[0]).c_str()); + iniPath.toStdWString().c_str() + ); savesDir.setPath(m_OrganizerCore.managedGame()->documentsDirectory().absoluteFilePath(QString::fromWCharArray(path))); } @@ -2026,11 +2038,16 @@ void MainWindow::on_actionAdd_Profile_triggered() } LocalSavegames *saveGames = m_OrganizerCore.managedGame()->feature(); - if (saveGames != nullptr && saveGames->updateSaveGames(m_OrganizerCore.currentProfile())) { - refreshSaveList(); + if (saveGames != nullptr) { + if (saveGames->prepareProfile(m_OrganizerCore.currentProfile())) + refreshSaveList(); } -// addProfile(); + BSAInvalidation *invalidation = m_OrganizerCore.managedGame()->feature(); + if (invalidation != nullptr) { + if (invalidation->prepareProfile(m_OrganizerCore.currentProfile())) + QTimer::singleShot(5, &m_OrganizerCore, SLOT(profileRefresh())); + } } void MainWindow::on_actionModify_Executables_triggered() -- cgit v1.3.1 From 4914fd172deffe15631ab37264a1ffda33637e99 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sat, 8 Dec 2018 01:19:37 -0600 Subject: Add workarounds button to reset window geometries --- src/mainwindow.cpp | 34 ++-- src/organizer_en.ts | 527 +++++++++++++++++++++++++------------------------ src/settings.cpp | 11 ++ src/settingsdialog.cpp | 4 + src/settingsdialog.h | 1 + src/settingsdialog.ui | 16 ++ 6 files changed, 323 insertions(+), 270 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index de015c54..84b77945 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1738,21 +1738,31 @@ void MainWindow::processUpdates() { void MainWindow::storeSettings(QSettings &settings) { settings.setValue("group_state", ui->groupCombo->currentIndex()); - - settings.setValue("window_geometry", saveGeometry()); - settings.setValue("window_split", ui->splitter->saveState()); - settings.setValue("log_split", ui->topLevelSplitter->saveState()); - - settings.setValue("browser_geometry", m_IntegratedBrowser.saveGeometry()); - - settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked()); - settings.setValue("selected_executable", ui->executablesListBox->currentIndex()); - for (const std::pair kv : m_PersistedGeometry) { - QString key = QString("geometry/") + kv.first; - settings.setValue(key, kv.second->saveState()); + if (settings.value("reset_geometry", false).toBool()) { + settings.remove("window_geometry"); + settings.remove("window_split"); + settings.remove("log_split"); + settings.remove("filters_visible"); + settings.remove("browser_geometry"); + settings.beginGroup("geometry"); + for (auto key : settings.childKeys()) { + settings.remove(key); + } + settings.endGroup(); + settings.remove("reset_geometry"); + } else { + settings.setValue("window_geometry", saveGeometry()); + settings.setValue("window_split", ui->splitter->saveState()); + settings.setValue("log_split", ui->topLevelSplitter->saveState()); + settings.setValue("browser_geometry", m_IntegratedBrowser.saveGeometry()); + settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked()); + for (const std::pair kv : m_PersistedGeometry) { + QString key = QString("geometry/") + kv.first; + settings.setValue(key, kv.second->saveState()); + } } } diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 2ea1c09a..1b752ae5 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -1620,7 +1620,7 @@ p, li { white-space: pre-wrap; } - + Create Backup @@ -1784,8 +1784,8 @@ p, li { white-space: pre-wrap; } - - + + Refresh @@ -1969,7 +1969,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -2010,7 +2010,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -2145,678 +2145,678 @@ Right now this has very limited functionality - + Also in: <br> - + No conflict - + <Edit...> - + This bsa is enabled in the ini file so it may be required! - + Activating Network Proxy - + Notice: Your current MO version (%1) is lower than the previous version (%2).<br>The GUI may not downgrade gracefully, so you may experience oddities.<br>However, there should be no serious issues. - + Choose Mod - + Mod Archive - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - + failed to spawn notepad.exe: %1 - + failed to change origin name: %1 - + failed to move "%1" from mod "%2" to "%3": %4 - + <Contains %1> - + <Checked> - + <Unchecked> - + <Update> - + <Mod Backup> - + <Managed by MO> - + <Managed outside MO> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - - + + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - + Failed to create backup. - + You need to be logged in with Nexus to resume a download - - + + You need to be logged in with Nexus to endorse - + Failed to display overwrite dialog: %1 - + Nexus ID for this Mod is unknown - + Web page for this mod is unknown - - - + + + Create Mod... - + This will create an empty mod. Please enter a name: - - + + A mod with this name already exists - + Create Separator... - + This will create a new separator. Please enter a name: - + A separator with this name already exists - + This will move all files from overwrite into a new, regular mod. Please enter a name: - - + + Are you sure? - + About to recursively delete: - + Not logged in, endorsement information will be wrong - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Export to csv - + CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet. You can also use online editors and converters instead. - + Select what mods you want export: - + All installed mods - + Only active (checked) mods from your current profile - + All currently visible mods in the mod list - + Choose what Columns to export: - + Mod_Priority - + Mod_Name - + Mod_Status - + Primary_Category - + Nexus_ID - + Mod_Nexus_URL - + Mod_Version - + Install_Date - + Download_File_Name - + export failed: %1 - + Open Game folder - + Open MyGames folder - + Open Instance folder - + Open Mods folder - + Open Profile folder - + Open Downloads folder - + Open MO2 Install folder - + Open MO2 Plugins folder - + Open MO2 Logs folder - + Install Mod... - + Create empty mod - + Create Separator - + Enable all visible - + Disable all visible - + Check all for update - + Export to csv... - - + + Send to - - + + Top - - + + Bottom - - + + Priority... - + Separator... - + All Mods - + Sync to Mods... - + Clear Overwrite... - - + + Open in Explorer - + Restore Backup - + Remove Backup... - - + + Change Categories - - + + Primary Category - + Rename Separator... - + Remove Separator... - + Select Color... - + Reset Color - + Change versioning scheme - + Un-ignore update - + Ignore update - - + + Enable selected - - + + Disable selected - + Rename Mod... - + Reinstall Mod - + Remove Mod... - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Mark as converted/working - + Visit on Nexus - + Visit web page - + Information... - - + + Exception: - - + + Unknown exception - + <All> - + <Multiple> - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -2824,12 +2824,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -2837,335 +2837,335 @@ You can also use online editors and converters instead. - + failed to remove %1 - + failed to create %1 - + Can't change download directory while downloads are in progress! - + failed to write to file %1 - + %1 written - + Select binary - + Binary - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - - + + Set Priority - + Set the priority of the selected plugins - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Thank you! - + Thank you for your endorsement! - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - - + + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Extract... - + This will restart MO, continue? - + Edit Categories... - + Deselect filter - + Remove - + Enable all - + Disable all - + Unlock load order - + Lock load order - + depends on missing "%1" - + incompatible with "%1" - + Please wait while LOOT is running - + loot failed. Exit code was: %1 - + failed to start loot - + failed to run loot: %1 - + Errors occured - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of modlist created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file - + Set the priority of the selected mods @@ -5462,13 +5462,13 @@ If the folder was still in use, restart MO and try again. - - + + <Manage...> - + failed to parse profile %1: %2 @@ -6268,27 +6268,38 @@ programs you are intentionally running. - + + + Resets the window geometries for all windows. This can be useful if a window becomes too small or too large, if a column becomes too thin or too wide, and in similar situations. + + + + + Reset Window Geometries + + + + These are workarounds for problems with Mod Organizer. Please make sure you read the help text before changing anything here. - + Diagnostics - + Log Level - + Decides the amount of data printed to "ModOrganizer.log" - + Decides the amount of data printed to "ModOrganizer.log". "Debug" produces very useful information for finding problems. There is usually no noteworthy performance impact but the file may become rather large. If this is a problem you may prefer the "Info" level for regluar use. On the "Error" level the log file usually remains empty. @@ -6296,37 +6307,37 @@ programs you are intentionally running. - + Debug - + Info (recommended) - + Warning - + Error - + Crash Dumps - + Decides which type of crash dumps are collected when injected processes crash. - + Decides which type of crash dumps are collected when injected processes crash. "None" Disables the generation of crash dumps by MO. @@ -6337,37 +6348,37 @@ programs you are intentionally running. - + None - + Mini (recommended) - + Data - + Full - + Max Dumps To Keep - + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. - + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. Set "Crash Dumps" above to None to disable crash dump collection. @@ -6375,12 +6386,12 @@ programs you are intentionally running. - + Hint: right click link and copy link location - + Logs and crash dumps are stored under your current instance in the <a href="LOGS_FULL_PATH">LOGS_DIR</a> and <a href="DUMPS_FULL_PATH">DUMPS_DIR</a> folders. diff --git a/src/settings.cpp b/src/settings.cpp index c311ef29..3abb0988 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1112,4 +1112,15 @@ void Settings::WorkaroundsTab::update() m_Settings.setValue("Settings/display_foreign", m_displayForeignBox->isChecked()); m_Settings.setValue("Settings/executable_blacklist", m_dialog.getExecutableBlacklist()); + + if (m_dialog.getResetGeometries()) { + m_Settings.setValue("reset_geometry", true); + if (QMessageBox::question(nullptr, + tr("Restart Mod Organizer?"), + tr("In order to reset the window geometries, MO must be restarted.\n" + "Restart it now?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + qApp->exit(INT_MAX); + } + } } diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index e1c4b10d..dec2532c 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -94,6 +94,10 @@ void SettingsDialog::accept() TutorableDialog::accept(); } +bool SettingsDialog::getResetGeometries() +{ + return ui->resetGeometryBtn->isChecked(); +} void SettingsDialog::on_loginCheckBox_toggled(bool checked) { diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 911f910d..95ed288f 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -70,6 +70,7 @@ public: QColor getContainsColor() { return m_ContainsColor; } QColor getContainedColor() { return m_ContainedColor; } QString getExecutableBlacklist() { return m_ExecutableBlacklist; } + bool getResetGeometries(); void setOverwritingColor(QColor col) { m_OverwritingColor = col; } void setOverwrittenColor(QColor col) { m_OverwrittenColor = col; } diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index a6c6a03b..91bfca4e 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -1057,6 +1057,22 @@ programs you are intentionally running. + + + + Resets the window geometries for all windows. This can be useful if a window becomes too small or too large, if a column becomes too thin or too wide, and in similar situations. + + + Resets the window geometries for all windows. This can be useful if a window becomes too small or too large, if a column becomes too thin or too wide, and in similar situations. + + + Reset Window Geometries + + + true + + + -- cgit v1.3.1 From c501b5cca081e1b3697904a3b4e1e236ea9c4e69 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sat, 8 Dec 2018 10:37:46 -0600 Subject: Add notes column to mod list --- src/mainwindow.cpp | 4 ++++ src/modinfo.cpp | 2 +- src/modinfo.h | 17 ++++++++++++++--- src/modinfodialog.cpp | 2 ++ src/modinfodialog.ui | 25 ++++++++++++++++++++++++- src/modinfoforeign.h | 2 ++ src/modinfooverwrite.h | 2 ++ src/modinforegular.cpp | 17 +++++++++++++++-- src/modinforegular.h | 12 ++++++++++++ src/modlist.cpp | 24 ++++++++++++++++++++++-- src/modlist.h | 3 ++- 11 files changed, 100 insertions(+), 10 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 84b77945..9a40ce2e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -299,6 +299,7 @@ MainWindow::MainWindow(QSettings &initSettings ui->modList->header()->setSectionHidden(ModList::COL_MODID, true); ui->modList->header()->setSectionHidden(ModList::COL_GAME, true); ui->modList->header()->setSectionHidden(ModList::COL_INSTALLTIME, true); + ui->modList->header()->setSectionHidden(ModList::COL_NOTES, true); } ui->modList->header()->setSectionHidden(ModList::COL_NAME, false); // prevent the name-column from being hidden @@ -1726,6 +1727,9 @@ void MainWindow::processUpdates() { lastHidden = hidden; } } + if (lastVersion < QVersionNumber(2,1,6)) { + ui->modList->header()->setSectionHidden(ModList::COL_NOTES, true); + } } if (currentVersion > lastVersion) diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 4c41a114..1c6139f6 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -104,7 +104,7 @@ QString ModInfo::getContentTypeName(int contentType) case CONTENT_SOUND: return tr("Sound Effects"); case CONTENT_SCRIPT: return tr("Scripts"); case CONTENT_SKSE: return tr("Script Extender"); - case CONTENT_SKSEFILES: return tr("Script Extender Files"); + case CONTENT_SKSEFILES: return tr("Script Extender Files"); case CONTENT_SKYPROC: return tr("SkyProc Tools"); case CONTENT_MCM: return tr("MCM Data"); case CONTENT_INI: return tr("INI files"); diff --git a/src/modinfo.h b/src/modinfo.h index 235fb50c..5a69185d 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -44,7 +44,7 @@ namespace MOShared { class DirectoryEntry; } /** * @brief Represents meta information about a single mod. - * + * * Represents meta information about a single mod. The class interface is used * to manage the mod collection * @@ -85,7 +85,7 @@ public: CONTENT_SOUND, CONTENT_SCRIPT, CONTENT_SKSE, - CONTENT_SKSEFILES, + CONTENT_SKSEFILES, CONTENT_SKYPROC, CONTENT_MCM, CONTENT_INI, @@ -268,6 +268,12 @@ public: **/ virtual void setCategory(int categoryID, bool active) = 0; + /** + * @brief changes the comments (manually set information displayed in the mod list) for this mod + * @param comments new comments + */ + virtual void setComments(const QString &comments) = 0; + /** * @brief change the notes (manually set information) for this mod * @param notes new notes @@ -495,6 +501,11 @@ public: */ virtual QString getDescription() const = 0; + /** + * @return comments for this mod + */ + virtual QString comments() const = 0; + /** * @return notes for this mod */ @@ -526,7 +537,7 @@ public: virtual QStringList archives() const = 0; /* - *@return the color choosen by the user for the mod/separator + *@return the color choosen by the user for the mod/separator */ virtual QColor getColor() { return QColor(); } diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 54910e41..416eca58 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -112,6 +112,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } ui->sourceGameEdit->setCurrentIndex(ui->sourceGameEdit->findData(gameName)); + ui->commentsEdit->setText(modInfo->comments()); ui->notesEdit->setText(modInfo->notes()); ui->descriptionView->setPage(new DescriptionPage); @@ -189,6 +190,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ModInfoDialog::~ModInfoDialog() { + m_ModInfo->setComments(ui->commentsEdit->text()); m_ModInfo->setNotes(ui->notesEdit->toPlainText()); saveCategories(ui->categoriesTree->invisibleRootItem()); saveIniTweaks(); // ini tweaks are written to the ini file directly. This is the only information not managed by ModInfo diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index b3873bf3..671c7f31 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -782,7 +782,30 @@ p, li { white-space: pre-wrap; } - + + + Enter comments about the mod here. These are displayed in the notes column of the mod list. + + + Enter comments about the mod here. These are displayed in the notes column of the mod list. + + + Enter comments about the mod here. These are displayed in the notes column of the mod list. + + + + + + + Enter notes about the mod here. These can be viewed in the mod list by hovering over the notes column or the flags column. + + + Enter notes about the mod here. These can be viewed in the mod list by hovering over the notes column or the flags column. + + + Enter notes about the mod here. These can be viewed in the mod list by hovering over the notes column or the flags column. + + diff --git a/src/modinfoforeign.h b/src/modinfoforeign.h index 0d7b3847..93061294 100644 --- a/src/modinfoforeign.h +++ b/src/modinfoforeign.h @@ -18,6 +18,7 @@ public: virtual bool updateNXMInfo() { return false; } virtual void setCategory(int, bool) {} virtual bool setName(const QString&) { return false; } + virtual void setComments(const QString&) {} virtual void setNotes(const QString&) {} virtual void setGameName(QString) {} virtual void setNexusID(int) {} @@ -33,6 +34,7 @@ public: virtual bool isEmpty() const { return false; } virtual QString name() const; virtual QString internalName() const { return name(); } + virtual QString comments() const { return ""; } virtual QString notes() const { return ""; } virtual QDateTime creationTime() const; virtual QString absolutePath() const; diff --git a/src/modinfooverwrite.h b/src/modinfooverwrite.h index 5681dc6f..4b98d715 100644 --- a/src/modinfooverwrite.h +++ b/src/modinfooverwrite.h @@ -20,6 +20,7 @@ public: virtual bool updateNXMInfo() { return false; } virtual void setCategory(int, bool) {} virtual bool setName(const QString&) { return false; } + virtual void setComments(const QString&) {} virtual void setNotes(const QString&) {} virtual void setGameName(QString) {} virtual void setNexusID(int) {} @@ -35,6 +36,7 @@ public: virtual bool alwaysEnabled() const { return true; } virtual bool isEmpty() const; virtual QString name() const { return "Overwrite"; } + virtual QString comments() const { return ""; } virtual QString notes() const { return ""; } virtual QDateTime creationTime() const { return QDateTime(); } virtual QString absolutePath() const; diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index d0f56a52..6a52d3df 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -76,6 +76,7 @@ bool ModInfoRegular::isEmpty() const void ModInfoRegular::readMeta() { QSettings metaFile(m_Path + "/meta.ini", QSettings::IniFormat); + m_Comments = metaFile.value("comments", "").toString(); m_Notes = metaFile.value("notes", "").toString(); QString tempGameName = metaFile.value("gameName", m_GameName).toString(); if (tempGameName.size()) m_GameName = tempGameName; @@ -148,6 +149,7 @@ void ModInfoRegular::saveMeta() metaFile.setValue("repository", m_Repository); metaFile.setValue("gameName", m_GameName); metaFile.setValue("modid", m_NexusID); + metaFile.setValue("comments", m_Comments); metaFile.setValue("notes", m_Notes); metaFile.setValue("nexusDescription", m_NexusDescription); metaFile.setValue("url", m_URL); @@ -324,6 +326,12 @@ bool ModInfoRegular::setName(const QString &name) return true; } +void ModInfoRegular::setComments(const QString &comments) +{ + m_Comments = comments; + m_MetaInfoChanged = true; +} + void ModInfoRegular::setNotes(const QString ¬es) { m_Notes = notes; @@ -405,7 +413,7 @@ void ModInfoRegular::setColor(QColor color) m_MetaInfoChanged = true; } -QColor ModInfoRegular::getColor() +QColor ModInfoRegular::getColor() { return m_Color; } @@ -510,7 +518,7 @@ std::vector ModInfoRegular::getContents() const if (sePluginDir.entryList(QStringList() << "*.dll").size() > 0) { m_CachedContent.push_back(CONTENT_SKSE); } - } + } } if (dir.exists("textures") || dir.exists("icons") || dir.exists("bookart")) m_CachedContent.push_back(CONTENT_TEXTURE); @@ -567,6 +575,11 @@ QString ModInfoRegular::getDescription() const } } +QString ModInfoRegular::comments() const +{ + return m_Comments; +} + QString ModInfoRegular::notes() const { return m_Notes; diff --git a/src/modinforegular.h b/src/modinforegular.h index 78a6e58a..33b26998 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -90,6 +90,12 @@ public: **/ bool setName(const QString &name); + /** + * @brief changes the comments (manually set information displayed in the mod list) for this mod + * @param comments new comments + */ + void setComments(const QString &comments); + /** * @brief change the notes (manually set information) for this mod * @param notes new notes @@ -279,6 +285,11 @@ public: */ virtual QString getDescription() const; + /** + * @return comments for this mod + */ + virtual QString comments() const; + /** * @return manually set notes for this mod */ @@ -353,6 +364,7 @@ private: QString m_Name; QString m_Path; QString m_InstallationFile; + QString m_Comments; QString m_Notes; QString m_NexusDescription; QString m_Repository; diff --git a/src/modlist.cpp b/src/modlist.cpp index 7ca0667a..547cc853 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -150,7 +150,14 @@ QString ModList::getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const case ModInfo::FLAG_SEPARATOR: return tr("Separator"); case ModInfo::FLAG_INVALID: return tr("No valid game data"); case ModInfo::FLAG_NOTENDORSED: return tr("Not endorsed yet"); - case ModInfo::FLAG_NOTES: return QString("%1").arg(modInfo->notes().replace("\n", "
    ")); + case ModInfo::FLAG_NOTES: { + QStringList output; + if (!modInfo->comments().isEmpty()) + output << QString("%1").arg(modInfo->comments()); + if (!modInfo->notes().isEmpty()) + output << QString("%1").arg(modInfo->notes()).replace("\n", "
    "); + return output.join("
    "); + } case ModInfo::FLAG_CONFLICT_OVERWRITE: return tr("Overwrites files"); case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return tr("Overwritten files"); case ModInfo::FLAG_CONFLICT_MIXED: return tr("Overwrites & Overwritten"); @@ -276,6 +283,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else { return QVariant(); } + } else if (column == COL_NOTES) { + return modInfo->comments(); } else { return tr("invalid"); } @@ -295,6 +304,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } else if (column == COL_VERSION) { return QVariant(Qt::AlignRight | Qt::AlignVCenter); + } else if (column == COL_NOTES) { + return QVariant(Qt::AlignLeft | Qt::AlignVCenter); } else { return QVariant(Qt::AlignCenter | Qt::AlignVCenter); } @@ -443,6 +454,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } return ToQString(categoryString.str()); + } else if (column == COL_NOTES) { + return getFlagText(ModInfo::FLAG_NOTES, modInfo); } else { return QVariant(); } @@ -559,6 +572,10 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role) result = false; } } break; + case COL_NOTES: { + info->setComments(value.toString()); + result = true; + } break; default: { qWarning("edit on column \"%s\" not supported", getColumnName(index.column()).toUtf8().constData()); @@ -625,7 +642,8 @@ Qt::ItemFlags ModList::flags(const QModelIndex &modelIndex) const (modelIndex.column() == COL_MODID)) { result |= Qt::ItemIsEditable; } - if ((modelIndex.column() == COL_NAME) + if (((modelIndex.column() == COL_NAME) || + (modelIndex.column() == COL_NOTES)) && (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end())) { result |= Qt::ItemIsEditable; } @@ -1147,6 +1165,7 @@ QString ModList::getColumnName(int column) case COL_GAME: return tr("Source Game"); case COL_MODID: return tr("Nexus ID"); case COL_INSTALLTIME: return tr("Installation"); + case COL_NOTES: return tr("Notes"); default: return tr("unknown"); } } @@ -1182,6 +1201,7 @@ QString ModList::getColumnToolTip(int column) ""); case COL_INSTALLTIME: return tr("Time this mod was installed"); + case COL_NOTES: return tr("User notes about the mod"); default: return tr("unknown"); } } diff --git a/src/modlist.h b/src/modlist.h index 8de08da3..42269386 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -65,8 +65,9 @@ public: COL_VERSION, COL_INSTALLTIME, COL_PRIORITY, + COL_NOTES, - COL_LASTCOLUMN = COL_PRIORITY + COL_LASTCOLUMN = COL_NOTES }; typedef boost::signals2::signal SignalModStateChanged; -- cgit v1.3.1 From 9d9dbd9f7e06f4abc3ad57f1574f98210ac65bc9 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sat, 8 Dec 2018 14:46:42 -0600 Subject: Add shortcut to select name filters on mod list and plugin list --- src/mainwindow.cpp | 28 ++++++++++++++++++++++++++++ src/mainwindow.h | 3 +++ 2 files changed, 31 insertions(+) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9a40ce2e..c1e00feb 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -412,6 +412,8 @@ MainWindow::MainWindow(QSettings &initSettings new QShortcut(QKeySequence::Refresh, this, SLOT(refreshProfile_activated())); + new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F), this, SLOT(search_activated())); + new QShortcut(QKeySequence(Qt::Key_Escape), this, SLOT(searchClear_activated())); m_UpdateProblemsTimer.setSingleShot(true); connect(&m_UpdateProblemsTimer, SIGNAL(timeout()), this, SLOT(updateProblemsButton())); @@ -2862,6 +2864,32 @@ void MainWindow::refreshProfile_activated() m_OrganizerCore.profileRefresh(); } +void MainWindow::search_activated() +{ + if (ui->modList->hasFocus() || ui->modFilterEdit->hasFocus()) { + ui->modFilterEdit->setFocus(); + ui->modFilterEdit->setSelection(0, INT_MAX); + } + + if (ui->espList->hasFocus() || ui->espFilterEdit->hasFocus()) { + ui->espFilterEdit->setFocus(); + ui->espFilterEdit->setSelection(0, INT_MAX); + } +} + +void MainWindow::searchClear_activated() +{ + if (ui->modFilterEdit->hasFocus()) { + ui->modFilterEdit->clear(); + ui->modList->setFocus(); + } + + if (ui->espFilterEdit->hasFocus()) { + ui->espFilterEdit->clear(); + ui->espList->setFocus(); + } +} + void MainWindow::information_clicked() { try { diff --git a/src/mainwindow.h b/src/mainwindow.h index 62ffe3de..366765a2 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -577,6 +577,9 @@ private slots: void modlistSelectionsChanged(const QItemSelection ¤t); void esplistSelectionsChanged(const QItemSelection ¤t); + void search_activated(); + void searchClear_activated(); + private slots: // ui slots // actions void on_actionAdd_Profile_triggered(); -- cgit v1.3.1 From f61236e4cf13786ab8606ba5ff33e6d7164cc63e Mon Sep 17 00:00:00 2001 From: Al12rs Date: Mon, 10 Dec 2018 20:53:45 +0100 Subject: Added UI locking while loadbar is active (for refresh of modlist). --- src/mainwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c1e00feb..97b6cd43 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2105,7 +2105,9 @@ void MainWindow::refresher_progress(int percent) if (percent == 100) { m_RefreshProgress->setVisible(false); statusBar()->hide(); + this->setEnabled(true); } else if (!m_RefreshProgress->isVisible()) { + this->setEnabled(false); statusBar()->show(); m_RefreshProgress->setVisible(true); m_RefreshProgress->setRange(0, 100); -- cgit v1.3.1 From f4af3264f049425610d3c829ce1d92482191daf4 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Tue, 11 Dec 2018 00:25:03 -0600 Subject: Sort tools menu to provide consistent results --- src/mainwindow.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 97b6cd43..2551aa2a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -436,7 +436,14 @@ MainWindow::MainWindow(QSettings &initSettings for (const QString &fileName : m_PluginContainer.pluginFileNames()) { installTranslator(QFileInfo(fileName).baseName()); } - for (IPluginTool *toolPlugin : m_PluginContainer.plugins()) { + + std::vector toolPlugins = m_PluginContainer.plugins(); + std::sort(toolPlugins.begin(), toolPlugins.end(), + [](IPluginTool *left, IPluginTool *right){ + return left->displayName().toLower() < right->displayName().toLower(); + } + ); + for (IPluginTool *toolPlugin : toolPlugins) { registerPluginTool(toolPlugin); } -- cgit v1.3.1 From 1266e13921812ac647e1b788fab5eb04e8267ec9 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Tue, 11 Dec 2018 01:52:53 -0600 Subject: Add the ability to change the managed game directory --- src/mainwindow.cpp | 13 +++++++++++-- src/settings.cpp | 11 +++++++++++ src/settings.h | 5 +++++ src/settingsdialog.cpp | 10 ++++++++++ src/settingsdialog.h | 2 ++ src/settingsdialog.ui | 7 +++++++ 6 files changed, 46 insertions(+), 2 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2551aa2a..834a1fce 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4178,11 +4178,22 @@ void MainWindow::on_actionSettings_triggered() QString oldModDirectory(settings.getModDirectory()); QString oldCacheDirectory(settings.getCacheDirectory()); QString oldProfilesDirectory(settings.getProfileDirectory()); + QString oldManagedGameDirectory(settings.getManagedGameDirectory()); bool oldDisplayForeign(settings.displayForeign()); bool proxy = settings.useProxy(); + DownloadManager *dlManager = m_OrganizerCore.downloadManager(); settings.query(&m_PluginContainer, this); + if (oldManagedGameDirectory != settings.getManagedGameDirectory()) { + QMessageBox::about(this, tr("Restarting MO"), + tr("Changing the managed game directory requires restarting MO.\n" + "Any pending downloads will be paused.\n\n" + "Click OK to restart MO now.")); + dlManager->pauseAll(); + qApp->exit(INT_MAX); + } + InstallationManager *instManager = m_OrganizerCore.installationManager(); instManager->setModsDirectory(settings.getModDirectory()); instManager->setDownloadDirectory(settings.getDownloadDirectory()); @@ -4194,8 +4205,6 @@ void MainWindow::on_actionSettings_triggered() refreshProfiles(); } - DownloadManager *dlManager = m_OrganizerCore.downloadManager(); - if (dlManager->getOutputDirectory() != settings.getDownloadDirectory()) { if (dlManager->downloadsInProgress()) { MessageDialog::showMessage(tr("Can't change download directory while " diff --git a/src/settings.cpp b/src/settings.cpp index b067106f..5db656d8 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -298,6 +298,11 @@ QString Settings::getModDirectory(bool resolve) const return getConfigurablePath("mod_directory", ToQString(AppConfig::modsPath()), resolve); } +QString Settings::getManagedGameDirectory() const +{ + return m_Settings.value("gamePath", "").toString(); +} + QString Settings::getProfileDirectory(bool resolve) const { return getConfigurablePath("profiles_directory", ToQString(AppConfig::profilesPath()), resolve); @@ -882,6 +887,12 @@ void Settings::PathsTab::update() } else { m_Settings.remove("Settings/base_directory"); } + + QFileInfo oldGameExe(m_parent->m_GamePlugin->gameDirectory().absoluteFilePath(m_parent->m_GamePlugin->binaryName())); + QFileInfo newGameExe(m_managedGameDirEdit->text()); + if (oldGameExe != newGameExe) { + m_Settings.setValue("gamePath", newGameExe.absolutePath()); + } } Settings::DiagnosticsTab::DiagnosticsTab(Settings *m_parent, SettingsDialog &m_dialog) diff --git a/src/settings.h b/src/settings.h index 2b8186f3..4009d4a3 100644 --- a/src/settings.h +++ b/src/settings.h @@ -160,6 +160,11 @@ public: **/ QString getCacheDirectory(bool resolve = true) const; + /** + * retrieve the directory where the managed game is stored (with native separators) + **/ + QString getManagedGameDirectory() const; + /** * retrieve the directory where profiles stored (with native separators) **/ diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index dec2532c..0d96e63a 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -220,6 +220,16 @@ void SettingsDialog::on_browseOverwriteDirBtn_clicked() } } +void SettingsDialog::on_browseGameDirBtn_clicked() +{ + QFileInfo oldGameExe(ui->managedGameDirEdit->text()); + + QString temp = QFileDialog::getOpenFileName(this, tr("Select game executable"), oldGameExe.absolutePath(), oldGameExe.fileName()); + if (!temp.isEmpty()) { + ui->managedGameDirEdit->setText(temp); + } +} + void SettingsDialog::on_containsBtn_clicked() { QColor result = QColorDialog::getColor(m_ContainsColor, this); diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 95ed288f..f0567d87 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -110,6 +110,8 @@ private slots: void on_browseProfilesDirBtn_clicked(); + void on_browseGameDirBtn_clicked(); + void on_overwritingBtn_clicked(); void on_overwrittenBtn_clicked(); diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 203078a6..80a6a0e7 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -386,6 +386,13 @@ If you use pre-releases, never contact me directly by e-mail or via private mess
    + + + + ... + + +
    -- cgit v1.3.1 From 413c04538359391b1544b329671b81947a2a6c24 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 11 Dec 2018 14:57:03 +0100 Subject: * Fixed order of data tab to be Folders first. * Remember expanded folders across refreshes. --- src/mainwindow.cpp | 95 +++++++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 44 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 834a1fce..935c3612 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1166,7 +1166,52 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const DirectoryEntry &directoryEntry, bool conflictsOnly) { - bool isDirectory = false; + bool isDirectory = true; + + std::wostringstream temp; + temp << directorySoFar << "\\" << directoryEntry.getName(); + { + + std::vector::const_iterator current, end; + directoryEntry.getSubDirectories(current, end); + for (; current != end; ++current) { + QString pathName = ToQString((*current)->getName()); + QStringList columns(pathName); + columns.append(""); + if (!(*current)->isEmpty()) { + QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); + directoryChild->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); + directoryChild->setData(0, Qt::UserRole + 3, isDirectory); + + if (conflictsOnly) { + updateTo(directoryChild, temp.str(), **current, conflictsOnly); + if (directoryChild->childCount() != 0) { + subTree->addChild(directoryChild); + } + else { + delete directoryChild; + } + } + else { + QTreeWidgetItem *onDemandLoad = new QTreeWidgetItem(QStringList()); + onDemandLoad->setData(0, Qt::UserRole + 0, "__loaded_on_demand__"); + onDemandLoad->setData(0, Qt::UserRole + 1, ToQString(temp.str())); + onDemandLoad->setData(0, Qt::UserRole + 2, conflictsOnly); + directoryChild->addChild(onDemandLoad); + subTree->addChild(directoryChild); + } + } + else { + QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); + directoryChild->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); + directoryChild->setData(0, Qt::UserRole + 3, isDirectory); + subTree->addChild(directoryChild); + } + } + } + + + isDirectory = false; { for (const FileEntry::Ptr current : directoryEntry.getFiles()) { if (conflictsOnly && (current->getAlternatives().size() == 0)) { @@ -1230,47 +1275,9 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director } } - std::wostringstream temp; - temp << directorySoFar << "\\" << directoryEntry.getName(); - { - isDirectory = true; - std::vector::const_iterator current, end; - directoryEntry.getSubDirectories(current, end); - for (; current != end; ++current) { - QString pathName = ToQString((*current)->getName()); - QStringList columns(pathName); - columns.append(""); - if (!(*current)->isEmpty()) { - QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - directoryChild->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); - directoryChild->setData(0, Qt::UserRole + 3, isDirectory); - - if (conflictsOnly) { - updateTo(directoryChild, temp.str(), **current, conflictsOnly); - if (directoryChild->childCount() != 0) { - subTree->addChild(directoryChild); - } else { - delete directoryChild; - } - } else { - QTreeWidgetItem *onDemandLoad = new QTreeWidgetItem(QStringList()); - onDemandLoad->setData(0, Qt::UserRole + 0, "__loaded_on_demand__"); - onDemandLoad->setData(0, Qt::UserRole + 1, ToQString(temp.str())); - onDemandLoad->setData(0, Qt::UserRole + 2, conflictsOnly); - directoryChild->addChild(onDemandLoad); - subTree->addChild(directoryChild); - } - } - else { - QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - directoryChild->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); - directoryChild->setData(0, Qt::UserRole + 3, isDirectory); - subTree->addChild(directoryChild); - } - } - } + - subTree->sortChildren(0, Qt::AscendingOrder); + //subTree->sortChildren(0, Qt::AscendingOrder); } void MainWindow::delayedRemove() @@ -1832,7 +1839,7 @@ void MainWindow::on_tabWidget_currentChanged(int index) } else if (index == 1) { m_OrganizerCore.refreshBSAList(); } else if (index == 2) { - refreshDataTree(); + refreshDataTreeKeepExpandedNodes(); } else if (index == 3) { refreshSaveList(); } @@ -2126,7 +2133,7 @@ void MainWindow::directory_refreshed() { // some problem-reports may rely on the virtual directory tree so they need to be updated // now - refreshDataTree(); + refreshDataTreeKeepExpandedNodes(); updateProblemsButton(); statusBar()->hide(); } @@ -4691,7 +4698,7 @@ void MainWindow::on_dataTree_customContextMenuRequested(const QPoint &pos) void MainWindow::on_conflictsCheckBox_toggled(bool) { - refreshDataTree(); + refreshDataTreeKeepExpandedNodes(); } -- cgit v1.3.1 From 0af33ccddf1962ff7ba72edda3d0aef660914a3c Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 11 Dec 2018 16:26:41 +0100 Subject: Added "Open Origin in Explorer" and "Open Origin Info..." options to ESPList context menu. --- src/mainwindow.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/mainwindow.h | 2 ++ 2 files changed, 70 insertions(+) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 935c3612..55ffedc0 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2837,6 +2837,29 @@ void MainWindow::openExplorer_clicked() } } +void MainWindow::openOriginExplorer_clicked() +{ + QItemSelectionModel *selection = ui->espList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 0) { + for (QModelIndex idx : selection->selectedRows()) { + QString fileName = idx.data().toString(); + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + std::vector flags = modInfo->getFlags(); + + ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } + } + else { + QModelIndex idx = selection->currentIndex(); + QString fileName = idx.data().toString(); + + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + std::vector flags = modInfo->getFlags(); + + ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } +} + void MainWindow::openExplorer_activated() { if (ui->modList->hasFocus()) { @@ -3150,6 +3173,38 @@ void MainWindow::on_listOptionsBtn_pressed() m_ContextRow = -1; } +void MainWindow::openOriginInformation_clicked() +{ + try { + QItemSelectionModel *selection = ui->espList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 0) { + for (QModelIndex idx : selection->selectedRows()) { + QString fileName = idx.data().toString(); + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + 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))); + } + } + } + else { + QModelIndex idx = selection->currentIndex(); + QString fileName = idx.data().toString(); + + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + 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))); + } + } + } + catch (const std::exception &e) { + reportError(e.what()); + } +} + void MainWindow::on_espList_doubleClicked(const QModelIndex &index) { if (!index.isValid()) { @@ -5175,6 +5230,19 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Lock load order"), this, SLOT(lockESPIndex())); } + menu.addSeparator(); + menu.addAction(tr("Open Origin in Explorer"), this, SLOT(openOriginExplorer_clicked())); + + QModelIndex idx = ui->espList->selectionModel()->currentIndex(); + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(idx.data().toString()))); + std::vector flags = modInfo->getFlags(); + + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end()) { + menu.addSeparator(); + QAction *infoAction = menu.addAction(tr("Open Origin Info..."), this, SLOT(openOriginInformation_clicked())); + menu.setDefaultAction(infoAction); + } + try { menu.exec(ui->espList->mapToGlobal(pos)); } catch (const std::exception &e) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 366765a2..d5484400 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -425,6 +425,8 @@ private slots: void visitOnNexus_clicked(); void visitWebPage_clicked(); void openExplorer_clicked(); + void openOriginExplorer_clicked(); + void openOriginInformation_clicked(); void information_clicked(); void enableSelectedMods_clicked(); void disableSelectedMods_clicked(); -- cgit v1.3.1 From c486220d20c2cea78beab12317c5493c720304a3 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 11 Dec 2018 16:33:39 +0100 Subject: Updated translations and removed an excessive separator from previous commit. --- src/mainwindow.cpp | 1 - src/organizer_en.ts | 1070 +++++++++++++++++++++++++++------------------------ 2 files changed, 568 insertions(+), 503 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 55ffedc0..b06eb44d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5238,7 +5238,6 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) std::vector flags = modInfo->getFlags(); if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end()) { - menu.addSeparator(); QAction *infoAction = menu.addAction(tr("Open Origin Info..."), this, SLOT(openOriginInformation_clicked())); menu.setDefaultAction(infoAction); } diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 1e699dfe..e3d6729c 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -1620,7 +1620,7 @@ p, li { white-space: pre-wrap; } - + Create Backup @@ -1784,8 +1784,8 @@ p, li { white-space: pre-wrap; } - - + + Refresh @@ -1969,7 +1969,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1980,7 +1980,7 @@ p, li { white-space: pre-wrap; } - + No Problems @@ -2010,7 +2010,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -2030,793 +2030,793 @@ Right now this has very limited functionality - + Toolbar - + Desktop - + Start Menu - + There is no supported sort mechanism for this game. You will probably have to use a third-party tool. - + Problems - + There are potential problems with your setup - + Everything seems to be in order - + Help on UI - + Documentation Wiki - + Report Issue - + Tutorials - + About - + About Qt - + Name - + Please enter a name for the new profile - + failed to create profile: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress - + There are still downloads in progress, do you really want to quit? - + Plugin "%1" failed: %2 - + Plugin "%1" failed - + Browse Mod Page - + Also in: <br> - + No conflict - + <Edit...> - + This bsa is enabled in the ini file so it may be required! - + Activating Network Proxy - + Notice: Your current MO version (%1) is lower than the previous version (%2).<br>The GUI may not downgrade gracefully, so you may experience oddities.<br>However, there should be no serious issues. - + Choose Mod - + Mod Archive - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - + failed to spawn notepad.exe: %1 - + failed to change origin name: %1 - + failed to move "%1" from mod "%2" to "%3": %4 - + <Contains %1> - + <Checked> - + <Unchecked> - + <Update> - + <Mod Backup> - + <Managed by MO> - + <Managed outside MO> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - - + + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - + Failed to create backup. - + You need to be logged in with Nexus to resume a download - - + + You need to be logged in with Nexus to endorse - + Failed to display overwrite dialog: %1 - + Nexus ID for this Mod is unknown - + Web page for this mod is unknown - - - + + + Create Mod... - + This will create an empty mod. Please enter a name: - - + + A mod with this name already exists - + Create Separator... - + This will create a new separator. Please enter a name: - + A separator with this name already exists - + This will move all files from overwrite into a new, regular mod. Please enter a name: - - + + Are you sure? - + About to recursively delete: - + Not logged in, endorsement information will be wrong - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Export to csv - + CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet. You can also use online editors and converters instead. - + Select what mods you want export: - + All installed mods - + Only active (checked) mods from your current profile - + All currently visible mods in the mod list - + Choose what Columns to export: - + Mod_Priority - + Mod_Name - + Mod_Status - + Primary_Category - + Nexus_ID - + Mod_Nexus_URL - + Mod_Version - + Install_Date - + Download_File_Name - + export failed: %1 - + Open Game folder - + Open MyGames folder - + Open Instance folder - + Open Mods folder - + Open Profile folder - + Open Downloads folder - + Open MO2 Install folder - + Open MO2 Plugins folder - + Open MO2 Logs folder - + Install Mod... - + Create empty mod - + Create Separator - + Enable all visible - + Disable all visible - + Check all for update - + Export to csv... - - + + Send to - - + + Top - - + + Bottom - - + + Priority... - + Separator... - + All Mods - + Sync to Mods... - + Clear Overwrite... - - + + Open in Explorer - + Restore Backup - + Remove Backup... - - + + Change Categories - - + + Primary Category - + Rename Separator... - + Remove Separator... - + Select Color... - + Reset Color - + Change versioning scheme - + Un-ignore update - + Ignore update - - + + Enable selected - - + + Disable selected - + Rename Mod... - + Reinstall Mod - + Remove Mod... - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Mark as converted/working - + Visit on Nexus - + Visit web page - + Information... - - + + Exception: - - + + Unknown exception - + <All> - + <Multiple> - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -2824,12 +2824,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -2837,335 +2837,358 @@ You can also use online editors and converters instead. - + failed to remove %1 - + failed to create %1 - + + Restarting MO + + + + + Changing the managed game directory requires restarting MO. +Any pending downloads will be paused. + +Click OK to restart MO now. + + + + Can't change download directory while downloads are in progress! - + failed to write to file %1 - + %1 written - + Select binary - + Binary - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - - + + Set Priority - + Set the priority of the selected plugins - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Thank you! - + Thank you for your endorsement! - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - - + + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Extract... - + This will restart MO, continue? - + Edit Categories... - + Deselect filter - + Remove - + Enable all - + Disable all - + Unlock load order - + Lock load order - + + Open Origin in Explorer + + + + + Open Origin Info... + + + + depends on missing "%1" - + incompatible with "%1" - + Please wait while LOOT is running - + loot failed. Exit code was: %1 - + failed to start loot - + failed to run loot: %1 - + Errors occured - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of modlist created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file - + Set the priority of the selected mods @@ -3542,22 +3565,36 @@ p, li { white-space: pre-wrap; } - + + + + Enter comments about the mod here. These are displayed in the notes column of the mod list. + + + + + + + Enter notes about the mod here. These can be viewed in the mod list by hovering over the notes column or the flags column. + + + + Filetree - + Open Mod in Explorer - + A directory view of this mod - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -3567,288 +3604,288 @@ p, li { white-space: pre-wrap; } - + Previous - + Next - + Close - + &Delete - + &Rename - + &Hide - + &Unhide - + &Open - + &New Folder - - + + Save changes? - - + + Save changes to "%1"? - + File Exists - + A file with that name exists, please enter a new one - + failed to move file - + failed to create directory "optional" - - + + Info requested, please wait - + Main - + Update - + Optional - + Old - + Misc - + Unknown - + Current Version: %1 - + No update available - + (description incomplete, please visit nexus) - + <a href="%1">Visit on Nexus</a> - + Failed to delete %1 - - - - + + + + Confirm - - + + Are sure you want to delete "%1"? - - + + Are sure you want to delete the selected files? - - + + New Folder - + Failed to create "%1" - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - - + + failed to rename %1 to %2 - + There already is a visible version of this file. Replace it? - + Select binary - + Binary - + file not found: %1 - + failed to generate preview for %1 - + Sorry - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Un-Hide - + Hide - - + + Open/Execute - - + + Preview - + Name - + Please enter a name - - + + Error - + Invalid name. Must be a valid file name - + A tweak by that name exists - + Create Tweak @@ -3887,18 +3924,18 @@ p, li { white-space: pre-wrap; } ModInfoRegular - - + + failed to write %1/meta.ini: error %2 - + %1 contains no esp/esm/esl and no asset (textures, meshes, interface, ...) directory - + Categories: <br> @@ -3999,176 +4036,186 @@ p, li { white-space: pre-wrap; } - + Overwrites files - + Overwritten files - + Overwrites & Overwritten - + Redundant - + Alternate game source - + Non-MO - + invalid - + installed version: "%1", newest version: "%2" - + The newest version on Nexus seems to be older than the one you have installed. This could either mean the version you have has been withdrawn (i.e. due to a bug) or the author uses a non-standard versioning scheme and that newest version is actually newer. Either way you may want to "upgrade". - + Categories: <br> - + Invalid name - + Name is already in use by another mod - + drag&drop failed: %1 - + Confirm - + Are you sure you want to remove "%1"? - + Flags - + Content - + Mod Name - + Version - + Priority - + Category - + Source Game - + Nexus ID - + Installation - - + + Notes + + + + + unknown - + Name of your mods - + Version of the mod (if available) - + Installation priority of your mod. The higher, the more "important" it is and thus overwrites files from mods with lower priority. - + Category of the mod. - + The source game which was the origin of this mod. - + Id of the mod as used on Nexus. - + Emblemes to highlight things that might require attention. - + Depicts the content of the mod:<br><table cellspacing=7><tr><td><img src=":/MO/gui/content/plugin" width=32/></td><td>Game plugins (esp/esm/esl)</td></tr><tr><td><img src=":/MO/gui/content/interface" width=32/></td><td>Interface</td></tr><tr><td><img src=":/MO/gui/content/mesh" width=32/></td><td>Meshes</td></tr><tr><td><img src=":/MO/gui/content/bsa" width=32/></td><td>BSA</td></tr><tr><td><img src=":/MO/gui/content/texture" width=32/></td><td>Textures</td></tr><tr><td><img src=":/MO/gui/content/sound" width=32/></td><td>Sounds</td></tr><tr><td><img src=":/MO/gui/content/music" width=32/></td><td>Music</td></tr><tr><td><img src=":/MO/gui/content/string" width=32/></td><td>Strings</td></tr><tr><td><img src=":/MO/gui/content/script" width=32/></td><td>Scripts (Papyrus)</td></tr><tr><td><img src=":/MO/gui/content/skse" width=32/></td><td>Script Extender plugins</td></tr><tr><td><img src=":/MO/gui/content/skyproc" width=32/></td><td>SkyProc Patcher</td></tr><tr><td><img src=":/MO/gui/content/menu" width=32/></td><td>Mod Configuration Menu</td></tr><tr><td><img src=":/MO/gui/content/inifile" width=32/></td><td>INI files</td></tr><tr><td><img src=":/MO/gui/content/modgroup" width=32/></td><td>ModGroup files</td></tr></table> - + Time this mod was installed + + + User notes about the mod + + ModListSortProxy @@ -4388,86 +4435,86 @@ Continue launching %1? - + Failed to refresh list of esps: %1 - + Multiple esps/esls activated, please check that they don't conflict. - + Download? - + A download has been started but no installed page plugin recognizes it. If you download anyway no information (i.e. version) will be associated with the download. Continue? - + failed to update mod list: %1 - - + + login successful - + Login failed - + Login failed, try again? - + login failed: %1. Download will not be associated with an account - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + MO1 "Script Extender" load mechanism has left hook.dll in your game folder - - + + Description missing - + <a href="%1">hook.dll</a> has been found in your game folder (right click to copy the full path). This is most likely a leftover of setting the ModOrganizer 1 load mechanism to "Script Extender", in which case you must remove this file either by changing the load mechanism in ModOrganizer 1 or manually removing the file, otherwise the game is likely to crash and burn. - + failed to save load order: %1 - + The designated write target "%1" is not enabled. @@ -5249,88 +5296,90 @@ If the folder was still in use, restart MO and try again. - Enter a new name or select one from the suggested list: + Enter a new name or select one from the suggested list: +(This is just the name of the Instance, not the game selection) + Enter a new name or select one from the suggested list: - - + + Canceled - + Invalid instance name - + The instance name "%1" is invalid. Use the name "%2" instead? - + The instance "%1" already exists. - + Please choose a different instance name, like: "%1 1" . - + Choose Instance - + Each Instance is a full set of MO data files (mods, downloads, profiles, configuration, ...). You can use multiple instances for different games. Instances are stored in Appdata and can be accessed by all MO installations. If your MO folder is writable, you can also store a single instance locally (called a Portable install, and all the MO data files will be inside the installation folder). - + New - + Create a new instance. - + Portable - + Use MO folder for data. - + Manage Instances - + Delete an Instance. - + failed to create %1 - + Data directory created - + New data directory created at %1. If you don't want to store a lot of data there, reconfigure the storage directories via settings. @@ -5442,7 +5491,7 @@ If the folder was still in use, restart MO and try again. - + Mod Organizer @@ -5457,18 +5506,18 @@ If the folder was still in use, restart MO and try again. - + Please use "Help" from the toolbar to get usage instructions to all elements - - + + <Manage...> - + failed to parse profile %1: %2 @@ -5519,12 +5568,12 @@ If the folder was still in use, restart MO and try again. - + Script Extender - + Proxy DLL @@ -5738,28 +5787,28 @@ Select Show Details option to see the full change-log. - - + + attempt to store setting for unknown plugin "%1" - + Error - + Failed to create "%1", you may not have the necessary permission. path remains unchanged. - + Restart Mod Organizer? - + In order to reset the window geometries, MO must be restarted. Restart it now? @@ -5920,6 +5969,7 @@ If you use pre-releases, never contact me directly by e-mail or via private mess + ... @@ -5980,23 +6030,23 @@ If you use pre-releases, never contact me directly by e-mail or via private mess - + Important: All directories have to be writeable! - - + + Nexus - + Allows automatic log-in when the Nexus-Page for the game is clicked. - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -6005,144 +6055,144 @@ p, li { white-space: pre-wrap; } - + If checked and if correct credentials are entered below, log-in to Nexus (for browsing and downloading) is automatic. - + Automatically Log-In to Nexus - - + + Username - - + + Password - + Remove cache and cookies. Forces a new login. - + Clear Cache - + Disable automatic internet features - + Disable automatic internet features. This does not affect features that are explicitly invoked by the user (like checking mods for updates, endorsing, opening the web browser) - + Offline Mode - + Use a proxy for network connections. - + Use a proxy for network connections. This uses the system-wide settings which can be configured in Internet Explorer. Please note that MO will start up a few seconds slower on some systems when using a proxy. - + Use HTTP Proxy (Uses System Settings) - + Associate with "Download with manager" links - + Known Servers (updated on download) - + Preferred Servers (Drag & Drop) - + Steam - + If you save your steam user ID and password here, they will be used when logging into steam. Note, however, your password will be stored unencrypted, so make sure your computer is secure. - + Plugins - + Author: - + Version: - + Description: - + Key - + Value - + Blacklisted Plugins (use <del> to remove): - + Workarounds - + Steam App ID - + The Steam AppID for your game - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -6158,17 +6208,17 @@ p, li { white-space: pre-wrap; } - + Load Mechanism - + Select loading mechanism. See help for details. - + Mod Organizer needs a dll to be injected into the game so all mods are visible to it. There are several means to do this: *Mod Organizer* (default) In this mode the Mod Organizer itself injects the dll. The disadvantage is that you always have to start the game through MO or a link created by it. @@ -6179,17 +6229,17 @@ If you use the Steam version of Oblivion the default will NOT work. In this case - + NMM Version - + The Version of Nexus Mod Manager to impersonate. - + Mod Organizer uses an API provided by the Nexus to provide features like checking for updates and downloading files. Unfortunately this API has not been made available officially to third party tools like MO so we have to impersonate the Nexus Mod Manager to be allowed in. On top of this Nexus has used the client identification to lock out outdated versions of NMM to force users to update. This means that MO also needs to impersonate the new version of NMM even if MO doesn't need an update. Therefore you can configure the version to identify as here. Please note that MO does identify itself as MO to the webserver, it's not lying about what it is. It is merely adding a "compatible" NMM version to the user agent. @@ -6198,44 +6248,44 @@ tl;dr-version: If Nexus-features don't work, insert the current version num - + Enforces that inactive ESPs and ESMs are never loaded. - + It seems that the Games occasionally load ESP or ESM files even if they haven't been activated as plugins. I don't yet know what the circumstances are, but user reports imply it is in some cases unwanted. If this is checked, ESPs and ESMs not checked in the List are invisible to the game and can not be loaded. - + Hide inactive ESPs/ESMs - + If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) - + If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) Uncheck this if you want to use Mod Organizer with total conversions (like Nehrim) but be aware that the game will crash if required files are not enabled. - + Force-enable game files - + Disable this to no longer display mods installed outside MO in the mod list (left pane). Assets from those mods will then be treated as having lowest mod priority together with the original game content. - + By default Mod Organizer will display esp+bsa bundles installed with foreign tools as mods (left pane). This allows you to control their priority in relation to other mods. This is particularly useful if you also use Steam Workshop to install mods. However, if you installed loose file mods outside MO which conflict with BSAs also installed outside MO those conflicts can't be resolved correctly. @@ -6243,24 +6293,35 @@ If you disable this feature, MO will only display official DLCs this way. Please - + Display mods installed outside MO - - + + + Disable this to prevent the GUI from being locked when running an executable. This may result in abnormal behavior. + + + + + Lock GUI when running executable + + + + + For Skyrim, this can be used instead of Archive Invalidation. It should make AI redundant for all Profiles. For the other games this is not a sufficient replacement for AI! - + Back-date BSAs - + Add executables to the blacklist to prevent them from accessing the virtual file system. This is useful to prevent unintended programs from being hooked. Hooking unintended @@ -6269,48 +6330,48 @@ programs you are intentionally running. - + Add executables to the blacklist to prevent them from accessing the virtual file system. This is useful to prevent unintended programs from being hooked. Hooking unintended programs may affect the execution of these programs or the programs you are intentionally running. - + Configure Executables Blacklist - - + + Resets the window geometries for all windows. This can be useful if a window becomes too small or too large, if a column becomes too thin or too wide, and in similar situations. - + Reset Window Geometries - + These are workarounds for problems with Mod Organizer. Please make sure you read the help text before changing anything here. - + Diagnostics - + Log Level - + Decides the amount of data printed to "ModOrganizer.log" - + Decides the amount of data printed to "ModOrganizer.log". "Debug" produces very useful information for finding problems. There is usually no noteworthy performance impact but the file may become rather large. If this is a problem you may prefer the "Info" level for regluar use. On the "Error" level the log file usually remains empty. @@ -6318,37 +6379,37 @@ programs you are intentionally running. - + Debug - + Info (recommended) - + Warning - + Error - + Crash Dumps - + Decides which type of crash dumps are collected when injected processes crash. - + Decides which type of crash dumps are collected when injected processes crash. "None" Disables the generation of crash dumps by MO. @@ -6359,37 +6420,37 @@ programs you are intentionally running. - + None - + Mini (recommended) - + Data - + Full - + Max Dumps To Keep - + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. - + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. Set "Crash Dumps" above to None to disable crash dump collection. @@ -6397,12 +6458,12 @@ programs you are intentionally running. - + Hint: right click link and copy link location - + Logs and crash dumps are stored under your current instance in the <a href="LOGS_FULL_PATH">LOGS_DIR</a> and <a href="DUMPS_FULL_PATH">DUMPS_DIR</a> folders. @@ -6468,12 +6529,17 @@ Example: - + + Select game executable + + + + Confirm? - + This will make all dialogs show up again where you checked the "Remember selection"-box. Continue? -- cgit v1.3.1 From dac716bb86a9e4ff514fe7d939f33b397dc120e5 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Tue, 11 Dec 2018 14:15:37 -0600 Subject: Allow tool plugins to define a submenu with the display name Setting the display name to "/" will group plugins under the submenu. --- src/iuserinterface.h | 5 +++- src/mainwindow.cpp | 66 ++++++++++++++++++++++++++++++++++++++-------------- src/mainwindow.h | 3 ++- 3 files changed, 55 insertions(+), 19 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/iuserinterface.h b/src/iuserinterface.h index 7ce71c24..034fa029 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -8,6 +8,8 @@ #include #include +#include + class QSettings; class IUserInterface @@ -16,7 +18,8 @@ public: virtual void storeSettings(QSettings &settings) = 0; - virtual void registerPluginTool(MOBase::IPluginTool *tool) = 0; + virtual void registerPluginTool(MOBase::IPluginTool *tool, QString name = QString(), QMenu *menu = nullptr) = 0; + virtual void registerPluginTools(std::vector toolPlugins) = 0; virtual void registerModPage(MOBase::IPluginModPage *modPage) = 0; virtual void installTranslator(const QString &name) = 0; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b06eb44d..61a745b2 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -267,7 +267,7 @@ MainWindow::MainWindow(QSettings &initSettings // insert spacers ui->toolBar->insertWidget(action, spacer); m_Sep = action; - // m_Sep would only use the last seperator anyway, and we only have the one anyway? + // m_Sep would only use the last separator anyway, and we only have the one anyway? break; } } @@ -437,15 +437,7 @@ MainWindow::MainWindow(QSettings &initSettings installTranslator(QFileInfo(fileName).baseName()); } - std::vector toolPlugins = m_PluginContainer.plugins(); - std::sort(toolPlugins.begin(), toolPlugins.end(), - [](IPluginTool *left, IPluginTool *right){ - return left->displayName().toLower() < right->displayName().toLower(); - } - ); - for (IPluginTool *toolPlugin : toolPlugins) { - registerPluginTool(toolPlugin); - } + registerPluginTools(m_PluginContainer.plugins()); for (IPluginModPage *modPagePlugin : m_PluginContainer.plugins()) { registerModPage(modPagePlugin); @@ -1046,15 +1038,57 @@ void MainWindow::modPagePluginInvoke() } } -void MainWindow::registerPluginTool(IPluginTool *tool) +void MainWindow::registerPluginTool(IPluginTool *tool, QString name, QMenu *menu) { - QAction *action = new QAction(tool->icon(), tool->displayName(), ui->toolBar); + if (name.isEmpty()) + name = tool->displayName(); + + QAction *action = new QAction(tool->icon(), name, ui->toolBar); action->setToolTip(tool->tooltip()); tool->setParentWidget(this); action->setData(qVariantFromValue((QObject*)tool)); connect(action, SIGNAL(triggered()), this, SLOT(toolPluginInvoke()), Qt::QueuedConnection); - QToolButton *toolBtn = qobject_cast(ui->toolBar->widgetForAction(ui->actionTool)); - toolBtn->menu()->addAction(action); + + if (menu == nullptr) { + QToolButton *toolBtn = qobject_cast(ui->toolBar->widgetForAction(ui->actionTool)); + toolBtn->menu()->addAction(action); + } else { + menu->addAction(action); + } +} + +void MainWindow::registerPluginTools(std::vector toolPlugins) +{ + // Sort the plugins by display name + std::sort(toolPlugins.begin(), toolPlugins.end(), + [](IPluginTool *left, IPluginTool *right) { + return left->displayName().toLower() < right->displayName().toLower(); + } + ); + + // Group the plugins into submenus + QMap>> submenuMap; + for (auto toolPlugin : toolPlugins) { + QStringList toolName = toolPlugin->displayName().split("/"); + QString submenu = toolName[0]; + toolName.pop_front(); + submenuMap[submenu].append(QPair(toolName.join("/"), toolPlugin)); + } + + // Start registering plugins + for (auto submenuKey : submenuMap.keys()) { + if (submenuMap[submenuKey].length() > 1) { + QMenu *submenu = new QMenu(submenuKey, this); + for (auto info : submenuMap[submenuKey]) { + registerPluginTool(info.second, info.first, submenu); + } + QToolButton *toolBtn = qobject_cast(ui->toolBar->widgetForAction(ui->actionTool)); + toolBtn->menu()->addMenu(submenu); + } + else { + registerPluginTool(submenuMap[submenuKey].front().second); + } + } } void MainWindow::registerModPage(IPluginModPage *modPage) @@ -1171,7 +1205,6 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director std::wostringstream temp; temp << directorySoFar << "\\" << directoryEntry.getName(); { - std::vector::const_iterator current, end; directoryEntry.getSubDirectories(current, end); for (; current != end; ++current) { @@ -1275,7 +1308,6 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director } } - //subTree->sortChildren(0, Qt::AscendingOrder); } @@ -2845,7 +2877,7 @@ void MainWindow::openOriginExplorer_clicked() QString fileName = idx.data().toString(); ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); std::vector flags = modInfo->getFlags(); - + ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index d5484400..69d08337 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -132,7 +132,8 @@ public: void saveArchiveList(); - void registerPluginTool(MOBase::IPluginTool *tool); + void registerPluginTool(MOBase::IPluginTool *tool, QString name = QString(), QMenu *menu = nullptr); + void registerPluginTools(std::vector toolPlugins); void registerModPage(MOBase::IPluginModPage *modPage); void addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info); -- cgit v1.3.1 From 013890f644c264d7da0d7da1adcd3f289ebee377 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Wed, 12 Dec 2018 03:02:29 -0600 Subject: Remember the last separator color used --- src/mainwindow.cpp | 19 ++- src/organizer_en.ts | 435 ++++++++++++++++++++++++++-------------------------- 2 files changed, 234 insertions(+), 220 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 61a745b2..18ac60bf 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3049,10 +3049,21 @@ void MainWindow::createSeparator_clicked() void MainWindow::setColor_clicked() { + QSettings &settings = m_OrganizerCore.settings().directInterface(); ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); - QColor color = QColorDialog::getColor(modInfo->getColor(), this); - if (!color.isValid()) + QColorDialog dialog(this); + QColor currentColor = modInfo->getColor(); + QColor previousColor = settings.value("previousSeparatorColor", QColor()).value(); + if (currentColor.isValid()) + dialog.setCurrentColor(currentColor); + else + dialog.setCurrentColor(previousColor); + if (!dialog.exec()) + return; + currentColor = dialog.currentColor(); + if (!currentColor.isValid()) return; + settings.setValue("previousSeparatorColor", currentColor); QItemSelectionModel *selection = ui->modList->selectionModel(); if (selection->hasSelection() && selection->selectedRows().count() > 1) { for (QModelIndex idx : selection->selectedRows()) { @@ -3060,12 +3071,12 @@ void MainWindow::setColor_clicked() auto flags = info->getFlags(); if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()) { - info->setColor(color); + info->setColor(currentColor); } } } else { - modInfo->setColor(color); + modInfo->setColor(currentColor); } } diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 6700dea8..44a386d6 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -1620,7 +1620,7 @@ p, li { white-space: pre-wrap; } - + Create Backup @@ -1784,8 +1784,8 @@ p, li { white-space: pre-wrap; } - - + + Refresh @@ -1969,7 +1969,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -2010,7 +2010,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -2281,16 +2281,16 @@ Right now this has very limited functionality - - + + failed to rename "%1" to "%2" - - - + + + Confirm @@ -2354,8 +2354,8 @@ Right now this has very limited functionality - - + + Create Mod... @@ -2367,7 +2367,7 @@ Please enter a name: - + A mod with this name already exists @@ -2388,435 +2388,435 @@ Please enter a name: - + This will move all files from overwrite into a new, regular mod. Please enter a name: - - + + Are you sure? - + About to recursively delete: - + Not logged in, endorsement information will be wrong - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Export to csv - + CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet. You can also use online editors and converters instead. - + Select what mods you want export: - + All installed mods - + Only active (checked) mods from your current profile - + All currently visible mods in the mod list - + Choose what Columns to export: - + Mod_Priority - + Mod_Name - + Mod_Status - + Primary_Category - + Nexus_ID - + Mod_Nexus_URL - + Mod_Version - + Install_Date - + Download_File_Name - + export failed: %1 - + Open Game folder - + Open MyGames folder - + Open Instance folder - + Open Mods folder - + Open Profile folder - + Open Downloads folder - + Open MO2 Install folder - + Open MO2 Plugins folder - + Open MO2 Logs folder - + Install Mod... - + Create empty mod - + Create Separator - + Enable all visible - + Disable all visible - + Check all for update - + Export to csv... - - + + Send to - - + + Top - - + + Bottom - - + + Priority... - + Separator... - + All Mods - + Sync to Mods... - + Clear Overwrite... - - + + Open in Explorer - + Restore Backup - + Remove Backup... - - + + Change Categories - - + + Primary Category - + Rename Separator... - + Remove Separator... - + Select Color... - + Reset Color - + Change versioning scheme - + Un-ignore update - + Ignore update - - + + Enable selected - - + + Disable selected - + Rename Mod... - + Reinstall Mod - + Remove Mod... - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Mark as converted/working - + Visit on Nexus - + Visit web page - + Information... - - + + Exception: - - + + Unknown exception - + <All> - + <Multiple> - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -2824,12 +2824,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -2837,22 +2837,22 @@ You can also use online editors and converters instead. - + failed to remove %1 - + failed to create %1 - + Restarting MO - + Changing the managed game directory requires restarting MO. Any pending downloads will be paused. @@ -2860,335 +2860,335 @@ Click OK to restart MO now. - + Can't change download directory while downloads are in progress! - + failed to write to file %1 - + %1 written - + Select binary - + Binary - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - - + + Set Priority - + Set the priority of the selected plugins - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Thank you! - + Thank you for your endorsement! - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - - + + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Extract... - + This will restart MO, continue? - + Edit Categories... - + Deselect filter - + Remove - + Enable all - + Disable all - + Unlock load order - + Lock load order - + Open Origin in Explorer - + Open Origin Info... - + depends on missing "%1" - + incompatible with "%1" - + Please wait while LOOT is running - + loot failed. Exit code was: %1 - + failed to start loot - + failed to run loot: %1 - + Errors occured - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of modlist created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file - + Set the priority of the selected mods @@ -5298,7 +5298,6 @@ If the folder was still in use, restart MO and try again. Enter a new name or select one from the suggested list: (This is just the name of the Instance, not the game selection) - Enter a new name or select one from the suggested list: @@ -5472,7 +5471,6 @@ If the folder was still in use, restart MO and try again. No game identified in "%1". The directory is required to contain the game binary. - No game identified in "%1". The directory is required to contain the game binary and its launcher. @@ -5513,7 +5511,7 @@ If the folder was still in use, restart MO and try again. - + <Manage...> @@ -5891,23 +5889,23 @@ If you use pre-releases, never contact me directly by e-mail or via private mess - - Mod Contains selected Plugin + + Reset Colors - - Plugin is Contained in selected Mod + + Is overwritten (loose files) - - Reset Colors + + Mod Contains selected Plugin - - Is overwritten (loose files) + + Plugin is Contained in selected Mod @@ -5921,6 +5919,11 @@ If you use pre-releases, never contact me directly by e-mail or via private mess When this is enabled, the color defined for a separator will be shown on the mod list scrollbar at the location of the separator. This can be useful for quick navigation between separator sections or to a specific separator section. + + + Show mod list separator colors on the scrollbar + + @@ -6254,22 +6257,6 @@ Please note that MO does identify itself as MO to the webserver, it's not l tl;dr-version: If Nexus-features don't work, insert the current version number of NMM here and try again. - - - Enforces that inactive ESPs and ESMs are never loaded. - - - - - It seems that the Games occasionally load ESP or ESM files even if they haven't been activated as plugins. -I don't yet know what the circumstances are, but user reports imply it is in some cases unwanted. If this is checked, ESPs and ESMs not checked in the List are invisible to the game and can not be loaded. - - - - - Hide inactive ESPs/ESMs - - If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) @@ -6304,6 +6291,22 @@ If you disable this feature, MO will only display official DLCs this way. Please Display mods installed outside MO + + + Enforces that inactive ESPs and ESMs are never loaded. + + + + + It seems that the Games occasionally load ESP or ESM files even if they haven't been activated as plugins. +I don't yet know what the circumstances are, but user reports imply it is in some cases unwanted. If this is checked, ESPs and ESMs not checked in the List are invisible to the game and can not be loaded. + + + + + Hide inactive ESPs/ESMs + + -- cgit v1.3.1 From 346bc630f8986e52101d51ac93e70154915c450a Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 12 Dec 2018 10:37:10 +0100 Subject: Create new Separators in last used color. --- src/mainwindow.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 18ac60bf..795f82cf 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3045,6 +3045,12 @@ void MainWindow::createSeparator_clicked() { m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority); } + QSettings &settings = m_OrganizerCore.settings().directInterface(); + QColor previousColor = settings.value("previousSeparatorColor", QColor()).value(); + if (previousColor.isValid()) { + ModInfo::getByIndex(ModInfo::getIndex(name))->setColor(previousColor); + } + } void MainWindow::setColor_clicked() -- cgit v1.3.1 From 25a9290cd57839a151d27ed7e454963a2ed833d9 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Wed, 12 Dec 2018 09:39:55 -0600 Subject: Reset previousSeparatorColor when a separator color is reset --- src/mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 795f82cf..931fa2af 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3104,6 +3104,7 @@ void MainWindow::resetColor_clicked() else { modInfo->setColor(color); } + Settings::instance().directInterface().remove("previousSeparatorColor"); } void MainWindow::createModFromOverwrite() -- cgit v1.3.1 From e15f768a1538e558f95914d7786635eaf58847ad Mon Sep 17 00:00:00 2001 From: Al12rs Date: Thu, 13 Dec 2018 21:18:35 +0100 Subject: Fixed opening multiple modinfodialog from Open Origin info. --- src/mainwindow.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 931fa2af..9f9dc0fb 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3227,7 +3227,9 @@ void MainWindow::openOriginInformation_clicked() { try { QItemSelectionModel *selection = ui->espList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 0) { + //we don't want to open multiple modinfodialogs. + /*if (selection->hasSelection() && selection->selectedRows().count() > 0) { + for (QModelIndex idx : selection->selectedRows()) { QString fileName = idx.data().toString(); ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); @@ -3238,16 +3240,15 @@ void MainWindow::openOriginInformation_clicked() } } } - else { - QModelIndex idx = selection->currentIndex(); - QString fileName = idx.data().toString(); + else {}*/ + QModelIndex idx = selection->currentIndex(); + QString fileName = idx.data().toString(); - ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); - std::vector flags = modInfo->getFlags(); + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + 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))); - } + if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { + displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); } } catch (const std::exception &e) { -- cgit v1.3.1 From ff51aa10c27ce09ddacccc84e09105f333a36abc Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 18 Dec 2018 00:10:37 +0100 Subject: Fixed performance issue of Data tab. --- src/mainwindow.cpp | 26 +++++++++++++++++--------- src/mainwindow.h | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9f9dc0fb..3e35ebec 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1198,9 +1198,11 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) } } -void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const DirectoryEntry &directoryEntry, bool conflictsOnly) +void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const DirectoryEntry &directoryEntry, bool conflictsOnly, QIcon *fileIcon, QIcon *folderIcon) { bool isDirectory = true; + //QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); + //QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); std::wostringstream temp; temp << directorySoFar << "\\" << directoryEntry.getName(); @@ -1213,11 +1215,11 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director columns.append(""); if (!(*current)->isEmpty()) { QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - directoryChild->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); + directoryChild->setData(0, Qt::DecorationRole, *folderIcon); directoryChild->setData(0, Qt::UserRole + 3, isDirectory); if (conflictsOnly) { - updateTo(directoryChild, temp.str(), **current, conflictsOnly); + updateTo(directoryChild, temp.str(), **current, conflictsOnly, fileIcon, folderIcon); if (directoryChild->childCount() != 0) { subTree->addChild(directoryChild); } @@ -1236,7 +1238,7 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director } else { QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns); - directoryChild->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); + directoryChild->setData(0, Qt::DecorationRole, *folderIcon); directoryChild->setData(0, Qt::UserRole + 3, isDirectory); subTree->addChild(directoryChild); } @@ -1281,7 +1283,7 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director fileChild->setFont(1, font); } fileChild->setData(0, Qt::UserRole, ToQString(current->getFullPath())); - fileChild->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::File)); + fileChild->setData(0, Qt::DecorationRole, *fileIcon); fileChild->setData(0, Qt::UserRole + 3, isDirectory); fileChild->setData(0, Qt::UserRole + 1, isArchive); fileChild->setData(1, Qt::UserRole, source); @@ -1322,6 +1324,7 @@ void MainWindow::delayedRemove() void MainWindow::expandDataTreeItem(QTreeWidgetItem *item) { + if ((item->childCount() == 1) && (item->child(0)->data(0, Qt::UserRole).toString() == "__loaded_on_demand__")) { // read the data we need from the sub-item, then dispose of it QTreeWidgetItem *onDemandDataItem = item->child(0); @@ -1331,7 +1334,9 @@ void MainWindow::expandDataTreeItem(QTreeWidgetItem *item) std::wstring virtualPath = (path + L"\\").substr(6) + ToWString(item->text(0)); DirectoryEntry *dir = m_OrganizerCore.directoryStructure()->findSubDirectoryRecursive(virtualPath); if (dir != nullptr) { - updateTo(item, path, *dir, conflictsOnly); + QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); + QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); + updateTo(item, path, *dir, conflictsOnly, &fileIcon, &folderIcon); } else { qWarning("failed to update view of %ls", path.c_str()); } @@ -1406,12 +1411,14 @@ void MainWindow::refreshDataTree() { QCheckBox *conflictsBox = findChild("conflictsCheckBox"); QTreeWidget *tree = findChild("dataTree"); + QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); + QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); tree->clear(); QStringList columns("data"); columns.append(""); QTreeWidgetItem *subTree = new QTreeWidgetItem(columns); subTree->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); - updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked()); + updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked(), &fileIcon, &folderIcon); tree->insertTopLevelItem(0, subTree); subTree->setExpanded(true); } @@ -1420,7 +1427,8 @@ void MainWindow::refreshDataTreeKeepExpandedNodes() { QCheckBox *conflictsBox = findChild("conflictsCheckBox"); QTreeWidget *tree = findChild("dataTree"); - + QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder); + QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File); QStringList expandedNodes; QTreeWidgetItemIterator it1(tree, QTreeWidgetItemIterator::NotHidden | QTreeWidgetItemIterator::HasChildren); while (*it1) { @@ -1436,7 +1444,7 @@ void MainWindow::refreshDataTreeKeepExpandedNodes() columns.append(""); QTreeWidgetItem *subTree = new QTreeWidgetItem(columns); subTree->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder)); - updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked()); + updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked(), &fileIcon, &folderIcon); tree->insertTopLevelItem(0, subTree); subTree->setExpanded(true); QTreeWidgetItemIterator it2(tree, QTreeWidgetItemIterator::HasChildren); diff --git a/src/mainwindow.h b/src/mainwindow.h index 69d08337..cd7e6f43 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -211,7 +211,7 @@ private: void startSteam(); - void updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const MOShared::DirectoryEntry &directoryEntry, bool conflictsOnly); + void updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const MOShared::DirectoryEntry &directoryEntry, bool conflictsOnly, QIcon *fileIcon, QIcon *folderIcon); bool refreshProfiles(bool selectProfile = true); void refreshExecutablesList(); void installMod(QString fileName = ""); -- cgit v1.3.1 From 9a33dc148054443cb0efd5916ff758fca5bc09e3 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Thu, 20 Dec 2018 19:12:11 +0100 Subject: *Added "Open INIs Folder" option. *Changed "Open Instance Folder" to open the Appdata or MO2 install instead of the baseDirectory. --- src/mainwindow.cpp | 19 ++++++++++++++++++- src/mainwindow.h | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3e35ebec..954c88c6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3639,7 +3639,11 @@ void MainWindow::disableVisibleMods() void MainWindow::openInstanceFolder() { - ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.settings().getBaseDirectory()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + QString dataPath = qApp->property("dataPath").toString(); + ::ShellExecuteW(nullptr, L"explore", ToWString(dataPath).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + + //opens BaseDirectory instead + //::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.settings().getBaseDirectory()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } void MainWindow::openLogsFolder() @@ -3665,6 +3669,17 @@ void MainWindow::openProfileFolder() ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.currentProfile()->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } +void MainWindow::openIniFolder() +{ + if (m_OrganizerCore.currentProfile()->localSettingsEnabled()) + { + ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.currentProfile()->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } + else { + ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.managedGame()->documentsDirectory().absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } +} + void MainWindow::openDownloadsFolder() { ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.settings().getDownloadDirectory()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); @@ -3864,6 +3879,8 @@ QMenu *MainWindow::openFolderMenu() FolderMenu->addAction(tr("Open MyGames folder"), this, SLOT(openMyGamesFolder())); + FolderMenu->addAction(tr("Open INIs folder"), this, SLOT(openIniFolder())); + FolderMenu->addSeparator(); FolderMenu->addAction(tr("Open Instance folder"), this, SLOT(openInstanceFolder())); diff --git a/src/mainwindow.h b/src/mainwindow.h index cd7e6f43..e799a64a 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -529,6 +529,7 @@ private slots: void openDownloadsFolder(); void openModsFolder(); void openProfileFolder(); + void openIniFolder(); void openGameFolder(); void openMyGamesFolder(); void startExeAction(); -- cgit v1.3.1