summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/editexecutablesdialog.cpp57
-rw-r--r--src/editexecutablesdialog.h21
-rw-r--r--src/editexecutablesdialog.ui45
-rw-r--r--src/mainwindow.cpp55
-rw-r--r--src/mainwindow.h1
-rw-r--r--src/organizercore.cpp85
-rw-r--r--src/organizercore.h20
-rw-r--r--src/usvfsconnector.cpp1
9 files changed, 235 insertions, 52 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a8b43c59..c6c038e8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -84,6 +84,7 @@ SET(organizer_SRCS
organizercore.cpp
instancemanager.cpp
usvfsconnector.cpp
+ eventfilter.cpp
shared/inject.cpp
shared/windows_error.cpp
@@ -170,6 +171,7 @@ SET(organizer_HDRS
iuserinterface.h
instancemanager.h
usvfsconnector.h
+ eventfilter.h
shared/inject.h
shared/windows_error.h
diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp
index 05a7603d..42d6f58b 100644
--- a/src/editexecutablesdialog.cpp
+++ b/src/editexecutablesdialog.cpp
@@ -29,15 +29,20 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using namespace MOBase;
using namespace MOShared;
-EditExecutablesDialog::EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent)
+EditExecutablesDialog::EditExecutablesDialog(
+ const ExecutablesList &executablesList, const ModList &modList,
+ Profile *profile, QWidget *parent)
: TutorableDialog("EditExecutables", parent)
, ui(new Ui::EditExecutablesDialog)
, m_CurrentItem(nullptr)
, m_ExecutablesList(executablesList)
+ , m_Profile(profile)
{
ui->setupUi(this);
refreshExecutablesWidget();
+
+ ui->newFilesModBox->addItems(modList.allMods());
}
EditExecutablesDialog::~EditExecutablesDialog()
@@ -86,23 +91,30 @@ void EditExecutablesDialog::resetInput()
ui->appIDOverwriteEdit->clear();
ui->overwriteAppIDBox->setChecked(false);
ui->useAppIconCheckBox->setChecked(false);
+ ui->newFilesModCheckBox->setChecked(false);
m_CurrentItem = nullptr;
}
void EditExecutablesDialog::saveExecutable()
{
- m_ExecutablesList.updateExecutable(ui->titleEdit->text(),
- QDir::fromNativeSeparators(ui->binaryEdit->text()),
- ui->argumentsEdit->text(),
- QDir::fromNativeSeparators(ui->workingDirEdit->text()),
- ui->overwriteAppIDBox->isChecked() ?
- ui->appIDOverwriteEdit->text() : "",
- Executable::UseApplicationIcon | Executable::CustomExecutable,
- (ui->useAppIconCheckBox->isChecked() ?
- Executable::UseApplicationIcon : Executable::Flags())
- | Executable::CustomExecutable);
+ m_ExecutablesList.updateExecutable(
+ ui->titleEdit->text(),
+ QDir::fromNativeSeparators(ui->binaryEdit->text()),
+ ui->argumentsEdit->text(),
+ QDir::fromNativeSeparators(ui->workingDirEdit->text()),
+ ui->overwriteAppIDBox->isChecked() ?
+ ui->appIDOverwriteEdit->text() : "",
+ Executable::UseApplicationIcon | Executable::CustomExecutable,
+ (ui->useAppIconCheckBox->isChecked() ?
+ Executable::UseApplicationIcon : Executable::Flags())
+ | Executable::CustomExecutable);
+
+ if (ui->newFilesModCheckBox->isChecked()) {
+ m_Profile->storeSetting("custom_overwrites", ui->titleEdit->text(),
+ ui->newFilesModBox->currentText());
}
+}
void EditExecutablesDialog::delayedRefresh()
@@ -212,8 +224,13 @@ bool EditExecutablesDialog::executableChanged()
{
if (m_CurrentItem != nullptr) {
Executable const &selectedExecutable(m_ExecutablesList.find(m_CurrentItem->text()));
+
+ QString storedCustomOverwrite = m_Profile->setting("custom_overwrites", selectedExecutable.m_Title).toString();
+
return selectedExecutable.m_Arguments != ui->argumentsEdit->text()
|| selectedExecutable.m_SteamAppID != ui->appIDOverwriteEdit->text()
+ || !storedCustomOverwrite.isEmpty() != ui->newFilesModCheckBox->isChecked()
+ || !storedCustomOverwrite.isEmpty() && (storedCustomOverwrite != ui->newFilesModBox->currentText())
|| selectedExecutable.m_WorkingDirectory != QDir::fromNativeSeparators(ui->workingDirEdit->text())
|| selectedExecutable.m_BinaryInfo.absoluteFilePath() != QDir::fromNativeSeparators(ui->binaryEdit->text())
|| selectedExecutable.usesOwnIcon() != ui->useAppIconCheckBox->isChecked();
@@ -296,5 +313,23 @@ void EditExecutablesDialog::on_executablesListBox_clicked(const QModelIndex &cur
ui->appIDOverwriteEdit->clear();
}
ui->useAppIconCheckBox->setChecked(selectedExecutable.usesOwnIcon());
+
+ int index = -1;
+
+ QString customOverwrite = m_Profile->setting("custom_overwrites", selectedExecutable.m_Title).toString();
+ if (!customOverwrite.isEmpty()) {
+ index = ui->newFilesModBox->findText(customOverwrite);
+ qDebug("find %s -> %d", qPrintable(customOverwrite), index);
+ }
+
+ ui->newFilesModCheckBox->setChecked(index != -1);
+ if (index != -1) {
+ ui->newFilesModBox->setCurrentIndex(index);
+ }
}
}
+
+void EditExecutablesDialog::on_newFilesModCheckBox_toggled(bool checked)
+{
+ ui->newFilesModBox->setEnabled(checked);
+}
diff --git a/src/editexecutablesdialog.h b/src/editexecutablesdialog.h
index 4f6c5315..0f3dbaff 100644
--- a/src/editexecutablesdialog.h
+++ b/src/editexecutablesdialog.h
@@ -24,11 +24,16 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QListWidgetItem>
#include <QTimer>
#include "executableslist.h"
+#include "profile.h"
namespace Ui {
class EditExecutablesDialog;
}
+
+class ModList;
+
+
/**
* @brief Dialog to manage the list of executables
**/
@@ -44,7 +49,10 @@ public:
* @param executablesList current list of executables
* @param parent parent widget
**/
- explicit EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent = 0);
+ explicit EditExecutablesDialog(const ExecutablesList &executablesList,
+ const ModList &modList,
+ Profile *profile,
+ QWidget *parent = 0);
~EditExecutablesDialog();
@@ -56,6 +64,10 @@ public:
ExecutablesList getExecutablesList() const;
void saveExecutable();
+
+private slots:
+ void on_newFilesModCheckBox_toggled(bool checked);
+
private slots:
void on_binaryEdit_textChanged(const QString &arg1);
@@ -89,12 +101,13 @@ private:
bool executableChanged();
private:
- Ui::EditExecutablesDialog *ui;
+ Ui::EditExecutablesDialog *ui;
- QListWidgetItem *m_CurrentItem;
+ QListWidgetItem *m_CurrentItem;
- ExecutablesList m_ExecutablesList;
+ ExecutablesList m_ExecutablesList;
+ Profile *m_Profile;
};
#endif // EDITEXECUTABLESDIALOG_H
diff --git a/src/editexecutablesdialog.ui b/src/editexecutablesdialog.ui
index 2640bb15..b3543c95 100644
--- a/src/editexecutablesdialog.ui
+++ b/src/editexecutablesdialog.ui
@@ -165,6 +165,27 @@ Right now the only case I know of where this needs to be overwritten is for the
</layout>
</item>
<item>
+ <layout class="QHBoxLayout" name="horizontalLayout_9" stretch="0,1">
+ <item>
+ <widget class="QCheckBox" name="newFilesModCheckBox">
+ <property name="toolTip">
+ <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>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="newFilesModBox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
<widget class="QCheckBox" name="useAppIconCheckBox">
<property name="text">
<string>Use Application's Icon for shortcuts</string>
@@ -172,6 +193,13 @@ Right now the only case I know of where this needs to be overwritten is for the
</widget>
</item>
<item>
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>(*) This setting is profile-specific</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="addButton">
@@ -235,6 +263,23 @@ Right now the only case I know of where this needs to be overwritten is for the
</item>
</layout>
</widget>
+ <tabstops>
+ <tabstop>executablesListBox</tabstop>
+ <tabstop>titleEdit</tabstop>
+ <tabstop>binaryEdit</tabstop>
+ <tabstop>browseButton</tabstop>
+ <tabstop>workingDirEdit</tabstop>
+ <tabstop>browseDirButton</tabstop>
+ <tabstop>argumentsEdit</tabstop>
+ <tabstop>overwriteAppIDBox</tabstop>
+ <tabstop>appIDOverwriteEdit</tabstop>
+ <tabstop>newFilesModCheckBox</tabstop>
+ <tabstop>newFilesModBox</tabstop>
+ <tabstop>useAppIconCheckBox</tabstop>
+ <tabstop>addButton</tabstop>
+ <tabstop>removeButton</tabstop>
+ <tabstop>closeButton</tabstop>
+ </tabstops>
<resources/>
<connections/>
</ui>
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index e174a17c..7944acfc 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1004,13 +1004,8 @@ void MainWindow::startExeAction()
{
QAction *action = qobject_cast<QAction*>(sender());
if (action != nullptr) {
- const Executable &selectedExecutable(m_OrganizerCore.executablesList()->find(action->text()));
m_OrganizerCore.spawnBinary(
- selectedExecutable.m_BinaryInfo,
- selectedExecutable.m_Arguments,
- selectedExecutable.m_WorkingDirectory.length() != 0 ? selectedExecutable.m_WorkingDirectory
- : selectedExecutable.m_BinaryInfo.absolutePath(),
- selectedExecutable.m_SteamAppID);
+ m_OrganizerCore.executablesList()->find(action->text()));
} else {
qCritical("not an action?");
}
@@ -1516,14 +1511,7 @@ void MainWindow::installMod(QString fileName)
void MainWindow::on_startButton_clicked()
{
- const Executable &selectedExecutable(getSelectedExecutable());
-
- m_OrganizerCore.spawnBinary(
- selectedExecutable.m_BinaryInfo,
- selectedExecutable.m_Arguments,
- selectedExecutable.m_WorkingDirectory.length() != 0 ? selectedExecutable.m_WorkingDirectory
- : selectedExecutable.m_BinaryInfo.absolutePath(),
- selectedExecutable.m_SteamAppID);
+ m_OrganizerCore.spawnBinary(getSelectedExecutable());
}
@@ -1613,7 +1601,9 @@ bool MainWindow::modifyExecutablesDialog()
{
bool result = false;
try {
- EditExecutablesDialog dialog(*m_OrganizerCore.executablesList());
+ EditExecutablesDialog dialog(*m_OrganizerCore.executablesList(),
+ *m_OrganizerCore.modList(),
+ m_OrganizerCore.currentProfile());
if (dialog.exec() == QDialog::Accepted) {
m_OrganizerCore.setExecutablesList(dialog.getExecutablesList());
result = true;
@@ -2423,7 +2413,32 @@ void MainWindow::information_clicked()
}
}
+void MainWindow::createEmptyMod_clicked()
+{
+ GuessedValue<QString> name;
+ name.setFilter(&fixDirectoryName);
+
+ while (name->isEmpty()) {
+ bool ok;
+ name.update(QInputDialog::getText(this, tr("Create Mod..."),
+ tr("This will create an empty mod.\n"
+ "Please enter a name:"), QLineEdit::Normal, "", &ok),
+ GUESS_USER);
+ if (!ok) {
+ return;
+ }
+ }
+
+ if (m_OrganizerCore.getMod(name) != nullptr) {
+ reportError(tr("A mod with this name already exists"));
+ return;
+ }
+ IModInterface *newMod = m_OrganizerCore.createMod(name);
+ if (newMod == nullptr) {
+ return;
+ }
+}
void MainWindow::createModFromOverwrite()
{
@@ -2862,6 +2877,8 @@ QMenu *MainWindow::modListContextMenu()
QMenu *menu = new QMenu(this);
menu->addAction(tr("Install Mod..."), this, SLOT(installMod_clicked()));
+ menu->addAction(tr("Create empty mod"), this, SLOT(createEmptyMod_clicked()));
+
menu->addAction(tr("Enable all visible"), this, SLOT(enableVisibleMods()));
menu->addAction(tr("Disable all visible"), this, SLOT(disableVisibleMods()));
@@ -3529,10 +3546,14 @@ void MainWindow::openDataFile()
QString arguments;
switch (getBinaryExecuteInfo(targetInfo, binaryInfo, arguments)) {
case 1: {
- m_OrganizerCore.spawnBinaryDirect(binaryInfo, arguments, m_OrganizerCore.currentProfile()->name(), targetInfo.absolutePath(), "");
+ m_OrganizerCore.spawnBinaryDirect(
+ binaryInfo, arguments, m_OrganizerCore.currentProfile()->name(),
+ targetInfo.absolutePath(), "", "");
} break;
case 2: {
- ::ShellExecuteW(nullptr, L"open", ToWString(targetInfo.absoluteFilePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
+ ::ShellExecuteW(nullptr, L"open",
+ ToWString(targetInfo.absoluteFilePath()).c_str(),
+ nullptr, nullptr, SW_SHOWNORMAL);
} break;
default: {
// nop
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 4e2e8e21..d2f3fb47 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -369,6 +369,7 @@ private slots:
// modlist context menu
void installMod_clicked();
+ void createEmptyMod_clicked();
void restoreBackup_clicked();
void renameMod_clicked();
void removeMod_clicked();
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index b1eaaef3..8f308c88 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -596,7 +596,7 @@ void OrganizerCore::createDefaultProfile()
void OrganizerCore::prepareVFS()
{
- m_USVFS.updateMapping(fileMapping());
+ m_USVFS.updateMapping(fileMapping(m_CurrentProfile->name(), QString()));
}
void OrganizerCore::setCurrentProfile(const QString &profileName)
@@ -969,10 +969,21 @@ QStringList OrganizerCore::modsSortedByProfilePriority() const
return res;
}
+void OrganizerCore::spawnBinary(const Executable &exe)
+{
+ spawnBinary(
+ exe.m_BinaryInfo, exe.m_Arguments,
+ exe.m_WorkingDirectory.length() != 0 ? exe.m_WorkingDirectory
+ : exe.m_BinaryInfo.absolutePath(),
+ exe.m_SteamAppID,
+ m_CurrentProfile->setting("custom_overwrites", exe.m_Title).toString());
+}
+
void OrganizerCore::spawnBinary(const QFileInfo &binary,
const QString &arguments,
const QDir &currentDirectory,
- const QString &steamAppID)
+ const QString &steamAppID,
+ const QString &customOverwrite)
{
LockedDialog *dialog = new LockedDialog(qApp->activeWindow());
dialog->show();
@@ -983,7 +994,7 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary,
HANDLE processHandle
= spawnBinaryDirect(binary, arguments, m_CurrentProfile->name(),
- currentDirectory, steamAppID);
+ currentDirectory, steamAppID, customOverwrite);
if (processHandle != INVALID_HANDLE_VALUE) {
if (m_UserInterface != nullptr) {
m_UserInterface->setWindowEnabled(false);
@@ -1082,7 +1093,8 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary,
const QString &arguments,
const QString &profileName,
const QDir &currentDirectory,
- const QString &steamAppID)
+ const QString &steamAppID,
+ const QString &customOverwrite)
{
prepareStart();
@@ -1135,11 +1147,16 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary,
}
if (m_AboutToRun(binary.absoluteFilePath())) {
- m_USVFS.updateMapping(fileMapping());
+ try {
+ m_USVFS.updateMapping(fileMapping(profileName, customOverwrite));
+ } catch (const std::exception &e) {
+ QMessageBox::warning(qApp->activeWindow(), tr("Error"), e.what());
+ return INVALID_HANDLE_VALUE;
+ }
+
QString modsPath = settings().getModDirectory();
QString binPath = binary.absoluteFilePath();
-
if (binPath.startsWith(modsPath, Qt::CaseInsensitive)) {
// binary was installed as a MO mod. Need to start it through a (hooked)
// proxy to ensure pathes are correct
@@ -1185,6 +1202,7 @@ HANDLE OrganizerCore::startApplication(const QString &executable,
}
}
QString steamAppID;
+ QString customOverwrite;
if (executable.contains('\\') || executable.contains('/')) {
// file path
@@ -1200,6 +1218,9 @@ HANDLE OrganizerCore::startApplication(const QString &executable,
try {
const Executable &exe = m_ExecutablesList.findByBinary(binary);
steamAppID = exe.m_SteamAppID;
+ customOverwrite
+ = m_CurrentProfile->setting("custom_overwrites", exe.m_Title)
+ .toString();
} catch (const std::runtime_error &) {
// nop
}
@@ -1208,6 +1229,9 @@ HANDLE OrganizerCore::startApplication(const QString &executable,
try {
const Executable &exe = m_ExecutablesList.find(executable);
steamAppID = exe.m_SteamAppID;
+ customOverwrite
+ = m_CurrentProfile->setting("custom_overwrites", exe.m_Title)
+ .toString();
if (arguments == "") {
arguments = exe.m_Arguments;
}
@@ -1223,7 +1247,7 @@ HANDLE OrganizerCore::startApplication(const QString &executable,
}
return spawnBinaryDirect(binary, arguments, profileName, currentDirectory,
- steamAppID);
+ steamAppID, customOverwrite);
}
bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode)
@@ -1781,7 +1805,8 @@ void OrganizerCore::prepareStart()
storeSettings();
}
-std::vector<Mapping> OrganizerCore::fileMapping()
+std::vector<Mapping> OrganizerCore::fileMapping(const QString &profileName,
+ const QString &customOverwrite)
{
// need to wait until directory structure
while (m_DirectoryUpdate) {
@@ -1789,12 +1814,39 @@ std::vector<Mapping> OrganizerCore::fileMapping()
QCoreApplication::processEvents();
}
- int overwriteId = m_DirectoryStructure->getOriginByName(L"Overwrite").getID();
-
IPluginGame *game = qApp->property("managed_game").value<IPluginGame *>();
- MappingType result = fileMapping(
- QDir::toNativeSeparators(game->dataDirectory().absolutePath()), "\\",
- directoryStructure(), directoryStructure(), overwriteId);
+ Profile profile(QDir(m_Settings.getProfileDirectory() + "/" + profileName),
+ game);
+
+ MappingType result;
+
+ QString dataPath
+ = QDir::toNativeSeparators(game->dataDirectory().absolutePath());
+
+ bool overwriteActive = false;
+
+ for (auto mod : profile.getActiveMods()) {
+ if (std::get<0>(mod).compare("overwrite", Qt::CaseInsensitive) == 0) {
+ continue;
+ }
+
+ unsigned int modIndex = ModInfo::getIndex(std::get<0>(mod));
+ ModInfo::Ptr modPtr = ModInfo::getByIndex(modIndex);
+
+ bool createTarget = customOverwrite == std::get<0>(mod);
+
+ overwriteActive |= createTarget;
+
+ if (modPtr->isRegular()) {
+ result.insert(result.end(), {QDir::toNativeSeparators(std::get<1>(mod)),
+ dataPath, true, createTarget});
+ }
+ }
+
+ if (!overwriteActive && !customOverwrite.isEmpty()) {
+ throw MyException(tr("The designated write target \"%1\" is not enabled.")
+ .arg(customOverwrite));
+ }
if (m_CurrentProfile->localSavesEnabled()) {
LocalSavegames *localSaves = game->feature<LocalSavegames>();
@@ -1808,6 +1860,13 @@ std::vector<Mapping> OrganizerCore::fileMapping()
}
}
+ result.insert(result.end(), {
+ QDir::toNativeSeparators(m_Settings.getOverwriteDirectory()),
+ QDir::toNativeSeparators(game->dataDirectory().absolutePath()),
+ true,
+ customOverwrite.isEmpty()
+ });
+
for (MOBase::IPluginFileMapper *mapper :
m_PluginContainer->plugins<MOBase::IPluginFileMapper>()) {
IPlugin *plugin = dynamic_cast<IPlugin *>(mapper);
diff --git a/src/organizercore.h b/src/organizercore.h
index ea37d72c..cd7af1b8 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -129,8 +129,8 @@ public:
void doAfterLogin(const std::function<void()> &function) { m_PostLoginTasks.append(function); }
- void spawnBinary(const QFileInfo &binary, const QString &arguments = "", const QDir &currentDirectory = QDir(), const QString &steamAppID = "");
- HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir &currentDirectory, const QString &steamAppID);
+ void spawnBinary(const Executable &exe);
+ HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir &currentDirectory, const QString &steamAppID, const QString &customOverwrite);
void loginSuccessfulUpdate(bool necessary);
void loginFailedUpdate(const QString &message);
@@ -173,11 +173,6 @@ public:
void refreshModList(bool saveChanges = true);
QStringList modsSortedByProfilePriority() const;
- /**
- * @brief return a descriptor of the mappings real file->virtual file
- */
- std::vector<Mapping> fileMapping();
-
public: // IPluginDiagnose interface
virtual std::vector<unsigned int> activeProblems() const;
@@ -231,12 +226,23 @@ private:
bool testForSteam();
+ /**
+ * @brief return a descriptor of the mappings real file->virtual file
+ */
+ std::vector<Mapping> fileMapping(const QString &profile,
+ const QString &customOverwrite);
+
std::vector<Mapping>
fileMapping(const QString &dataPath, const QString &relPath,
const MOShared::DirectoryEntry *base,
const MOShared::DirectoryEntry *directoryEntry,
int createDestination);
+ void spawnBinary(const QFileInfo &binary, const QString &arguments = "",
+ const QDir &currentDirectory = QDir(),
+ const QString &steamAppID = "",
+ const QString &customOverwrite = "");
+
private slots:
void directory_refreshed();
diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp
index bc818685..2af28522 100644
--- a/src/usvfsconnector.cpp
+++ b/src/usvfsconnector.cpp
@@ -136,6 +136,7 @@ UsvfsConnector::~UsvfsConnector()
m_WorkerThread.wait();
}
+
void UsvfsConnector::updateMapping(const MappingType &mapping)
{
QProgressDialog progress;