summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorisanae <14251494+isanae@users.noreply.github.com>2019-10-10 06:12:07 -0400
committerGitHub <noreply@github.com>2019-10-10 06:12:07 -0400
commit168fc950f9b15ce89084316d96a1f613a12e3013 (patch)
tree73b1eb1afd04830e65c25060f1ce4763db98b642 /src
parent0f5ecb8b78b86f309be3ef1902dbf4ee51738fc4 (diff)
parent4d3495b3fb00b644c57e773bbcdbfb2eee7c0ea6 (diff)
Merge pull request #863 from isanae/executables-fixes
Executables fixes
Diffstat (limited to 'src')
-rw-r--r--src/editexecutablesdialog.cpp224
-rw-r--r--src/editexecutablesdialog.h22
-rw-r--r--src/editexecutablesdialog.ui39
-rw-r--r--src/executableslist.cpp16
-rw-r--r--src/executableslist.h6
-rw-r--r--src/mainwindow.cpp154
-rw-r--r--src/mainwindow.h5
-rw-r--r--src/modinfo.cpp25
-rw-r--r--src/modinfo.h12
-rw-r--r--src/stylesheets/Night Eyes.qss5
-rw-r--r--src/stylesheets/vs15 Dark-Green.qss7
-rw-r--r--src/stylesheets/vs15 Dark-Orange.qss7
-rw-r--r--src/stylesheets/vs15 Dark-Purple.qss7
-rw-r--r--src/stylesheets/vs15 Dark-Red.qss7
-rw-r--r--src/stylesheets/vs15 Dark-Yellow.qss7
-rw-r--r--src/stylesheets/vs15 Dark.qss7
16 files changed, 430 insertions, 120 deletions
diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp
index 0d6367b8..3efadc4c 100644
--- a/src/editexecutablesdialog.cpp
+++ b/src/editexecutablesdialog.cpp
@@ -33,7 +33,29 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using namespace MOBase;
using namespace MOShared;
-EditExecutablesDialog::EditExecutablesDialog(OrganizerCore& oc, QWidget* parent)
+class IgnoreChanges
+{
+public:
+ IgnoreChanges(EditExecutablesDialog* d)
+ : m_dialog(d)
+ {
+ m_dialog->m_settingUI = true;
+ }
+
+ ~IgnoreChanges()
+ {
+ m_dialog->m_settingUI = false;
+ }
+
+ IgnoreChanges(const IgnoreChanges&) = delete;
+ IgnoreChanges& operator=(const IgnoreChanges&) = delete;
+
+private:
+ EditExecutablesDialog* m_dialog;
+};
+
+
+EditExecutablesDialog::EditExecutablesDialog(OrganizerCore& oc, int sel, QWidget* parent)
: TutorableDialog("EditExecutables", parent)
, ui(new Ui::EditExecutablesDialog)
, m_organizerCore(oc)
@@ -49,10 +71,25 @@ EditExecutablesDialog::EditExecutablesDialog(OrganizerCore& oc, QWidget* parent)
loadCustomOverwrites();
loadForcedLibraries();
- ui->mods->addItems(m_organizerCore.modList()->allMods());
+ for (auto&& m : m_organizerCore.modList()->allMods()) {
+ if (ModInfo::isRegularName(m)) {
+ ui->mods->addItem(m);
+ }
+ }
+
fillList();
setDirty(false);
+ if (sel >= 0 && sel < ui->list->count()) {
+ selectIndex(sel);
+ }
+
+ auto* m = new QMenu;
+ m->addAction(tr("Add from file..."), [&]{ addFromFile(); });
+ m->addAction(tr("Add empty"), [&]{ addEmpty(); });
+ m->addAction(tr("Clone selected"), [&]{ clone(); });
+ ui->add->setMenu(m);
+
// some widgets need to do more than just save() and have their own handler
connect(ui->binary, &QLineEdit::textChanged, [&]{ save(); });
connect(ui->workingDirectory, &QLineEdit::textChanged, [&]{ save(); });
@@ -60,6 +97,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(); });
+ connect(ui->hide, &QCheckBox::toggled, [&]{ save(); });
connect(ui->list->model(), &QAbstractItemModel::rowsMoved, [&]{ saveOrder(); });
}
@@ -168,6 +206,14 @@ void EditExecutablesDialog::setDirty(bool b)
}
}
+void EditExecutablesDialog::selectIndex(int i)
+{
+ if (i >= 0 && i < ui->list->count()) {
+ ui->list->selectionModel()->setCurrentIndex(
+ ui->list->model()->index(i, 0), QItemSelectionModel::ClearAndSelect);
+ }
+}
+
QListWidgetItem* EditExecutablesDialog::selectedItem()
{
const auto selection = ui->list->selectedItems();
@@ -206,7 +252,7 @@ void EditExecutablesDialog::fillList()
// select the first one in the list, if any
if (ui->list->count() > 0) {
- ui->list->item(0)->setSelected(true);
+ selectIndex(0);
} else {
updateUI(nullptr, nullptr);
}
@@ -221,7 +267,7 @@ void EditExecutablesDialog::updateUI(
const QListWidgetItem* item, const Executable* e)
{
// the ui is currently being set, ignore changes
- m_settingUI = true;
+ IgnoreChanges c(this);
if (e) {
setEdits(*e);
@@ -230,9 +276,6 @@ void EditExecutablesDialog::updateUI(
}
setButtons(item, e);
-
- // any changes from now on are from the user
- m_settingUI = false;
}
void EditExecutablesDialog::setButtons(
@@ -274,6 +317,10 @@ void EditExecutablesDialog::clearEdits()
ui->configureLibraries->setEnabled(false);
ui->useApplicationIcon->setEnabled(false);
ui->useApplicationIcon->setChecked(false);
+ ui->hide->setEnabled(false);
+ ui->hide->setChecked(false);
+
+ m_lastGoodTitle = "";
}
void EditExecutablesDialog::setEdits(const Executable& e)
@@ -286,6 +333,9 @@ void EditExecutablesDialog::setEdits(const Executable& e)
ui->steamAppID->setEnabled(!e.steamAppID().isEmpty());
ui->steamAppID->setText(e.steamAppID());
ui->useApplicationIcon->setChecked(e.usesOwnIcon());
+ ui->hide->setChecked(e.hide());
+
+ m_lastGoodTitle = e.title();
{
int modIndex = -1;
@@ -329,6 +379,7 @@ void EditExecutablesDialog::setEdits(const Executable& e)
ui->useApplicationIcon->setEnabled(true);
ui->createFilesInMod->setEnabled(true);
ui->forceLoadLibraries->setEnabled(true);
+ ui->hide->setEnabled(true);
}
void EditExecutablesDialog::save()
@@ -388,6 +439,12 @@ void EditExecutablesDialog::save()
e->flags(e->flags() & (~Executable::UseApplicationIcon));
}
+ if (ui->hide->isChecked()) {
+ e->flags(e->flags() | Executable::Hide);
+ } else {
+ e->flags(e->flags() & (~Executable::Hide));
+ }
+
setDirty(true);
}
@@ -421,13 +478,14 @@ void EditExecutablesDialog::move(QListWidgetItem* item, int direction)
return;
}
- const auto row = ui->list->row(item);
+ const auto oldRow = ui->list->row(item);
+ const auto newRow = oldRow + (direction > 0 ? 1 : -1);
// removing item
- ui->list->takeItem(row);
- ui->list->insertItem(row + (direction > 0 ? 1 : -1), item);
- item->setSelected(true);
+ ui->list->takeItem(oldRow);
+ ui->list->insertItem(newRow, item);
+ selectIndex(newRow);
setDirty(true);
}
@@ -459,20 +517,7 @@ void EditExecutablesDialog::on_reset_clicked()
void EditExecutablesDialog::on_add_clicked()
{
- auto title = m_executablesList.makeNonConflictingTitle(tr("New Executable"));
- if (!title) {
- return;
- }
-
- const Executable e(*title);
-
- m_executablesList.setExecutable(e);
-
- auto* item = createListItem(e);
- ui->list->addItem(item);
- item->setSelected(true);
-
- setDirty(true);
+ addFromFile();
}
void EditExecutablesDialog::on_remove_clicked()
@@ -505,10 +550,10 @@ void EditExecutablesDialog::on_remove_clicked()
if (currentRow >= ui->list->count()) {
// that was the last item, select the new list item, if any
if (ui->list->count() > 0) {
- ui->list->item(ui->list->count() - 1)->setSelected(true);
+ selectIndex(ui->list->count() - 1);
}
} else {
- ui->list->item(currentRow)->setSelected(true);
+ selectIndex(currentRow);
}
setDirty(true);
@@ -548,17 +593,26 @@ bool EditExecutablesDialog::isTitleConflicting(const QString& s)
return false;
}
-void EditExecutablesDialog::on_title_textChanged(const QString& s)
+void EditExecutablesDialog::on_title_textChanged(const QString& original)
{
if (m_settingUI) {
return;
}
- // don't allow changing the title to something that already exists
+ auto s = original.trimmed();
+
+ // disallow empty names
+ if (s.isEmpty()) {
+ return;
+ }
+
+ // disallow changing the title to something that already exists
if (isTitleConflicting(s)) {
return;
}
+ m_lastGoodTitle = s;
+
// must save before modifying the item in the list widget because saving
// relies on the item's text being the same as an item in m_executablesList
save();
@@ -570,6 +624,11 @@ void EditExecutablesDialog::on_title_textChanged(const QString& s)
}
}
+void EditExecutablesDialog::on_title_editingFinished()
+{
+ ui->title->setText(m_lastGoodTitle);
+}
+
void EditExecutablesDialog::on_overwriteSteamAppID_toggled(bool checked)
{
if (m_settingUI) {
@@ -602,35 +661,78 @@ void EditExecutablesDialog::on_forceLoadLibraries_toggled(bool checked)
void EditExecutablesDialog::on_browseBinary_clicked()
{
- const QString binaryName = FileDialogMemory::getOpenFileName(
- "editExecutableBinary", this, tr("Select a binary"), ui->binary->text(),
- tr("Executable (%1)").arg("*.exe *.bat *.jar"));
+ const auto binaryName = browseBinary(ui->binary->text());
+ if (binaryName.fileName().isEmpty()) {
+ return;
+ }
+
+ setBinary(binaryName);
+ save();
+}
+
+void EditExecutablesDialog::addFromFile()
+{
+ const auto binary = browseBinary(ui->binary->text());
+ if (binary.fileName().isEmpty()) {
+ return;
+ }
+
+ addNew(Executable(binary.fileName()));
+ setBinary(binary);
+}
+
+void EditExecutablesDialog::addEmpty()
+{
+ addNew(Executable(tr("New Executable")));
+}
+
+void EditExecutablesDialog::clone()
+{
+ auto* e = selectedExe();
+ if (!e) {
+ return;
+ }
+
+ addNew(*e);
+}
- if (binaryName.isNull()) {
- // canceled
+void EditExecutablesDialog::addNew(Executable e)
+{
+ const auto fixedTitle = m_executablesList.makeNonConflictingTitle(e.title());
+ if (!fixedTitle) {
return;
}
+ e.title(*fixedTitle);
+
+ m_executablesList.setExecutable(e);
+
+ auto* item = createListItem(e);
+ ui->list->addItem(item);
+
+ selectIndex(ui->list->count() - 1);
+ setDirty(true);
+}
+
+void EditExecutablesDialog::setBinary(const QFileInfo& binary)
+{
// setting binary
- if (binaryName.endsWith(".jar", Qt::CaseInsensitive)) {
+ if (binary.suffix().compare("jar", Qt::CaseInsensitive) == 0) {
// special case for jar files, uses the system java installation
- setJarBinary(binaryName);
+ setJarBinary(binary);
} else {
- ui->binary->setText(QDir::toNativeSeparators(binaryName));
+ ui->binary->setText(QDir::toNativeSeparators(binary.absoluteFilePath()));
}
- // setting title if currently empty or some variation of "New Executable"
- if (ui->title->text().isEmpty() ||
- ui->title->text().startsWith(tr("New Executable"), Qt::CaseInsensitive)) {
- const auto prefix = QFileInfo(binaryName).baseName();
+ // setting title if some variation of "New Executable"
+ if (ui->title->text().startsWith(tr("New Executable"), Qt::CaseInsensitive)) {
+ const auto prefix = binary.baseName();
const auto newTitle = m_executablesList.makeNonConflictingTitle(prefix);
if (newTitle) {
ui->title->setText(*newTitle);
}
}
-
- save();
}
void EditExecutablesDialog::on_browseWorkingDirectory_clicked()
@@ -640,7 +742,7 @@ void EditExecutablesDialog::on_browseWorkingDirectory_clicked()
ui->workingDirectory->text());
if (dirName.isNull()) {
- // canceled
+ // cancelled
return;
}
@@ -679,9 +781,26 @@ void EditExecutablesDialog::on_buttons_clicked(QAbstractButton* b)
}
}
-void EditExecutablesDialog::setJarBinary(const QString& binaryName)
+QFileInfo EditExecutablesDialog::browseBinary(const QString& initial)
{
- auto java = OrganizerCore::findJavaInstallation(binaryName);
+ const QString Filters =
+ tr("Executables (*.exe *.bat *.jar)") + ";;" +
+ tr("All Files (*.*)");
+
+ const auto f = FileDialogMemory::getOpenFileName(
+ "editExecutableBinary", this, tr("Select an executable"),
+ initial, Filters);
+
+ if (f.isNull()) {
+ return {};
+ }
+
+ return QFileInfo(f);
+}
+
+void EditExecutablesDialog::setJarBinary(const QFileInfo& binary)
+{
+ auto java = OrganizerCore::findJavaInstallation(binary.absoluteFilePath());
if (java.isEmpty()) {
QMessageBox::information(
@@ -691,13 +810,14 @@ void EditExecutablesDialog::setJarBinary(const QString& binaryName)
"the binary."));
}
- // only save once
+ {
+ // only save once
+ IgnoreChanges c(this);
- m_settingUI = true;
- ui->binary->setText(java);
- ui->workingDirectory->setText(QDir::toNativeSeparators(QFileInfo(binaryName).absolutePath()));
- ui->arguments->setText("-jar \"" + QDir::toNativeSeparators(binaryName) + "\"");
- m_settingUI = false;
+ ui->binary->setText(java);
+ ui->workingDirectory->setText(QDir::toNativeSeparators(binary.absolutePath()));
+ ui->arguments->setText("-jar \"" + QDir::toNativeSeparators(binary.absoluteFilePath()) + "\"");
+ }
save();
}
diff --git a/src/editexecutablesdialog.h b/src/editexecutablesdialog.h
index 494f0651..c8be0bf6 100644
--- a/src/editexecutablesdialog.h
+++ b/src/editexecutablesdialog.h
@@ -141,13 +141,15 @@ private:
**/
class EditExecutablesDialog : public MOBase::TutorableDialog
{
- Q_OBJECT
+ Q_OBJECT;
+ friend class IgnoreChanges;
public:
using CustomOverwrites = ToggableMap<QString>;
using ForcedLibraries = ToggableMap<QList<MOBase::ExecutableForcedLoadSetting>>;
- explicit EditExecutablesDialog(OrganizerCore& oc, QWidget* parent=nullptr);
+ explicit EditExecutablesDialog(
+ OrganizerCore& oc, int selection=-1, QWidget* parent=nullptr);
~EditExecutablesDialog();
@@ -169,6 +171,7 @@ private slots:
void on_down_clicked();
void on_title_textChanged(const QString& s);
+ void on_title_editingFinished();
void on_overwriteSteamAppID_toggled(bool checked);
void on_createFilesInMod_toggled(bool checked);
void on_forceLoadLibraries_toggled(bool checked);
@@ -196,6 +199,10 @@ private:
// forced libraries set in the dialog
ForcedLibraries m_forcedLibraries;
+ // remembers the last executable title that made sense, reverts to this when
+ // the widget loses focus if it's empty
+ QString m_lastGoodTitle;
+
// true when the change events being triggered are in response to loading
// the executable's data into the UI, not from a user change
bool m_settingUI;
@@ -217,10 +224,19 @@ private:
void saveOrder();
bool canMove(const QListWidgetItem* item, int direction);
void move(QListWidgetItem* item, int direction);
- void setJarBinary(const QString& binaryName);
bool isTitleConflicting(const QString& s);
void commitChanges();
void setDirty(bool b);
+ void selectIndex(int i);
+
+ void addFromFile();
+ void addEmpty();
+ void clone();
+ void addNew(Executable e);
+
+ QFileInfo browseBinary(const QString& initial);
+ void setBinary(const QFileInfo& binary);
+ void setJarBinary(const QFileInfo& binary);
};
#endif // EDITEXECUTABLESDIALOG_H
diff --git a/src/editexecutablesdialog.ui b/src/editexecutablesdialog.ui
index c2ff7d31..6f50b3b3 100644
--- a/src/editexecutablesdialog.ui
+++ b/src/editexecutablesdialog.ui
@@ -106,6 +106,9 @@
<iconset resource="resources.qrc">
<normaloff>:/MO/gui/add</normaloff>:/MO/gui/add</iconset>
</property>
+ <property name="popupMode">
+ <enum>QToolButton::InstantPopup</enum>
+ </property>
</widget>
</item>
<item>
@@ -140,7 +143,7 @@
<string>Move the executable up in the list</string>
</property>
<property name="text">
- <string>Move the executable up in the list</string>
+ <string>Up</string>
</property>
<property name="icon">
<iconset resource="resources.qrc">
@@ -160,7 +163,7 @@
<string>Move the executable down in the list</string>
</property>
<property name="text">
- <string>Move the executable down in the list</string>
+ <string>Down</string>
</property>
<property name="icon">
<iconset resource="resources.qrc">
@@ -375,7 +378,7 @@ Right now the only case I know of where this needs to be overwritten is for the
<string>If this is enabled, new files are created in the specified mod instead of the &quot;Overwrite&quot; mod.</string>
</property>
<property name="text">
- <string>Create Files in Mod instead of Overwrite (*)</string>
+ <string>Create files in mod instead of overwrite (*)</string>
</property>
</widget>
</item>
@@ -396,7 +399,7 @@ Right now the only case I know of where this needs to be overwritten is for the
<string>If this is enabled, the configured libraries will be automatically loaded when this executable is launched.</string>
</property>
<property name="text">
- <string>Force Load Libraries (*)</string>
+ <string>Force load libraries (*)</string>
</property>
</widget>
</item>
@@ -428,14 +431,27 @@ Right now the only case I know of where this needs to be overwritten is for the
<item>
<widget class="QCheckBox" name="useApplicationIcon">
<property name="text">
- <string>Use Application's Icon for desktop shortcuts</string>
+ <string>Use application's icon for desktop shortcuts</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="hide">
+ <property name="toolTip">
+ <string>This executable will not appear in the list, on the toolbar or in the menu. It will still be visible in this dialog.</string>
+ </property>
+ <property name="whatsThis">
+ <string>This executable will not appear in the list, on the toolbar or in the menu. It will still be visible in this dialog.</string>
+ </property>
+ <property name="text">
+ <string>Hide in user interface</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
- <string>(*) Profile Specific</string>
+ <string>(*) Profile specific</string>
</property>
<property name="margin">
<number>5</number>
@@ -475,14 +491,25 @@ Right now the only case I know of where this needs to be overwritten is for the
</layout>
</widget>
<tabstops>
+ <tabstop>add</tabstop>
+ <tabstop>remove</tabstop>
+ <tabstop>up</tabstop>
+ <tabstop>down</tabstop>
+ <tabstop>reset</tabstop>
+ <tabstop>list</tabstop>
+ <tabstop>title</tabstop>
<tabstop>binary</tabstop>
<tabstop>browseBinary</tabstop>
<tabstop>workingDirectory</tabstop>
<tabstop>browseWorkingDirectory</tabstop>
+ <tabstop>arguments</tabstop>
<tabstop>overwriteSteamAppID</tabstop>
<tabstop>steamAppID</tabstop>
<tabstop>createFilesInMod</tabstop>
<tabstop>mods</tabstop>
+ <tabstop>forceLoadLibraries</tabstop>
+ <tabstop>configureLibraries</tabstop>
+ <tabstop>useApplicationIcon</tabstop>
</tabstops>
<resources>
<include location="resources.qrc"/>
diff --git a/src/executableslist.cpp b/src/executableslist.cpp
index dce9181b..1be34b53 100644
--- a/src/executableslist.cpp
+++ b/src/executableslist.cpp
@@ -84,6 +84,9 @@ void ExecutablesList::load(const MOBase::IPluginGame* game, const Settings& s)
if (map["ownicon"].toBool())
flags |= Executable::UseApplicationIcon;
+ if (map["hide"].toBool())
+ flags |= Executable::Hide;
+
if (map.contains("custom")) {
// the "custom" setting only exists in older versions
needsUpgrade = true;
@@ -116,6 +119,7 @@ void ExecutablesList::store(Settings& s)
map["title"] = item.title();
map["toolbar"] = item.isShownOnToolbar();
map["ownicon"] = item.usesOwnIcon();
+ map["hide"] = item.hide();
map["binary"] = item.binaryInfo().absoluteFilePath();
map["arguments"] = item.arguments();
map["workingDirectory"] = item.workingDirectory();
@@ -343,6 +347,10 @@ void ExecutablesList::dump() const
flags.push_back("icon");
}
+ if (e.flags() & Executable::Hide) {
+ flags.push_back("hide");
+ }
+
log::debug(
" . executable '{}'\n"
" binary: {}\n"
@@ -456,11 +464,13 @@ bool Executable::usesOwnIcon() const
return m_flags.testFlag(UseApplicationIcon);
}
-void Executable::mergeFrom(const Executable& other)
+bool Executable::hide() const
{
- // flags on plugin executables that the user is allowed to change
- const auto allow = ShowInToolbar;
+ return m_flags.testFlag(Hide);
+}
+void Executable::mergeFrom(const Executable& other)
+{
// this happens after executables are loaded from settings and plugin
// executables are being added, or when users are modifying executables
diff --git a/src/executableslist.h b/src/executableslist.h
index 23cf3cfe..a18042db 100644
--- a/src/executableslist.h
+++ b/src/executableslist.h
@@ -39,8 +39,9 @@ class Executable
public:
enum Flag
{
- ShowInToolbar = 0x02,
- UseApplicationIcon = 0x04
+ ShowInToolbar = 0x02,
+ UseApplicationIcon = 0x04,
+ Hide = 0x08
};
Q_DECLARE_FLAGS(Flags, Flag);
@@ -69,6 +70,7 @@ public:
bool isShownOnToolbar() const;
void setShownOnToolbar(bool state);
bool usesOwnIcon() const;
+ bool hide() const;
void mergeFrom(const Executable& other);
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 23733cac..8d42d020 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -757,7 +757,7 @@ void MainWindow::updatePinnedExecutables()
bool hasLinks = false;
for (const auto& exe : *m_OrganizerCore.executablesList()) {
- if (exe.isShownOnToolbar()) {
+ if (!exe.hide() && exe.isShownOnToolbar()) {
hasLinks = true;
QAction *exeAction = new QAction(
@@ -1811,23 +1811,44 @@ bool MainWindow::refreshProfiles(bool selectProfile)
void MainWindow::refreshExecutablesList()
{
- QComboBox* executablesList = findChild<QComboBox*>("executablesListBox");
- executablesList->setEnabled(false);
- executablesList->clear();
- executablesList->addItem(tr("<Edit...>"));
+ QAbstractItemModel *model = ui->executablesListBox->model();
+
+ auto add = [&](const QString& title, const QFileInfo& binary) {
+ QIcon icon;
+ if (!binary.fileName().isEmpty()) {
+ icon = iconForExecutable(binary.filePath());
+ }
+
+ ui->executablesListBox->addItem(icon, title);
+
+ const auto i = ui->executablesListBox->count() - 1;
+
+ model->setData(
+ model->index(i, 0),
+ QSize(0, ui->executablesListBox->iconSize().height() + 4),
+ Qt::SizeHintRole);
+ };
- QAbstractItemModel *model = executablesList->model();
- int i = 0;
+ ui->executablesListBox->setEnabled(false);
+ ui->executablesListBox->clear();
+
+ add(tr("<Edit...>"), {});
+
for (const auto& exe : *m_OrganizerCore.executablesList()) {
- QIcon icon = iconForExecutable(exe.binaryInfo().filePath());
- executablesList->addItem(icon, exe.title());
- model->setData(model->index(i, 0), QSize(0, executablesList->iconSize().height() + 4), Qt::SizeHintRole);
- ++i;
+ if (!exe.hide()) {
+ add(exe.title(), exe.binaryInfo());
+ }
+ }
+
+ if (ui->executablesListBox->count() == 1) {
+ // all executables are hidden, add an empty one to at least be able to
+ // switch to edit
+ add(tr("(no executables)"), QFileInfo(":badfile"));
}
ui->executablesListBox->setCurrentIndex(1);
- executablesList->setEnabled(true);
+ ui->executablesListBox->setEnabled(true);
}
@@ -2344,36 +2365,51 @@ void MainWindow::installMod(QString fileName)
}
}
-void MainWindow::on_startButton_clicked() {
- ui->startButton->setEnabled(false);
+void MainWindow::on_startButton_clicked()
+{
try {
- const Executable &selectedExecutable(getSelectedExecutable());
- QString customOverwrite = m_OrganizerCore.currentProfile()->setting("custom_overwrites", selectedExecutable.title()).toString();
- auto forcedLibraries = m_OrganizerCore.currentProfile()->determineForcedLibraries(selectedExecutable.title());
- if (!m_OrganizerCore.currentProfile()->forcedLibrariesEnabled(selectedExecutable.title())) {
+ const Executable* selectedExecutable = getSelectedExecutable();
+ if (!selectedExecutable) {
+ return;
+ }
+
+ ui->startButton->setEnabled(false);
+
+ auto* profile = m_OrganizerCore.currentProfile();
+
+ const QString customOverwrite = profile->setting(
+ "custom_overwrites", selectedExecutable->title()).toString();
+
+ auto forcedLibraries = profile->determineForcedLibraries(
+ selectedExecutable->title());
+
+ if (!profile->forcedLibrariesEnabled(selectedExecutable->title())) {
forcedLibraries.clear();
}
+
m_OrganizerCore.spawnBinary(
- selectedExecutable.binaryInfo(), selectedExecutable.arguments(),
- selectedExecutable.workingDirectory().length() != 0
- ? selectedExecutable.workingDirectory()
- : selectedExecutable.binaryInfo().absolutePath(),
- selectedExecutable.steamAppID(),
- customOverwrite,
- forcedLibraries);
+ selectedExecutable->binaryInfo(),
+ selectedExecutable->arguments(),
+ selectedExecutable->workingDirectory().length() != 0 ?
+ selectedExecutable->workingDirectory() :
+ selectedExecutable->binaryInfo().absolutePath(),
+ selectedExecutable->steamAppID(),
+ customOverwrite,
+ forcedLibraries);
} catch (...) {
ui->startButton->setEnabled(true);
throw;
}
+
ui->startButton->setEnabled(true);
}
-bool MainWindow::modifyExecutablesDialog()
+bool MainWindow::modifyExecutablesDialog(int selection)
{
bool result = false;
try {
- EditExecutablesDialog dialog(m_OrganizerCore, this);
+ EditExecutablesDialog dialog(m_OrganizerCore, selection, this);
result = (dialog.exec() == QDialog::Accepted);
@@ -2398,8 +2434,14 @@ void MainWindow::on_executablesListBox_currentIndexChanged(int index)
m_OldExecutableIndex = index;
if (index == 0) {
- modifyExecutablesDialog();
- ui->executablesListBox->setCurrentIndex(previousIndex);
+ modifyExecutablesDialog(previousIndex - 1);
+ const auto newCount = ui->executablesListBox->count();
+
+ if (previousIndex >= 0 && previousIndex < newCount) {
+ ui->executablesListBox->setCurrentIndex(previousIndex);
+ } else {
+ ui->executablesListBox->setCurrentIndex(newCount - 1);
+ }
}
}
@@ -2474,8 +2516,15 @@ void MainWindow::on_actionAdd_Profile_triggered()
void MainWindow::on_actionModify_Executables_triggered()
{
- if (modifyExecutablesDialog()) {
- ui->executablesListBox->setCurrentIndex(m_OldExecutableIndex);
+ const auto sel = (m_OldExecutableIndex > 0 ? m_OldExecutableIndex - 1 : 0);
+
+ if (modifyExecutablesDialog(sel)) {
+ const auto newCount = ui->executablesListBox->count();
+ if (m_OldExecutableIndex >= 0 && m_OldExecutableIndex < newCount) {
+ ui->executablesListBox->setCurrentIndex(m_OldExecutableIndex);
+ } else {
+ ui->executablesListBox->setCurrentIndex(newCount - 1);
+ }
}
}
@@ -4994,33 +5043,43 @@ void MainWindow::on_savegameList_customContextMenuRequested(const QPoint &pos)
void MainWindow::linkToolbar()
{
- Executable& exe = getSelectedExecutable();
+ Executable* exe = getSelectedExecutable();
+ if (!exe) {
+ return;
+ }
- exe.setShownOnToolbar(!exe.isShownOnToolbar());
+ exe->setShownOnToolbar(!exe->isShownOnToolbar());
updatePinnedExecutables();
}
void MainWindow::linkDesktop()
{
- env::Shortcut(getSelectedExecutable()).toggle(env::Shortcut::Desktop);
+ if (auto* exe=getSelectedExecutable()) {
+ env::Shortcut(*exe).toggle(env::Shortcut::Desktop);
+ }
}
void MainWindow::linkMenu()
{
- env::Shortcut(getSelectedExecutable()).toggle(env::Shortcut::StartMenu);
+ if (auto* exe=getSelectedExecutable()) {
+ env::Shortcut(*exe).toggle(env::Shortcut::StartMenu);
+ }
}
void MainWindow::on_linkButton_pressed()
{
- const Executable& exe = getSelectedExecutable();
+ const Executable* exe = getSelectedExecutable();
+ if (!exe) {
+ return;
+ }
const QIcon addIcon(":/MO/gui/link");
const QIcon removeIcon(":/MO/gui/remove");
- env::Shortcut shortcut(exe);
+ env::Shortcut shortcut(*exe);
m_LinkToolbar->setIcon(
- exe.isShownOnToolbar() ? removeIcon : addIcon);
+ exe->isShownOnToolbar() ? removeIcon : addIcon);
m_LinkDesktop->setIcon(
shortcut.exists(env::Shortcut::Desktop) ? removeIcon : addIcon);
@@ -6363,16 +6422,19 @@ void MainWindow::on_groupCombo_currentIndexChanged(int index)
modFilterActive(m_ModListSortProxy->isFilterActive());
}
-const Executable &MainWindow::getSelectedExecutable() const
+Executable* MainWindow::getSelectedExecutable()
{
- const QString name = ui->executablesListBox->itemText(ui->executablesListBox->currentIndex());
- return m_OrganizerCore.executablesList()->get(name);
-}
+ const QString name = ui->executablesListBox->itemText(
+ ui->executablesListBox->currentIndex());
-Executable &MainWindow::getSelectedExecutable()
-{
- const QString name = ui->executablesListBox->itemText(ui->executablesListBox->currentIndex());
- return m_OrganizerCore.executablesList()->get(name);
+ try
+ {
+ return &m_OrganizerCore.executablesList()->get(name);
+ }
+ catch(std::runtime_error&)
+ {
+ return nullptr;
+ }
}
void MainWindow::on_showHiddenBox_toggled(bool checked)
diff --git a/src/mainwindow.h b/src/mainwindow.h
index b04096be..dbbd0bd9 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -235,7 +235,7 @@ private:
QList<MOBase::IOrganizer::FileInfo> findFileInfos(const QString &path, const std::function<bool (const MOBase::IOrganizer::FileInfo &)> &filter) const;
- bool modifyExecutablesDialog();
+ bool modifyExecutablesDialog(int selection);
void displayModInformation(int row, ModInfoTabIDs tab=ModInfoTabIDs::None);
void testExtractBSA(int modIndex);
@@ -396,8 +396,7 @@ private:
// when painting the count
QIcon m_originalNotificationIcon;
- Executable const &getSelectedExecutable() const;
- Executable &getSelectedExecutable();
+ Executable* getSelectedExecutable();
private slots:
diff --git a/src/modinfo.cpp b/src/modinfo.cpp
index e3daa4fd..6e048bfb 100644
--- a/src/modinfo.cpp
+++ b/src/modinfo.cpp
@@ -61,17 +61,32 @@ bool ModInfo::ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS)
return QString::compare(LHS->name(), RHS->name(), Qt::CaseInsensitive) < 0;
}
+bool ModInfo::isSeparatorName(const QString& name)
+{
+ static QRegExp separatorExp(".*_separator");
+ return separatorExp.exactMatch(name);
+}
+
+bool ModInfo::isBackupName(const QString& name)
+{
+ static QRegExp backupExp(".*backup[0-9]*");
+ return backupExp.exactMatch(name);
+}
+
+bool ModInfo::isRegularName(const QString& name)
+{
+ return !isSeparatorName(name) && !isBackupName(name);
+}
+
ModInfo::Ptr ModInfo::createFrom(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &dir, DirectoryEntry **directoryStructure)
{
QMutexLocker locker(&s_Mutex);
- // int id = s_NextID++;
- static QRegExp backupExp(".*backup[0-9]*");
- static QRegExp separatorExp(".*_separator");
ModInfo::Ptr result;
- if (backupExp.exactMatch(dir.dirName())) {
+
+ if (isBackupName(dir.dirName())) {
result = ModInfo::Ptr(new ModInfoBackup(pluginContainer, game, dir, directoryStructure));
- } else if(separatorExp.exactMatch(dir.dirName())){
+ } else if (isSeparatorName(dir.dirName())) {
result = Ptr(new ModInfoSeparator(pluginContainer, game, dir, directoryStructure));
} else {
result = ModInfo::Ptr(new ModInfoRegular(pluginContainer, game, dir, directoryStructure));
diff --git a/src/modinfo.h b/src/modinfo.h
index e395f45b..30a115c7 100644
--- a/src/modinfo.h
+++ b/src/modinfo.h
@@ -229,6 +229,18 @@ public:
*/
static ModInfo::Ptr createFromPlugin(const QString &modName, const QString &espName, const QStringList &bsaNames, ModInfo::EModType modType, MOShared::DirectoryEntry **directoryStructure, PluginContainer *pluginContainer);
+ // whether the given name is used for separators
+ //
+ static bool isSeparatorName(const QString& name);
+
+ // whether the given name is used for backups
+ //
+ static bool isBackupName(const QString& name);
+
+ // whether the given name is used for regular mods
+ //
+ static bool isRegularName(const QString& name);
+
/**
* @brief retieve a name for one of the CONTENT_ enums
* @param contentType the content value
diff --git a/src/stylesheets/Night Eyes.qss b/src/stylesheets/Night Eyes.qss
index 142acb6a..0430a8c8 100644
--- a/src/stylesheets/Night Eyes.qss
+++ b/src/stylesheets/Night Eyes.qss
@@ -80,6 +80,11 @@ QToolButton:pressed
background: #181818;
}
+QToolButton::menu-indicator
+{
+ width: 8px;
+}
+
/* Left Pane & File Trees ----------------------------------------------------- */
diff --git a/src/stylesheets/vs15 Dark-Green.qss b/src/stylesheets/vs15 Dark-Green.qss
index 9cf26a31..88e7651f 100644
--- a/src/stylesheets/vs15 Dark-Green.qss
+++ b/src/stylesheets/vs15 Dark-Green.qss
@@ -209,6 +209,13 @@ QToolButton {
QToolButton:pressed {
background-color: #009933; }
+QToolButton::menu-indicator {
+ image: url(./vs15/combobox-down.png);
+ subcontrol-origin: padding;
+ subcontrol-position: center right;
+ padding-top: 10%;
+ padding-right: 5%; }
+
/* Group Boxes #QGroupBox */
QGroupBox {
border-color: #3F3F46;
diff --git a/src/stylesheets/vs15 Dark-Orange.qss b/src/stylesheets/vs15 Dark-Orange.qss
index 041f1b00..488da3c4 100644
--- a/src/stylesheets/vs15 Dark-Orange.qss
+++ b/src/stylesheets/vs15 Dark-Orange.qss
@@ -210,6 +210,13 @@ QToolButton {
QToolButton:pressed {
background-color: #CC6600; }
+QToolButton::menu-indicator {
+ image: url(./vs15/combobox-down.png);
+ subcontrol-origin: padding;
+ subcontrol-position: center right;
+ padding-top: 10%;
+ padding-right: 5%; }
+
/* Group Boxes #QGroupBox */
QGroupBox {
border-color: #3F3F46;
diff --git a/src/stylesheets/vs15 Dark-Purple.qss b/src/stylesheets/vs15 Dark-Purple.qss
index bc8bbcde..24c8705a 100644
--- a/src/stylesheets/vs15 Dark-Purple.qss
+++ b/src/stylesheets/vs15 Dark-Purple.qss
@@ -210,6 +210,13 @@ QToolButton {
QToolButton:pressed {
background-color: #7E2AD2; }
+QToolButton::menu-indicator {
+ image: url(./vs15/combobox-down.png);
+ subcontrol-origin: padding;
+ subcontrol-position: center right;
+ padding-top: 10%;
+ padding-right: 5%; }
+
/* Group Boxes #QGroupBox */
QGroupBox {
border-color: #3F3F46;
diff --git a/src/stylesheets/vs15 Dark-Red.qss b/src/stylesheets/vs15 Dark-Red.qss
index 0c9143cd..0c0e21a8 100644
--- a/src/stylesheets/vs15 Dark-Red.qss
+++ b/src/stylesheets/vs15 Dark-Red.qss
@@ -210,6 +210,13 @@ QToolButton {
QToolButton:pressed {
background-color: #990000; }
+QToolButton::menu-indicator {
+ image: url(./vs15/combobox-down.png);
+ subcontrol-origin: padding;
+ subcontrol-position: center right;
+ padding-top: 10%;
+ padding-right: 5%; }
+
/* Group Boxes #QGroupBox */
QGroupBox {
border-color: #3F3F46;
diff --git a/src/stylesheets/vs15 Dark-Yellow.qss b/src/stylesheets/vs15 Dark-Yellow.qss
index 2eb42534..2cf1cb2e 100644
--- a/src/stylesheets/vs15 Dark-Yellow.qss
+++ b/src/stylesheets/vs15 Dark-Yellow.qss
@@ -210,6 +210,13 @@ QToolButton {
QToolButton:pressed {
background-color: #9A9A00; }
+QToolButton::menu-indicator {
+ image: url(./vs15/combobox-down.png);
+ subcontrol-origin: padding;
+ subcontrol-position: center right;
+ padding-top: 10%;
+ padding-right: 5%; }
+
/* Group Boxes #QGroupBox */
QGroupBox {
border-color: #3F3F46;
diff --git a/src/stylesheets/vs15 Dark.qss b/src/stylesheets/vs15 Dark.qss
index d67cce35..a5781d72 100644
--- a/src/stylesheets/vs15 Dark.qss
+++ b/src/stylesheets/vs15 Dark.qss
@@ -209,6 +209,13 @@ QToolButton {
QToolButton:pressed {
background-color: #3399FF; }
+QToolButton::menu-indicator {
+ image: url(./vs15/combobox-down.png);
+ subcontrol-origin: padding;
+ subcontrol-position: center right;
+ padding-top: 10%;
+ padding-right: 5%; }
+
/* Group Boxes #QGroupBox */
QGroupBox {
border-color: #3F3F46;