summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt11
-rw-r--r--src/editexecutablesdialog.cpp143
-rw-r--r--src/editexecutablesdialog.h9
-rw-r--r--src/editexecutablesdialog.ui37
-rw-r--r--src/executableslist.cpp156
-rw-r--r--src/executableslist.h24
-rw-r--r--src/mainwindow.cpp3
-rw-r--r--src/pch.h1
8 files changed, 221 insertions, 163 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5623a851..645a5a73 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -293,7 +293,6 @@ set(core
categories
shared/directoryentry
directoryrefresher
- executableslist
installationmanager
instancemanager
loadmechanism
@@ -309,7 +308,6 @@ set(dialogs
activatemodsdialog
categoriesdialog
credentialsdialog
- editexecutablesdialog
filedialogmemory
forcedloaddialog
forcedloaddialogwidget
@@ -333,6 +331,11 @@ set(downloads
downloadmanager
)
+set(executables
+ executableslist
+ editexecutablesdialog
+)
+
set(locking
ilockedwaitingforprocess
lockeddialog
@@ -412,8 +415,8 @@ set(widgets
)
set(src_filters
- application core browser dialogs downloads locking modinfo modlist plugins
- previews profiles settings utilities widgets
+ application core browser dialogs downloads executables locking modinfo
+ modlist plugins previews profiles settings utilities widgets
)
foreach(filter in list ${src_filters})
diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp
index 6c0522e4..882bb8b5 100644
--- a/src/editexecutablesdialog.cpp
+++ b/src/editexecutablesdialog.cpp
@@ -49,8 +49,9 @@ EditExecutablesDialog::EditExecutablesDialog(OrganizerCore& oc, QWidget* parent)
m_customOverwrites.load(m_organizerCore.currentProfile(), m_executablesList);
m_forcedLibraries.load(m_organizerCore.currentProfile(), m_executablesList);
- fillExecutableList();
+ fillList();
ui->mods->addItems(m_organizerCore.modList()->allMods());
+ setDirty(false);
// some widgets need to do more than just save() and have their own handler
connect(ui->binary, &QLineEdit::textChanged, [&]{ save(); });
@@ -59,13 +60,7 @@ EditExecutablesDialog::EditExecutablesDialog(OrganizerCore& oc, QWidget* parent)
connect(ui->steamAppID, &QLineEdit::textChanged, [&]{ save(); });
connect(ui->mods, &QComboBox::currentTextChanged, [&]{ save(); });
connect(ui->useApplicationIcon, &QCheckBox::toggled, [&]{ save(); });
-
- // select the first one in the list, if any
- if (ui->list->count() > 0) {
- ui->list->item(0)->setSelected(true);
- } else {
- updateUI(nullptr, nullptr);
- }
+ connect(ui->list->model(), &QAbstractItemModel::rowsMoved, [&]{ saveOrder(); });
}
EditExecutablesDialog::~EditExecutablesDialog() = default;
@@ -132,6 +127,15 @@ void EditExecutablesDialog::commitChanges()
// set the new executables list
m_organizerCore.setExecutablesList(newExecutables);
+
+ setDirty(false);
+}
+
+void EditExecutablesDialog::setDirty(bool b)
+{
+ if (auto* button=ui->buttons->button(QDialogButtonBox::Apply)) {
+ button->setEnabled(b);
+ }
}
QListWidgetItem* EditExecutablesDialog::selectedItem()
@@ -162,26 +166,25 @@ Executable* EditExecutablesDialog::selectedExe()
return &*itor;
}
-void EditExecutablesDialog::fillExecutableList()
+void EditExecutablesDialog::fillList()
{
ui->list->clear();
for(const auto& exe : m_executablesList) {
ui->list->addItem(createListItem(exe));
}
+
+ // select the first one in the list, if any
+ if (ui->list->count() > 0) {
+ ui->list->item(0)->setSelected(true);
+ } else {
+ updateUI(nullptr, nullptr);
+ }
}
QListWidgetItem* EditExecutablesDialog::createListItem(const Executable& exe)
{
- QListWidgetItem *newItem = new QListWidgetItem(exe.title());
-
- if (!exe.isCustom()) {
- auto f = newItem->font();
- f.setItalic(true);
- newItem->setFont(f);
- }
-
- return newItem;
+ return new QListWidgetItem(exe.title());
}
void EditExecutablesDialog::updateUI(
@@ -205,14 +208,12 @@ void EditExecutablesDialog::updateUI(
void EditExecutablesDialog::setButtons(
const QListWidgetItem* item, const Executable* e)
{
- // add is always enabled
+ // add and remove are always enabled
if (item) {
- ui->remove->setEnabled(e->isCustom());
ui->up->setEnabled(canMove(item, -1));
ui->down->setEnabled(canMove(item, +1));
} else {
- ui->remove->setEnabled(false);
ui->up->setEnabled(false);
ui->down->setEnabled(false);
}
@@ -243,7 +244,6 @@ void EditExecutablesDialog::clearEdits()
ui->configureLibraries->setEnabled(false);
ui->useApplicationIcon->setEnabled(false);
ui->useApplicationIcon->setChecked(false);
- ui->pluginProvidedLabel->setVisible(false);
}
void EditExecutablesDialog::setEdits(const Executable& e)
@@ -287,19 +287,15 @@ void EditExecutablesDialog::setEdits(const Executable& e)
ui->configureLibraries->setEnabled(hasForcedLibraries);
}
- ui->pluginProvidedLabel->setVisible(!e.isCustom());
-
- // only enabled for custom executables
- ui->title->setEnabled(e.isCustom());
- ui->binary->setEnabled(e.isCustom());
- ui->browseBinary->setEnabled(e.isCustom());
- ui->workingDirectory->setEnabled(e.isCustom());
- ui->browseWorkingDirectory->setEnabled(e.isCustom());
- ui->arguments->setEnabled(e.isCustom());
- ui->overwriteSteamAppID->setEnabled(e.isCustom());
- ui->useApplicationIcon->setEnabled(e.isCustom());
-
// always enabled
+ ui->title->setEnabled(true);
+ ui->binary->setEnabled(true);
+ ui->browseBinary->setEnabled(true);
+ ui->workingDirectory->setEnabled(true);
+ ui->browseWorkingDirectory->setEnabled(true);
+ ui->arguments->setEnabled(true);
+ ui->overwriteSteamAppID->setEnabled(true);
+ ui->useApplicationIcon->setEnabled(true);
ui->createFilesInMod->setEnabled(true);
ui->forceLoadLibraries->setEnabled(true);
}
@@ -361,6 +357,14 @@ void EditExecutablesDialog::save()
} else {
e->flags(e->flags() & (~Executable::UseApplicationIcon));
}
+
+ setDirty(true);
+}
+
+void EditExecutablesDialog::saveOrder()
+{
+ m_executablesList = getExecutablesList();
+ setDirty(true);
}
bool EditExecutablesDialog::canMove(const QListWidgetItem* item, int direction)
@@ -393,6 +397,8 @@ void EditExecutablesDialog::move(QListWidgetItem* item, int direction)
ui->list->takeItem(row);
ui->list->insertItem(row + (direction > 0 ? 1 : -1), item);
item->setSelected(true);
+
+ setDirty(true);
}
void EditExecutablesDialog::on_list_itemSelectionChanged()
@@ -400,22 +406,43 @@ void EditExecutablesDialog::on_list_itemSelectionChanged()
updateUI(selectedItem(), selectedExe());
}
+void EditExecutablesDialog::on_reset_clicked()
+{
+ const auto title = tr("Reset plugin executables");
+
+ const auto text = tr(
+ "This will restore all the executables provided by the game plugin. If "
+ "there are existing executables with the same names, they will be "
+ "automatically renamed and left unchanged.");
+
+ const auto buttons = QMessageBox::Ok | QMessageBox::Cancel;
+
+ if (QMessageBox::question(this, title, text, buttons) != QMessageBox::Ok) {
+ return;
+ }
+
+ m_executablesList.resetFromPlugin(m_organizerCore.managedGame());
+ fillList();
+
+ setDirty(true);
+}
+
void EditExecutablesDialog::on_add_clicked()
{
- auto title = makeNonConflictingTitle(tr("New Executable"));
+ auto title = m_executablesList.makeNonConflictingTitle(tr("New Executable"));
if (!title) {
return;
}
- auto e = Executable()
- .title(*title)
- .flags(Executable::CustomExecutable);
+ const Executable e(*title);
m_executablesList.setExecutable(e);
auto* item = createListItem(e);
ui->list->addItem(item);
item->setSelected(true);
+
+ setDirty(true);
}
void EditExecutablesDialog::on_remove_clicked()
@@ -453,6 +480,8 @@ void EditExecutablesDialog::on_remove_clicked()
} else {
ui->list->item(currentRow)->setSelected(true);
}
+
+ setDirty(true);
}
void EditExecutablesDialog::on_up_clicked()
@@ -563,7 +592,7 @@ void EditExecutablesDialog::on_browseBinary_clicked()
// setting title if currently empty
if (ui->title->text().isEmpty()) {
const auto prefix = QFileInfo(binaryName).baseName();
- const auto newTitle = makeNonConflictingTitle(prefix);
+ const auto newTitle = m_executablesList.makeNonConflictingTitle(prefix);
if (newTitle) {
ui->title->setText(*newTitle);
@@ -607,15 +636,16 @@ void EditExecutablesDialog::on_configureLibraries_clicked()
}
}
-void EditExecutablesDialog::on_buttons_accepted()
+void EditExecutablesDialog::on_buttons_clicked(QAbstractButton* b)
{
- commitChanges();
- accept();
-}
-
-void EditExecutablesDialog::on_buttons_rejected()
-{
- reject();
+ if (b == ui->buttons->button(QDialogButtonBox::Ok)) {
+ commitChanges();
+ accept();
+ } else if (b == ui->buttons->button(QDialogButtonBox::Apply)) {
+ commitChanges();
+ } else {
+ reject();
+ }
}
void EditExecutablesDialog::setJarBinary(const QString& binaryName)
@@ -641,25 +671,6 @@ void EditExecutablesDialog::setJarBinary(const QString& binaryName)
save();
}
-std::optional<QString> EditExecutablesDialog::makeNonConflictingTitle(
- const QString& prefix)
-{
- QString title = prefix;
-
- for (int i=1; i<100; ++i) {
- if (!m_executablesList.titleExists(title)) {
- return title;
- }
-
- title = prefix + QString(" (%1)").arg(i);
- }
-
- qCritical().nospace()
- << "ran out of executable titles for prefix '" << prefix << "'";
-
- return {};
-}
-
void CustomOverwrites::load(Profile* p, const ExecutablesList& exes)
{
diff --git a/src/editexecutablesdialog.h b/src/editexecutablesdialog.h
index 10a6166f..4a04ef42 100644
--- a/src/editexecutablesdialog.h
+++ b/src/editexecutablesdialog.h
@@ -110,6 +110,7 @@ public:
private slots:
void on_list_itemSelectionChanged();
+ void on_reset_clicked();
void on_add_clicked();
void on_remove_clicked();
void on_up_clicked();
@@ -124,8 +125,7 @@ private slots:
void on_browseWorkingDirectory_clicked();
void on_configureLibraries_clicked();
- void on_buttons_accepted();
- void on_buttons_rejected();
+ void on_buttons_clicked(QAbstractButton* b);
private:
std::unique_ptr<Ui::EditExecutablesDialog> ui;
@@ -140,19 +140,20 @@ private:
QListWidgetItem* selectedItem();
Executable* selectedExe();
- void fillExecutableList();
+ void fillList();
QListWidgetItem* createListItem(const Executable& exe);
void updateUI(const QListWidgetItem* item, const Executable* e);
void clearEdits();
void setEdits(const Executable& e);
void setButtons(const QListWidgetItem* item, const Executable* e);
void save();
+ void saveOrder();
bool canMove(const QListWidgetItem* item, int direction);
void move(QListWidgetItem* item, int direction);
void setJarBinary(const QString& binaryName);
- std::optional<QString> makeNonConflictingTitle(const QString& prefix);
bool isTitleConflicting(const QString& s);
void commitChanges();
+ void setDirty(bool b);
};
#endif // EDITEXECUTABLESDIALOG_H
diff --git a/src/editexecutablesdialog.ui b/src/editexecutablesdialog.ui
index 9b6c8153..a42dbeed 100644
--- a/src/editexecutablesdialog.ui
+++ b/src/editexecutablesdialog.ui
@@ -168,6 +168,26 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QToolButton" name="reset">
+ <property name="toolTip">
+ <string>Adds the executables provided by the game plugin and moves any existing executables out of the way</string>
+ </property>
+ <property name="statusTip">
+ <string>Adds the executables provided by the game plugin and moves any existing executables out of the way</string>
+ </property>
+ <property name="whatsThis">
+ <string>Adds the executables provided by the game plugin and moves any existing executables out of the way</string>
+ </property>
+ <property name="text">
+ <string>Reset</string>
+ </property>
+ <property name="icon">
+ <iconset resource="resources.qrc">
+ <normaloff>:/MO/gui/edit_clear</normaloff>:/MO/gui/edit_clear</iconset>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
<item>
@@ -426,21 +446,6 @@ Right now the only case I know of where this needs to be overwritten is for the
</widget>
</item>
<item>
- <widget class="QLabel" name="pluginProvidedLabel">
- <property name="font">
- <font>
- <italic>true</italic>
- </font>
- </property>
- <property name="text">
- <string>This executable is provided by the game plugin</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -463,7 +468,7 @@ Right now the only case I know of where this needs to be overwritten is for the
<item>
<widget class="QDialogButtonBox" name="buttons">
<property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
diff --git a/src/executableslist.cpp b/src/executableslist.cpp
index 0283a845..daf200a6 100644
--- a/src/executableslist.cpp
+++ b/src/executableslist.cpp
@@ -74,8 +74,6 @@ void ExecutablesList::load(const MOBase::IPluginGame* game, QSettings& settings)
settings.setArrayIndex(i);
Executable::Flags flags;
- if (settings.value("custom", true).toBool())
- flags |= Executable::CustomExecutable;
if (settings.value("toolbar", false).toBool())
flags |= Executable::ShowInToolbar;
if (settings.value("ownicon", false).toBool())
@@ -92,7 +90,7 @@ void ExecutablesList::load(const MOBase::IPluginGame* game, QSettings& settings)
settings.endArray();
- addFromPlugin(game);
+ addFromPlugin(game, IgnoreExisting);
}
void ExecutablesList::store(QSettings& settings)
@@ -106,29 +104,33 @@ void ExecutablesList::store(QSettings& settings)
settings.setArrayIndex(count++);
settings.setValue("title", item.title());
- settings.setValue("custom", item.isCustom());
settings.setValue("toolbar", item.isShownOnToolbar());
settings.setValue("ownicon", item.usesOwnIcon());
-
- if (item.isCustom()) {
- settings.setValue("binary", item.binaryInfo().absoluteFilePath());
- settings.setValue("arguments", item.arguments());
- settings.setValue("workingDirectory", item.workingDirectory());
- settings.setValue("steamAppID", item.steamAppID());
- }
+ settings.setValue("binary", item.binaryInfo().absoluteFilePath());
+ settings.setValue("arguments", item.arguments());
+ settings.setValue("workingDirectory", item.workingDirectory());
+ settings.setValue("steamAppID", item.steamAppID());
}
settings.endArray();
}
-void ExecutablesList::addFromPlugin(IPluginGame const *game)
+void ExecutablesList::resetFromPlugin(MOBase::IPluginGame const *game)
+{
+ qDebug("resetting plugin executables");
+ addFromPlugin(game, MoveExisting);
+}
+
+void ExecutablesList::addFromPlugin(IPluginGame const *game, SetFlags flags)
{
Q_ASSERT(game != nullptr);
for (const ExecutableInfo &info : game->executables()) {
- if (info.isValid()) {
- setExecutable({info, Executable::UseApplicationIcon});
+ if (!info.isValid()) {
+ continue;
}
+
+ setExecutable({info, Executable::UseApplicationIcon}, flags);
}
const QFileInfo eppBin(QCoreApplication::applicationDirPath() + "/explorer++/Explorer++.exe");
@@ -137,12 +139,14 @@ void ExecutablesList::addFromPlugin(IPluginGame const *game)
const auto args = QString("\"%1\"")
.arg(QDir::toNativeSeparators(game->dataDirectory().absolutePath()));
- setExecutable(Executable()
+ const auto exe = Executable()
.title("Explore Virtual Folder")
.binaryInfo(eppBin)
.arguments(args)
.workingDirectory(eppBin.absolutePath())
- .flags(Executable::UseApplicationIcon));
+ .flags(Executable::UseApplicationIcon);
+
+ setExecutable(exe, flags);
}
}
@@ -188,28 +192,81 @@ bool ExecutablesList::titleExists(const QString &title) const
return std::find_if(m_Executables.begin(), m_Executables.end(), test) != m_Executables.end();
}
-void ExecutablesList::setExecutable(const Executable &executable)
+void ExecutablesList::setExecutable(const Executable &exe)
+{
+ setExecutable(exe, MergeExisting);
+}
+
+void ExecutablesList::setExecutable(const Executable &exe, SetFlags flags)
{
- auto itor = find(executable.title());
+ auto itor = find(exe.title());
+
+ if (itor != end()) {
+ if (flags == IgnoreExisting) {
+ return;
+ }
+
+ if (flags == MoveExisting) {
+ const auto newTitle = makeNonConflictingTitle(exe.title());
+ if (!newTitle) {
+ qCritical().nospace()
+ << "executable '" << exe.title() << "' was in the way but could "
+ << "not be renamed";
+
+ return;
+ }
+
+ qWarning().nospace()
+ << "executable '" << itor->title() << "' was in the way and was "
+ << "renamed to '" << *newTitle << "'";
+
+ itor->title(*newTitle);
+ itor = end();
+ }
+ }
if (itor == m_Executables.end()) {
- m_Executables.push_back(executable);
+ m_Executables.push_back(exe);
} else {
- itor->mergeFrom(executable);
+ itor->mergeFrom(exe);
}
}
void ExecutablesList::remove(const QString &title)
{
- for (std::vector<Executable>::iterator iter = m_Executables.begin(); iter != m_Executables.end(); ++iter) {
- if (iter->isCustom() && (iter->title() == title)) {
- m_Executables.erase(iter);
- break;
+ auto itor = find(title);
+ if (itor != m_Executables.end()) {
+ m_Executables.erase(itor);
+ }
+}
+
+std::optional<QString> ExecutablesList::makeNonConflictingTitle(
+ const QString& prefix)
+{
+ const int max = 100;
+
+ QString title = prefix;
+
+ for (int i=1; i<max; ++i) {
+ if (!titleExists(title)) {
+ return title;
}
+
+ title = prefix + QString(" (%1)").arg(i);
}
+
+ qCritical().nospace()
+ << "ran out of executable titles for prefix '" << prefix << "'";
+
+ return {};
}
+Executable::Executable(QString title)
+ : m_title(title)
+{
+}
+
Executable::Executable(const MOBase::ExecutableInfo& info, Flags flags) :
m_title(info.title()),
m_binaryInfo(info.binary()),
@@ -286,11 +343,6 @@ Executable& Executable::flags(Flags f)
return *this;
}
-bool Executable::isCustom() const
-{
- return m_flags.testFlag(CustomExecutable);
-}
-
bool Executable::isShownOnToolbar() const
{
return m_flags.testFlag(ShowInToolbar);
@@ -315,43 +367,13 @@ void Executable::mergeFrom(const Executable& other)
// flags on plugin executables that the user is allowed to change
const auto allow = ShowInToolbar;
+ // this happens after executables are loaded from settings and plugin
+ // executables are being added, or when users are modifying executables
- if (!other.isCustom()) {
- // this happens when loading plugin executables in addFromPlugin(), replace
- // everything in case the plugin has changed
-
- // remember the flags though
- const auto flags = m_flags;
-
- // overwrite everything
- *this = other;
-
- // set the user flags
- m_flags |= (flags & allow);
- }
- else if (!isCustom()) {
- // this happens when the user is trying to modify a plugin executable
- m_flags |= (other.flags() & allow);
- } else {
- // this happens after executables are loaded from settings and plugin
- // executables are being added, or when users are modifying executables
-
- m_title = other.title();
- m_arguments = other.arguments();
- m_steamAppID = other.steamAppID();
- m_workingDirectory = other.workingDirectory();
-
- // don't overwrite a valid binary with an invalid one
- if (other.binaryInfo().exists()) {
- m_binaryInfo = other.binaryInfo();
- }
-
- if (!other.isCustom()) {
- // overwriting a custom executable with a plugin, merge all the flags
- m_flags |= other.flags();
- } else {
- // overwriting a custom with another custom, just replace the flags
- m_flags = other.flags();
- }
- }
+ m_title = other.title();
+ m_binaryInfo = other.binaryInfo();
+ m_arguments = other.arguments();
+ m_steamAppID = other.steamAppID();
+ m_workingDirectory = other.workingDirectory();
+ m_flags = other.flags();
}
diff --git a/src/executableslist.h b/src/executableslist.h
index 54a105d0..61582e71 100644
--- a/src/executableslist.h
+++ b/src/executableslist.h
@@ -23,6 +23,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "executableinfo.h"
#include <vector>
+#include <optional>
#include <QFileInfo>
#include <QMetaType>
@@ -37,14 +38,13 @@ class Executable
public:
enum Flag
{
- CustomExecutable = 0x01,
ShowInToolbar = 0x02,
UseApplicationIcon = 0x04
};
Q_DECLARE_FLAGS(Flags, Flag);
- Executable() = default;
+ Executable(QString title={});
/**
* @brief Executable from plugin
@@ -65,7 +65,6 @@ public:
Executable& workingDirectory(const QString& s);
Executable& flags(Flags f);
- bool isCustom() const;
bool isShownOnToolbar() const;
void setShownOnToolbar(bool state);
bool usesOwnIcon() const;
@@ -106,6 +105,8 @@ public:
*/
void load(const MOBase::IPluginGame* game, QSettings& settings);
+ void resetFromPlugin(MOBase::IPluginGame const *game);
+
/**
* @brief writes the current list to the settings
*/
@@ -166,13 +167,28 @@ public:
**/
void remove(const QString &title);
+ std::optional<QString> makeNonConflictingTitle(const QString& prefix);
+
private:
+ enum SetFlags
+ {
+ IgnoreExisting = 1,
+ MergeExisting,
+ MoveExisting
+ };
+
std::vector<Executable> m_Executables;
/**
* @brief add the executables preconfigured for this game
**/
- void addFromPlugin(MOBase::IPluginGame const *game);
+ void addFromPlugin(MOBase::IPluginGame const *game, SetFlags flags);
+
+ /**
+ * @brief add a new executable to the list
+ * @param executable
+ */
+ void setExecutable(const Executable &exe, SetFlags flags);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Executable::Flags)
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 15a6eb62..b5e9c320 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -5513,8 +5513,7 @@ void MainWindow::addAsExecutable()
.title(name)
.binaryInfo(binaryInfo)
.arguments(arguments)
- .workingDirectory(targetInfo.absolutePath())
- .flags(Executable::CustomExecutable));
+ .workingDirectory(targetInfo.absolutePath()));
refreshExecutablesList();
}
diff --git a/src/pch.h b/src/pch.h
index e86c0da6..1d8df43a 100644
--- a/src/pch.h
+++ b/src/pch.h
@@ -13,6 +13,7 @@
#include <list>
#include <map>
#include <memory>
+#include <optional>
#include <regex>
#include <set>
#include <sstream>