mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
Compare commits
68
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4573d111a2 | ||
|
|
84ec954073 | ||
|
|
63188eb6f3 | ||
|
|
0a51fdf08e | ||
|
|
cb315b4179 | ||
|
|
d96d904765 | ||
|
|
9588667404 | ||
|
|
b008753e1d | ||
|
|
3c7ab3b668 | ||
|
|
d85019f4b1 | ||
|
|
05edcf8840 | ||
|
|
2a22461f14 | ||
|
|
0c3c822d77 | ||
|
|
f0ee9c3e4d | ||
|
|
83edfe0c96 | ||
|
|
86d833bcc8 | ||
|
|
58198a6bfd | ||
|
|
32339addbf | ||
|
|
dd875c05de | ||
|
|
17938b503a | ||
|
|
269d4f6765 | ||
|
|
0fa7f7469f | ||
|
|
ea1c2b46fe | ||
|
|
89f3c0d65a | ||
|
|
821f95b8ab | ||
|
|
716dbcc97d | ||
|
|
8e7ac4e064 | ||
|
|
69a8a40b99 | ||
|
|
229161565e | ||
|
|
9c3b390f08 | ||
|
|
5244544424 | ||
|
|
217ca284fe | ||
|
|
0aecc3427a | ||
|
|
b6627ff5e0 | ||
|
|
b0d86c9e01 | ||
|
|
a7d6eb75c2 | ||
|
|
6f09cf2205 | ||
|
|
e6abe486a7 | ||
|
|
52fcfaa292 | ||
|
|
4230313ef5 | ||
|
|
ff8057d34b | ||
|
|
b098214860 | ||
|
|
8cf429a1dd | ||
|
|
9f7bec01d0 | ||
|
|
643a50ad5a | ||
|
|
eaaa1b312d | ||
|
|
9c17ecc87e | ||
|
|
051589c38e | ||
|
|
48fae44566 | ||
|
|
e1c8d00979 | ||
|
|
0503097494 | ||
|
|
3a39f90870 | ||
|
|
18ad656586 | ||
|
|
eaafc489fc | ||
|
|
7799c348ba | ||
|
|
0a1f3efac5 | ||
|
|
1eea37e4e1 | ||
|
|
9a5965afa7 | ||
|
|
7853ba5f9a | ||
|
|
90dcc81715 | ||
|
|
1e75fd8dae | ||
|
|
61033814fd | ||
|
|
ca591dbd23 | ||
|
|
7602fef6e7 | ||
|
|
67b1f9dca2 | ||
|
|
5ae279d8bb | ||
|
|
81ef3d0a81 | ||
|
|
dc0e67a8b3 |
@@ -76,11 +76,12 @@ void DirectoryRefresher::cleanStructure(DirectoryEntry *structure)
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryRefresher::addModBSAToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &archives)
|
||||
void DirectoryRefresher::addModBSAToStructure(DirectoryEntry *directoryStructure, const QString &modName,
|
||||
int priority, const QString &directory, const QStringList &archives)
|
||||
{
|
||||
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
|
||||
|
||||
foreach (const QString &archive, archives) {
|
||||
for (const QString &archive : archives) {
|
||||
QFileInfo fileInfo(archive);
|
||||
if (m_EnabledArchives.find(fileInfo.fileName()) != m_EnabledArchives.end()) {
|
||||
try {
|
||||
@@ -93,7 +94,8 @@ void DirectoryRefresher::addModBSAToStructure(DirectoryEntry *directoryStructure
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryRefresher::addModFilesToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles)
|
||||
void DirectoryRefresher::addModFilesToStructure(DirectoryEntry *directoryStructure, const QString &modName,
|
||||
int priority, const QString &directory, const QStringList &stealFiles)
|
||||
{
|
||||
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
|
||||
|
||||
|
||||
@@ -30,8 +30,10 @@ using namespace MOBase;
|
||||
using namespace MOShared;
|
||||
|
||||
EditExecutablesDialog::EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent)
|
||||
: TutorableDialog("EditExecutables", parent),
|
||||
ui(new Ui::EditExecutablesDialog), m_CurrentItem(nullptr), m_ExecutablesList(executablesList)
|
||||
: TutorableDialog("EditExecutables", parent)
|
||||
, ui(new Ui::EditExecutablesDialog)
|
||||
, m_CurrentItem(nullptr)
|
||||
, m_ExecutablesList(executablesList)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@@ -47,7 +49,7 @@ ExecutablesList EditExecutablesDialog::getExecutablesList() const
|
||||
{
|
||||
ExecutablesList newList;
|
||||
for (int i = 0; i < ui->executablesListBox->count(); ++i) {
|
||||
newList.addExecutable(ui->executablesListBox->item(i)->data(Qt::UserRole).value<Executable>());
|
||||
newList.addExecutable(m_ExecutablesList.find(ui->executablesListBox->item(i)->text()));
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
@@ -60,10 +62,7 @@ void EditExecutablesDialog::refreshExecutablesWidget()
|
||||
|
||||
for(; current != end; ++current) {
|
||||
QListWidgetItem *newItem = new QListWidgetItem(current->m_Title);
|
||||
QVariant temp;
|
||||
temp.setValue(*current);
|
||||
newItem->setData(Qt::UserRole, temp);
|
||||
newItem->setTextColor(current->m_Custom ? QColor(Qt::black) : QColor(Qt::darkGray));
|
||||
newItem->setTextColor(current->isCustom() ? QColor(Qt::black) : QColor(Qt::darkGray));
|
||||
ui->executablesListBox->addItem(newItem);
|
||||
}
|
||||
|
||||
@@ -72,9 +71,9 @@ void EditExecutablesDialog::refreshExecutablesWidget()
|
||||
}
|
||||
|
||||
|
||||
void EditExecutablesDialog::on_binaryEdit_textChanged(const QString &arg1)
|
||||
void EditExecutablesDialog::on_binaryEdit_textChanged(const QString &name)
|
||||
{
|
||||
QFileInfo fileInfo(arg1);
|
||||
QFileInfo fileInfo(name);
|
||||
ui->addButton->setEnabled(fileInfo.exists() && fileInfo.isFile());
|
||||
}
|
||||
|
||||
@@ -87,19 +86,27 @@ void EditExecutablesDialog::resetInput()
|
||||
ui->appIDOverwriteEdit->clear();
|
||||
ui->overwriteAppIDBox->setChecked(false);
|
||||
ui->closeCheckBox->setChecked(false);
|
||||
ui->useAppIconCheckBox->setChecked(false);
|
||||
m_CurrentItem = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void EditExecutablesDialog::saveExecutable()
|
||||
{
|
||||
m_ExecutablesList.addExecutable(ui->titleEdit->text(), QDir::fromNativeSeparators(ui->binaryEdit->text()),
|
||||
ui->argumentsEdit->text(), QDir::fromNativeSeparators(ui->workingDirEdit->text()),
|
||||
(ui->closeCheckBox->checkState() == Qt::Checked) ? ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE
|
||||
: ExecutableInfo::CloseMOStyle::DEFAULT_STAY,
|
||||
ui->overwriteAppIDBox->isChecked() ? ui->appIDOverwriteEdit->text() : "",
|
||||
true, false);
|
||||
}
|
||||
m_ExecutablesList.updateExecutable(ui->titleEdit->text(),
|
||||
QDir::fromNativeSeparators(ui->binaryEdit->text()),
|
||||
ui->argumentsEdit->text(),
|
||||
QDir::fromNativeSeparators(ui->workingDirEdit->text()),
|
||||
(ui->closeCheckBox->checkState() == Qt::Checked) ?
|
||||
ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE
|
||||
: ExecutableInfo::CloseMOStyle::DEFAULT_STAY,
|
||||
ui->overwriteAppIDBox->isChecked() ?
|
||||
ui->appIDOverwriteEdit->text() : "",
|
||||
Executable::UseApplicationIcon | Executable::CustomExecutable,
|
||||
(ui->useAppIconCheckBox->isChecked() ?
|
||||
Executable::UseApplicationIcon : Executable::Flags())
|
||||
| Executable::CustomExecutable);
|
||||
}
|
||||
|
||||
|
||||
void EditExecutablesDialog::delayedRefresh()
|
||||
@@ -113,7 +120,9 @@ void EditExecutablesDialog::delayedRefresh()
|
||||
|
||||
void EditExecutablesDialog::on_addButton_clicked()
|
||||
{
|
||||
saveExecutable();
|
||||
if (executableChanged()) {
|
||||
saveExecutable();
|
||||
}
|
||||
|
||||
resetInput();
|
||||
refreshExecutablesWidget();
|
||||
@@ -170,8 +179,6 @@ void EditExecutablesDialog::on_browseDirButton_clicked()
|
||||
|
||||
void EditExecutablesDialog::on_removeButton_clicked()
|
||||
{
|
||||
// QLineEdit *binaryEdit = findChild<QLineEdit*>("binaryEdit");
|
||||
|
||||
if (QMessageBox::question(this, tr("Confirm"), tr("Really remove \"%1\" from executables?").arg(ui->titleEdit->text()),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
m_ExecutablesList.remove(ui->titleEdit->text());
|
||||
@@ -206,15 +213,19 @@ void EditExecutablesDialog::on_titleEdit_textChanged(const QString &arg1)
|
||||
bool EditExecutablesDialog::executableChanged()
|
||||
{
|
||||
if (m_CurrentItem != nullptr) {
|
||||
const Executable &selectedExecutable = m_CurrentItem->data(Qt::UserRole).value<Executable>();
|
||||
|
||||
Executable const &selectedExecutable(m_ExecutablesList.find(m_CurrentItem->text()));
|
||||
return selectedExecutable.m_Arguments != ui->argumentsEdit->text()
|
||||
|| selectedExecutable.m_SteamAppID != ui->appIDOverwriteEdit->text()
|
||||
|| selectedExecutable.m_WorkingDirectory != QDir::fromNativeSeparators(ui->workingDirEdit->text())
|
||||
|| selectedExecutable.m_BinaryInfo.absoluteFilePath() != QDir::fromNativeSeparators(ui->binaryEdit->text())
|
||||
|| (selectedExecutable.m_CloseMO == ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE) != ui->closeCheckBox->isChecked();
|
||||
|| (selectedExecutable.m_CloseMO == ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE) != ui->closeCheckBox->isChecked()
|
||||
|| selectedExecutable.usesOwnIcon() != ui->useAppIconCheckBox->isChecked();
|
||||
} else {
|
||||
return false;
|
||||
QFileInfo fileInfo(ui->binaryEdit->text());
|
||||
return !ui->binaryEdit->text().isEmpty()
|
||||
&& !ui->titleEdit->text().isEmpty()
|
||||
&& fileInfo.exists()
|
||||
&& fileInfo.isFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +283,7 @@ void EditExecutablesDialog::on_executablesListBox_clicked(const QModelIndex &cur
|
||||
|
||||
m_CurrentItem = ui->executablesListBox->item(current.row());
|
||||
|
||||
const Executable &selectedExecutable = m_CurrentItem->data(Qt::UserRole).value<Executable>();
|
||||
Executable const &selectedExecutable(m_ExecutablesList.find(m_CurrentItem->text()));
|
||||
|
||||
ui->titleEdit->setText(selectedExecutable.m_Title);
|
||||
ui->binaryEdit->setText(QDir::toNativeSeparators(selectedExecutable.m_BinaryInfo.absoluteFilePath()));
|
||||
@@ -286,12 +297,13 @@ void EditExecutablesDialog::on_executablesListBox_clicked(const QModelIndex &cur
|
||||
ui->closeCheckBox->setEnabled(true);
|
||||
ui->closeCheckBox->setToolTip(tr("If checked, MO will be closed once the specified executable is run."));
|
||||
}
|
||||
ui->removeButton->setEnabled(selectedExecutable.m_Custom);
|
||||
ui->removeButton->setEnabled(selectedExecutable.isCustom());
|
||||
ui->overwriteAppIDBox->setChecked(!selectedExecutable.m_SteamAppID.isEmpty());
|
||||
if (!selectedExecutable.m_SteamAppID.isEmpty()) {
|
||||
ui->appIDOverwriteEdit->setText(selectedExecutable.m_SteamAppID);
|
||||
} else {
|
||||
ui->appIDOverwriteEdit->clear();
|
||||
}
|
||||
ui->useAppIconCheckBox->setChecked(selectedExecutable.usesOwnIcon());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>384</width>
|
||||
<height>362</height>
|
||||
<height>446</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -164,21 +164,28 @@ Right now the only case I know of where this needs to be overwritten is for the
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="closeCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>If checked, MO will be closed once the specified executable is run.</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>If checked, MO will be closed once the specified executable is run.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close MO when started</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useAppIconCheckBox">
|
||||
<property name="text">
|
||||
<string>Use Application's Icon for shortcuts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="closeCheckBox">
|
||||
<property name="toolTip">
|
||||
<string>If checked, MO will be closed once the specified executable is run.</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>If checked, MO will be closed once the specified executable is run.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close MO when started</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addButton">
|
||||
<property name="toolTip">
|
||||
@@ -240,6 +247,10 @@ Right now the only case I know of where this needs to be overwritten is for the
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>executablesListBox</zorder>
|
||||
<zorder>closeButton</zorder>
|
||||
<zorder>closeCheckBox</zorder>
|
||||
<zorder>useAppIconCheckBox</zorder>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
+37
-74
@@ -30,39 +30,6 @@ using namespace MOBase;
|
||||
using namespace MOShared;
|
||||
|
||||
|
||||
static QDataStream &operator<<(QDataStream &out, const Executable &obj)
|
||||
{
|
||||
out << obj.m_Title
|
||||
<< obj.m_BinaryInfo.absoluteFilePath()
|
||||
<< obj.m_Arguments
|
||||
<< static_cast<std::underlying_type<ExecutableInfo::CloseMOStyle>::type>(obj.m_CloseMO)
|
||||
<< obj.m_SteamAppID
|
||||
<< obj.m_WorkingDirectory
|
||||
<< obj.m_Custom
|
||||
<< obj.m_Toolbar;
|
||||
return out;
|
||||
}
|
||||
|
||||
static QDataStream &operator>>(QDataStream &in, Executable &obj)
|
||||
{
|
||||
QString binaryTemp;
|
||||
int closeStyleTemp;
|
||||
in >> obj.m_Title >> binaryTemp >> obj.m_Arguments >> closeStyleTemp
|
||||
>> obj.m_SteamAppID >> obj.m_WorkingDirectory >> obj.m_Custom >> obj.m_Toolbar;
|
||||
|
||||
obj.m_CloseMO = static_cast<ExecutableInfo::CloseMOStyle>(closeStyleTemp);
|
||||
obj.m_BinaryInfo.setFile(binaryTemp);
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
void registerExecutable()
|
||||
{
|
||||
qRegisterMetaType<Executable>("Executable");
|
||||
qRegisterMetaTypeStreamOperators<Executable>("Executable");
|
||||
}
|
||||
|
||||
|
||||
ExecutablesList::ExecutablesList()
|
||||
{
|
||||
}
|
||||
@@ -102,23 +69,23 @@ void ExecutablesList::getExecutables(std::vector<Executable>::const_iterator &be
|
||||
|
||||
const Executable &ExecutablesList::find(const QString &title) const
|
||||
{
|
||||
for (std::vector<Executable>::const_iterator iter = m_Executables.begin(); iter != m_Executables.end(); ++iter) {
|
||||
if (iter->m_Title == title) {
|
||||
return *iter;
|
||||
for (Executable const &exe : m_Executables) {
|
||||
if (exe.m_Title == title) {
|
||||
return exe;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("invalid name");
|
||||
throw std::runtime_error(QString("invalid name %1").arg(title).toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
|
||||
Executable &ExecutablesList::find(const QString &title)
|
||||
{
|
||||
for (Executable &exe : m_Executables) {
|
||||
if (QString::compare(exe.m_Title, title, Qt::CaseInsensitive) == 0) {
|
||||
if (exe.m_Title == title) {
|
||||
return exe;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("invalid name");
|
||||
throw std::runtime_error(QString("invalid name %1").arg(title).toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
|
||||
@@ -162,42 +129,34 @@ void ExecutablesList::addExecutable(const Executable &executable)
|
||||
}
|
||||
|
||||
|
||||
void ExecutablesList::position(const QString &title, bool toolbar, int pos)
|
||||
{
|
||||
auto existingExe = findExe(title);
|
||||
if (existingExe != m_Executables.end()) {
|
||||
Executable temp = *existingExe;
|
||||
temp.m_Toolbar = toolbar;
|
||||
m_Executables.erase(existingExe);
|
||||
m_Executables.insert(m_Executables.begin() + pos, temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ExecutablesList::addExecutable(const QString &title, const QString &executableName, const QString &arguments,
|
||||
const QString &workingDirectory, ExecutableInfo::CloseMOStyle closeMO, const QString &steamAppID,
|
||||
bool custom, bool toolbar, int pos)
|
||||
void ExecutablesList::updateExecutable(const QString &title,
|
||||
const QString &executableName,
|
||||
const QString &arguments,
|
||||
const QString &workingDirectory,
|
||||
ExecutableInfo::CloseMOStyle closeMO,
|
||||
const QString &steamAppID,
|
||||
Executable::Flags mask,
|
||||
Executable::Flags flags)
|
||||
{
|
||||
QFileInfo file(executableName);
|
||||
auto existingExe = findExe(title);
|
||||
flags &= mask;
|
||||
|
||||
if (existingExe != m_Executables.end()) {
|
||||
existingExe->m_Title = title;
|
||||
existingExe->m_Custom = custom;
|
||||
existingExe->m_CloseMO = closeMO;
|
||||
existingExe->m_Toolbar = toolbar;
|
||||
if (custom) {
|
||||
// for pre-configured executables don't overwrite settings we didn't store
|
||||
existingExe->m_BinaryInfo = file;
|
||||
existingExe->m_Flags &= ~mask;
|
||||
existingExe->m_Flags |= flags;
|
||||
// for pre-configured executables don't overwrite settings we didn't store
|
||||
if (flags & Executable::CustomExecutable) {
|
||||
if (file.exists()) {
|
||||
// don't overwrite a valid binary with an invalid one
|
||||
existingExe->m_BinaryInfo = file;
|
||||
}
|
||||
existingExe->m_Arguments = arguments;
|
||||
existingExe->m_WorkingDirectory = workingDirectory;
|
||||
existingExe->m_SteamAppID = steamAppID;
|
||||
}
|
||||
if (pos >= 0) {
|
||||
Executable temp = *existingExe;
|
||||
m_Executables.erase(existingExe);
|
||||
m_Executables.insert(m_Executables.begin() + pos, temp);
|
||||
}
|
||||
} else {
|
||||
Executable newExe;
|
||||
newExe.m_Title = title;
|
||||
@@ -206,13 +165,8 @@ void ExecutablesList::addExecutable(const QString &title, const QString &executa
|
||||
newExe.m_Arguments = arguments;
|
||||
newExe.m_WorkingDirectory = workingDirectory;
|
||||
newExe.m_SteamAppID = steamAppID;
|
||||
newExe.m_Custom = true;
|
||||
newExe.m_Toolbar = toolbar;
|
||||
if ((pos < 0) || (pos >= static_cast<int>(m_Executables.size()))) {
|
||||
m_Executables.push_back(newExe);
|
||||
} else {
|
||||
m_Executables.insert(m_Executables.begin() + pos, newExe);
|
||||
}
|
||||
newExe.m_Flags = Executable::CustomExecutable | flags;
|
||||
m_Executables.push_back(newExe);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,7 +174,7 @@ void ExecutablesList::addExecutable(const QString &title, const QString &executa
|
||||
void ExecutablesList::remove(const QString &title)
|
||||
{
|
||||
for (std::vector<Executable>::iterator iter = m_Executables.begin(); iter != m_Executables.end(); ++iter) {
|
||||
if (iter->m_Custom && (iter->m_Title == title)) {
|
||||
if (iter->isCustom() && (iter->m_Title == title)) {
|
||||
m_Executables.erase(iter);
|
||||
break;
|
||||
}
|
||||
@@ -241,8 +195,17 @@ void ExecutablesList::addExecutableInternal(const QString &title, const QString
|
||||
newExe.m_Arguments = arguments;
|
||||
newExe.m_WorkingDirectory = workingDirectory;
|
||||
newExe.m_SteamAppID = steamAppID;
|
||||
newExe.m_Custom = false;
|
||||
newExe.m_Toolbar = false;
|
||||
newExe.m_Flags = 0;
|
||||
m_Executables.push_back(newExe);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Executable::showOnToolbar(bool state)
|
||||
{
|
||||
if (state) {
|
||||
m_Flags |= ShowInToolbar;
|
||||
} else {
|
||||
m_Flags &= ~ShowInToolbar;
|
||||
}
|
||||
}
|
||||
|
||||
+59
-30
@@ -39,13 +39,26 @@ struct Executable {
|
||||
QString m_SteamAppID;
|
||||
QString m_WorkingDirectory;
|
||||
|
||||
bool m_Custom;
|
||||
bool m_Toolbar;
|
||||
enum Flag {
|
||||
CustomExecutable = 0x01,
|
||||
ShowInToolbar = 0x02,
|
||||
UseApplicationIcon = 0x04,
|
||||
|
||||
AllFlags = 0xff //I know, I know
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(Flags, Flag)
|
||||
|
||||
Flags m_Flags;
|
||||
|
||||
bool isCustom() const { return m_Flags.testFlag(CustomExecutable); }
|
||||
|
||||
bool isShownOnToolbar() const { return m_Flags.testFlag(ShowInToolbar); }
|
||||
|
||||
void showOnToolbar(bool state);
|
||||
|
||||
bool usesOwnIcon() const { return m_Flags.testFlag(UseApplicationIcon); }
|
||||
};
|
||||
Q_DECLARE_METATYPE(Executable)
|
||||
|
||||
|
||||
void registerExecutable();
|
||||
|
||||
|
||||
/*!
|
||||
@@ -68,13 +81,13 @@ public:
|
||||
void init(MOBase::IPluginGame *game);
|
||||
|
||||
/**
|
||||
* @brief retrieve an executable by index
|
||||
* @brief find an executable by its name
|
||||
*
|
||||
* @param index index of the executable to look up
|
||||
* @return reference to the executable
|
||||
* @exception out_of_range will throw an exception if the index is invalid
|
||||
* @param title the title of the executable to look up
|
||||
* @return the executable
|
||||
* @exception runtime_error will throw an exception if the name is not correct
|
||||
**/
|
||||
const Executable &get(int index) const { return m_Executables.at(index); }
|
||||
const Executable &find(const QString &title) const;
|
||||
|
||||
/**
|
||||
* @brief find an executable by its name
|
||||
@@ -83,16 +96,7 @@ public:
|
||||
* @return the executable
|
||||
* @exception runtime_error will throw an exception if the name is not correct
|
||||
**/
|
||||
const Executable &find(const QString &tilte) const;
|
||||
|
||||
/**
|
||||
* @brief find an executable by its name
|
||||
*
|
||||
* @param title the title of the executable to look up
|
||||
* @return the executable
|
||||
* @exception runtime_error will throw an exception if the name is not correct
|
||||
**/
|
||||
Executable &find(const QString &tilte);
|
||||
Executable &find(const QString &title);
|
||||
|
||||
/**
|
||||
* @brief find an executable by a fileinfo structure
|
||||
@@ -123,17 +127,33 @@ public:
|
||||
* @param arguments arguments to pass to the executable
|
||||
* @param closeMO if true, MO will be closed when the binary is started
|
||||
**/
|
||||
void addExecutable(const QString &title, const QString &executableName, const QString &arguments,
|
||||
const QString &workingDirectory, MOBase::ExecutableInfo::CloseMOStyle closeMO, const QString &steamAppID,
|
||||
bool custom, bool toolbar, int pos = -1);
|
||||
void addExecutable(const QString &title,
|
||||
const QString &executableName,
|
||||
const QString &arguments,
|
||||
const QString &workingDirectory,
|
||||
MOBase::ExecutableInfo::CloseMOStyle closeMO,
|
||||
const QString &steamAppID,
|
||||
Executable::Flags flags)
|
||||
{
|
||||
updateExecutable(title, executableName, arguments, workingDirectory, closeMO, steamAppID, Executable::AllFlags, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief change position of an executable which is expected to exist
|
||||
* @param title title of the executable
|
||||
* @param toolbar enable/disable placement on the toolbar
|
||||
* @param pos new position for the executable
|
||||
*/
|
||||
void position(const QString &title, bool toolbar, int pos);
|
||||
* @brief Update an executable to the list
|
||||
*
|
||||
* @param title name displayed in the UI
|
||||
* @param executableName the actual filename to execute
|
||||
* @param arguments arguments to pass to the executable
|
||||
* @param closeMO if true, MO will be closed when the binary is started
|
||||
**/
|
||||
void updateExecutable(const QString &title,
|
||||
const QString &executableName,
|
||||
const QString &arguments,
|
||||
const QString &workingDirectory,
|
||||
MOBase::ExecutableInfo::CloseMOStyle closeMO,
|
||||
const QString &steamAppID,
|
||||
Executable::Flags mask,
|
||||
Executable::Flags flags);
|
||||
|
||||
/**
|
||||
* @brief remove the executable with the specified file name. This needs to be an absolute file path
|
||||
@@ -159,6 +179,13 @@ public:
|
||||
**/
|
||||
void getExecutables(std::vector<Executable>::iterator &begin, std::vector<Executable>::iterator &end);
|
||||
|
||||
/**
|
||||
* @brief get the number of executables (custom or otherwise)
|
||||
**/
|
||||
size_t size() const {
|
||||
return m_Executables.size();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::vector<Executable>::iterator findExe(const QString &title);
|
||||
@@ -173,4 +200,6 @@ private:
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Executable::Flags)
|
||||
|
||||
#endif // EXECUTABLESLIST_H
|
||||
|
||||
+26
-37
@@ -124,18 +124,16 @@ void InstallationManager::mapToArchive(const DirectoryTree::Node *node, std::wst
|
||||
}
|
||||
|
||||
for (DirectoryTree::const_leaf_iterator iter = node->leafsBegin(); iter != node->leafsEnd(); ++iter) {
|
||||
data[iter->getIndex()]->setSkip(false);
|
||||
std::wstring temp = path + iter->getName().toStdWString();
|
||||
data[iter->getIndex()]->setOutputFileName(temp.c_str());
|
||||
data[iter->getIndex()]->addOutputFileName(temp.c_str());
|
||||
}
|
||||
|
||||
for (DirectoryTree::const_node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) {
|
||||
std::wstring temp = path + (*iter)->getData().name.toStdWString();
|
||||
if ((*iter)->getData().index != -1) {
|
||||
data[(*iter)->getData().index]->setSkip(false);
|
||||
std::wstring temp = path + (*iter)->getData().name.toStdWString();
|
||||
data[(*iter)->getData().index]->setOutputFileName(temp.c_str());
|
||||
data[(*iter)->getData().index]->addOutputFileName(temp.c_str());
|
||||
}
|
||||
mapToArchive(*iter, path + (*iter)->getData().name.toStdWString(), data);
|
||||
mapToArchive(*iter, temp, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,11 +144,6 @@ void InstallationManager::mapToArchive(const DirectoryTree::Node *baseNode)
|
||||
size_t size;
|
||||
m_CurrentArchive->getFileList(data, size);
|
||||
|
||||
// first disable all files + folders, we will re-enable those present in baseNode
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
data[i]->setSkip(true);
|
||||
}
|
||||
|
||||
std::wstring currentPath;
|
||||
|
||||
mapToArchive(baseNode, currentPath, data);
|
||||
@@ -169,32 +162,29 @@ bool InstallationManager::unpackSingleFile(const QString &fileName)
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
if (_wcsicmp(data[i]->getFileName(), ToWString(fileName).c_str()) == 0) {
|
||||
available = true;
|
||||
data[i]->setSkip(false);
|
||||
data[i]->setOutputFileName(ToWString(baseName).c_str());
|
||||
data[i]->addOutputFileName(ToWString(baseName).c_str());
|
||||
m_TempFilesToDelete.insert(baseName);
|
||||
} else {
|
||||
data[i]->setSkip(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (available) {
|
||||
m_InstallationProgress.setWindowTitle(tr("Extracting files"));
|
||||
m_InstallationProgress.setLabelText(QString());
|
||||
m_InstallationProgress.setValue(0);
|
||||
m_InstallationProgress.setWindowModality(Qt::WindowModal);
|
||||
m_InstallationProgress.show();
|
||||
|
||||
bool res = m_CurrentArchive->extract(ToWString(QDir::toNativeSeparators(QDir::tempPath())).c_str(),
|
||||
new MethodCallback<InstallationManager, void, float>(this, &InstallationManager::updateProgress),
|
||||
new MethodCallback<InstallationManager, void, LPCWSTR>(this, &InstallationManager::dummyProgressFile),
|
||||
new MethodCallback<InstallationManager, void, LPCWSTR>(this, &InstallationManager::report7ZipError));
|
||||
|
||||
m_InstallationProgress.hide();
|
||||
|
||||
return res;
|
||||
} else {
|
||||
if (!available) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_InstallationProgress.setWindowTitle(tr("Extracting files"));
|
||||
m_InstallationProgress.setLabelText(QString());
|
||||
m_InstallationProgress.setValue(0);
|
||||
m_InstallationProgress.setWindowModality(Qt::WindowModal);
|
||||
m_InstallationProgress.show();
|
||||
|
||||
bool res = m_CurrentArchive->extract(ToWString(QDir::toNativeSeparators(QDir::tempPath())).c_str(),
|
||||
new MethodCallback<InstallationManager, void, float>(this, &InstallationManager::updateProgress),
|
||||
new MethodCallback<InstallationManager, void, LPCWSTR>(this, &InstallationManager::dummyProgressFile),
|
||||
new MethodCallback<InstallationManager, void, LPCWSTR>(this, &InstallationManager::report7ZipError));
|
||||
|
||||
m_InstallationProgress.hide();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -251,14 +241,11 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool
|
||||
++targetFile;
|
||||
}
|
||||
}
|
||||
data[i]->setOutputFileName(targetFile);
|
||||
data[i]->addOutputFileName(targetFile);
|
||||
|
||||
result.append(QDir::tempPath().append("/").append(ToQString(targetFile)));
|
||||
|
||||
data[i]->setSkip(false);
|
||||
m_TempFilesToDelete.insert(ToQString(targetFile));
|
||||
} else {
|
||||
data[i]->setSkip(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,7 +591,7 @@ bool InstallationManager::wasCancelled()
|
||||
}
|
||||
|
||||
|
||||
void InstallationManager::postInstallCleanup() const
|
||||
void InstallationManager::postInstallCleanup()
|
||||
{
|
||||
m_CurrentArchive->close();
|
||||
|
||||
@@ -618,12 +605,14 @@ void InstallationManager::postInstallCleanup() const
|
||||
|
||||
// clean up temp files
|
||||
// TODO: this doesn't yet remove directories. Also, the files may be left there if this point isn't reached
|
||||
foreach (const QString &tempFile, m_TempFilesToDelete) {
|
||||
for (const QString &tempFile : m_TempFilesToDelete) {
|
||||
QFileInfo fileInfo(QDir::tempPath() + "/" + tempFile);
|
||||
QFile::remove(fileInfo.absoluteFilePath());
|
||||
directoriesToRemove.insert(fileInfo.absolutePath());
|
||||
}
|
||||
|
||||
m_TempFilesToDelete.clear();
|
||||
|
||||
// try to delete each directory we had temporary files in. the call fails for non-empty directories which is ok
|
||||
foreach (const QString &dir, directoriesToRemove) {
|
||||
QDir().rmdir(dir);
|
||||
|
||||
@@ -177,7 +177,7 @@ private:
|
||||
|
||||
bool ensureValidModName(MOBase::GuessedValue<QString> &name) const;
|
||||
|
||||
void postInstallCleanup() const;
|
||||
void postInstallCleanup();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
+32
-28
@@ -62,6 +62,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "selectiondialog.h"
|
||||
#include "moapplication.h"
|
||||
#include "tutorialmanager.h"
|
||||
#include "nxmaccessmanager.h"
|
||||
#include <iostream>
|
||||
#include <QMessageBox>
|
||||
#include <QSharedMemory>
|
||||
@@ -170,13 +171,14 @@ void cleanupDir()
|
||||
"proxy.dll"
|
||||
};
|
||||
|
||||
qDebug("removing obsolete files");
|
||||
|
||||
for (const QString &fileName : fileNames) {
|
||||
QString fullPath = qApp->applicationDirPath() + "/" + fileName;
|
||||
if (QFile::exists(fullPath)
|
||||
&& !shellDelete(QStringList(fullPath), true)) {
|
||||
qDebug("failed to remove obsolete %s", qPrintable(fullPath));
|
||||
if (QFile::exists(fullPath)) {
|
||||
if (shellDelete(QStringList(fullPath), true)) {
|
||||
qDebug("removed obsolete file %s", qPrintable(fullPath));
|
||||
} else {
|
||||
qDebug("failed to remove obsolete %s", qPrintable(fullPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,22 +207,28 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *except
|
||||
FuncMiniDumpWriteDump funcDump = (FuncMiniDumpWriteDump)::GetProcAddress(dbgDLL, "MiniDumpWriteDump");
|
||||
if (funcDump) {
|
||||
if (QMessageBox::question(nullptr, QObject::tr("Woops"),
|
||||
QObject::tr("ModOrganizer has crashed! Should a diagnostic file be created? If you send me this file "
|
||||
"(%1) to sherb@gmx.net, the bug is a lot more likely to be fixed. "
|
||||
"Please include a short description of what you were doing when the crash happened").arg(qApp->applicationFilePath().append(".dmp")),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
QObject::tr("ModOrganizer has crashed! "
|
||||
"Should a diagnostic file be created? "
|
||||
"If you send me this file (%1) to sherb@gmx.net, "
|
||||
"the bug is a lot more likely to be fixed. "
|
||||
"Please include a short description of what you were "
|
||||
"doing when the crash happened"
|
||||
).arg(qApp->applicationFilePath().append(".dmp")),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
|
||||
std::wstring dumpName = ToWString(qApp->applicationFilePath().append(".dmp"));
|
||||
|
||||
HANDLE dumpFile = ::CreateFile(dumpName.c_str(),
|
||||
GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
GENERIC_WRITE, FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (dumpFile != INVALID_HANDLE_VALUE) {
|
||||
_MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
|
||||
exceptionInfo.ThreadId = ::GetCurrentThreadId();
|
||||
exceptionInfo.ExceptionPointers = exceptionPtrs;
|
||||
exceptionInfo.ClientPointers = false;
|
||||
|
||||
BOOL success = funcDump(::GetCurrentProcess(), ::GetCurrentProcessId(), dumpFile, MiniDumpNormal, &exceptionInfo, nullptr, nullptr);
|
||||
BOOL success = funcDump(::GetCurrentProcess(), ::GetCurrentProcessId(), dumpFile,
|
||||
MiniDumpNormal, &exceptionInfo, nullptr, nullptr);
|
||||
|
||||
::FlushFileBuffers(dumpFile);
|
||||
::CloseHandle(dumpFile);
|
||||
@@ -248,11 +256,6 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *except
|
||||
return result;
|
||||
}
|
||||
|
||||
static void registerMetaTypes()
|
||||
{
|
||||
registerExecutable();
|
||||
}
|
||||
|
||||
static bool HaveWriteAccess(const std::wstring &path)
|
||||
{
|
||||
bool writable = false;
|
||||
@@ -340,13 +343,9 @@ int main(int argc, char *argv[])
|
||||
QString dataPath =
|
||||
instanceID.isEmpty() ? application.applicationDirPath()
|
||||
: QDir::fromNativeSeparators(
|
||||
#if QT_VERSION >= 0x050000
|
||||
QStandardPaths::writableLocation(QStandardPaths::DataLocation)
|
||||
#else
|
||||
QDesktopServices::storageLocation(QDesktopServices::DataLocation)
|
||||
#endif
|
||||
QStandardPaths::writableLocation(QStandardPaths::DataLocation)
|
||||
+ "/" + instanceID
|
||||
);
|
||||
);
|
||||
application.setProperty("dataPath", dataPath);
|
||||
|
||||
if (!QDir(dataPath).exists()) {
|
||||
@@ -414,8 +413,6 @@ int main(int argc, char *argv[])
|
||||
::SetEnvironmentVariableW(L"PATH", newPath.c_str());
|
||||
}
|
||||
|
||||
registerMetaTypes();
|
||||
|
||||
QStringList arguments = application.arguments();
|
||||
|
||||
bool forcePrimary = false;
|
||||
@@ -499,21 +496,26 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
IPluginGame *game = organizer.managedGame();
|
||||
|
||||
if (!settings.contains("game_edition")) {
|
||||
std::vector<std::wstring> editions = GameInfo::instance().getSteamVariants();
|
||||
QStringList editions = game->gameVariants();
|
||||
if (editions.size() > 1) {
|
||||
SelectionDialog selection(QObject::tr("Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!)"), nullptr);
|
||||
int index = 0;
|
||||
for (auto iter = editions.begin(); iter != editions.end(); ++iter) {
|
||||
selection.addChoice(ToQString(*iter), "", index++);
|
||||
for (const QString &edition : editions) {
|
||||
selection.addChoice(edition, "", index++);
|
||||
}
|
||||
if (selection.exec() == QDialog::Rejected) {
|
||||
return -1;
|
||||
} else {
|
||||
settings.setValue("game_edition", selection.getChoiceData().toInt());
|
||||
settings.setValue("game_edition", selection.getChoiceString());
|
||||
}
|
||||
}
|
||||
}
|
||||
game->setGameVariant(settings.value("game_edition").toString());
|
||||
|
||||
|
||||
#pragma message("edition isn't used?")
|
||||
qDebug("managing game at %s", qPrintable(QDir::toNativeSeparators(gamePath)));
|
||||
|
||||
@@ -540,6 +542,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
NexusInterface::instance()->getAccessManager()->startLoginCheck();
|
||||
|
||||
qDebug("initializing tutorials");
|
||||
TutorialManager::init(qApp->applicationDirPath() + "/" + QString::fromStdWString(AppConfig::tutorialsPath()) + "/",
|
||||
&organizer);
|
||||
|
||||
+178
-112
File diff suppressed because it is too large
Load Diff
+12
-4
@@ -30,7 +30,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include <QProgressBar>
|
||||
#include <QTranslator>
|
||||
#include <QPluginLoader>
|
||||
#include "executableslist.h"
|
||||
#include "modlist.h"
|
||||
#include "pluginlist.h"
|
||||
#include "plugincontainer.h"
|
||||
@@ -93,8 +92,6 @@ public:
|
||||
void refreshDataTree();
|
||||
void refreshSaveList();
|
||||
|
||||
void setExecutablesList(const ExecutablesList &executablesList);
|
||||
|
||||
void setModListSorting(int index);
|
||||
void setESPListSorting(int index);
|
||||
|
||||
@@ -124,6 +121,7 @@ public:
|
||||
|
||||
virtual MOBase::DelayedFileWriterBase &archivesWriter() override { return m_ArchiveListWriter; }
|
||||
|
||||
void updateWindowTitle(const QString &accountName, bool premium);
|
||||
public slots:
|
||||
|
||||
void displayColumnSelection(const QPoint &pos);
|
||||
@@ -329,6 +327,17 @@ private:
|
||||
|
||||
MOBase::DelayedFileWriter m_ArchiveListWriter;
|
||||
|
||||
enum class ShortcutType {
|
||||
Toolbar,
|
||||
Desktop,
|
||||
StartMenu
|
||||
};
|
||||
|
||||
void addWindowsLink(ShortcutType const);
|
||||
|
||||
Executable const &getSelectedExecutable() const;
|
||||
Executable &getSelectedExecutable();
|
||||
|
||||
private slots:
|
||||
|
||||
void showMessage(const QString &message);
|
||||
@@ -525,7 +534,6 @@ private slots: // ui slots
|
||||
void on_categoriesOrBtn_toggled(bool checked);
|
||||
void on_managedArchiveLabel_linkHovered(const QString &link);
|
||||
void on_manageArchivesBox_toggled(bool checked);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1010,7 +1010,7 @@ QStringList ModInfoRegular::archives() const
|
||||
{
|
||||
QStringList result;
|
||||
QDir dir(this->absolutePath());
|
||||
foreach (const QString &archive, dir.entryList(QStringList("*.bsa"))) {
|
||||
for (const QString &archive : dir.entryList(QStringList("*.bsa"))) {
|
||||
result.append(this->absolutePath() + "/" + archive);
|
||||
}
|
||||
return result;
|
||||
|
||||
+11
-2
@@ -587,6 +587,12 @@ QStringList ModList::mimeTypes() const
|
||||
return result;
|
||||
}
|
||||
|
||||
QMimeData *ModList::mimeData(const QModelIndexList &indexes) const
|
||||
{
|
||||
QMimeData *result = QAbstractItemModel::mimeData(indexes);
|
||||
result->setData("text/plain", "mod");
|
||||
return result;
|
||||
}
|
||||
|
||||
void ModList::changeModPriority(std::vector<int> sourceIndices, int newPriority)
|
||||
{
|
||||
@@ -824,7 +830,6 @@ bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &pa
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ModList::dropMod(const QMimeData *mimeData, int row, const QModelIndex &parent)
|
||||
{
|
||||
|
||||
@@ -877,10 +882,13 @@ bool ModList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int
|
||||
}
|
||||
|
||||
if (m_Profile == nullptr) return false;
|
||||
|
||||
if (mimeData->hasUrls()) {
|
||||
return dropURLs(mimeData, row, parent);
|
||||
} else {
|
||||
} else if (mimeData->hasText()) {
|
||||
return dropMod(mimeData, row, parent);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1141,3 +1149,4 @@ bool ModList::eventFilter(QObject *obj, QEvent *event)
|
||||
}
|
||||
return QAbstractItemModel::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ public: // implementation of virtual functions of QAbstractItemModel
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const;
|
||||
virtual Qt::DropActions supportedDropActions() const { return Qt::MoveAction | Qt::CopyAction; }
|
||||
virtual QStringList mimeTypes() const;
|
||||
virtual QMimeData *mimeData(const QModelIndexList &indexes) const;
|
||||
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
||||
virtual bool removeRows(int row, int count, const QModelIndex &parent);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "modinfo.h"
|
||||
#include "profile.h"
|
||||
#include "messagedialog.h"
|
||||
#include "qtgroupingproxy.h"
|
||||
#include <QMenu>
|
||||
#include <QCheckBox>
|
||||
#include <QWidgetAction>
|
||||
@@ -102,9 +103,25 @@ void ModListSortProxy::disableAllVisible()
|
||||
invalidate();
|
||||
}
|
||||
|
||||
unsigned long ModListSortProxy::flagsId(const std::vector<ModInfo::EFlag> &flags) const
|
||||
{
|
||||
unsigned long result = 0;
|
||||
for (ModInfo::EFlag flag : flags) {
|
||||
if ((flag != ModInfo::FLAG_FOREIGN)
|
||||
&& (flag != ModInfo::FLAG_OVERWRITE)) {
|
||||
result += 1 << (int)flag;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ModListSortProxy::lessThan(const QModelIndex &left,
|
||||
const QModelIndex &right) const
|
||||
{
|
||||
if (sourceModel()->hasChildren(left) || sourceModel()->hasChildren(right)) {
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
}
|
||||
|
||||
bool lOk, rOk;
|
||||
int leftIndex = left.data(Qt::UserRole + 1).toInt(&lOk);
|
||||
int rightIndex = right.data(Qt::UserRole + 1).toInt(&rOk);
|
||||
@@ -130,8 +147,13 @@ bool ModListSortProxy::lessThan(const QModelIndex &left,
|
||||
|
||||
switch (left.column()) {
|
||||
case ModList::COL_FLAGS: {
|
||||
if (leftMod->getFlags().size() != rightMod->getFlags().size())
|
||||
lt = leftMod->getFlags().size() < rightMod->getFlags().size();
|
||||
std::vector<ModInfo::EFlag> leftFlags = leftMod->getFlags();
|
||||
std::vector<ModInfo::EFlag> rightFlags = rightMod->getFlags();
|
||||
if (leftFlags.size() != rightFlags.size()) {
|
||||
lt = leftFlags.size() < rightFlags.size();
|
||||
} else {
|
||||
lt = flagsId(leftFlags) < flagsId(rightFlags);
|
||||
}
|
||||
} break;
|
||||
case ModList::COL_CONTENT: {
|
||||
std::vector<ModInfo::EContent> lContent = leftMod->getContents();
|
||||
@@ -204,7 +226,7 @@ void ModListSortProxy::updateFilter(const QString &filter)
|
||||
|
||||
bool ModListSortProxy::hasConflictFlag(const std::vector<ModInfo::EFlag> &flags) const
|
||||
{
|
||||
foreach (ModInfo::EFlag flag, flags) {
|
||||
for (ModInfo::EFlag flag : flags) {
|
||||
if ((flag == ModInfo::FLAG_CONFLICT_MIXED) ||
|
||||
(flag == ModInfo::FLAG_CONFLICT_OVERWRITE) ||
|
||||
(flag == ModInfo::FLAG_CONFLICT_OVERWRITTEN) ||
|
||||
@@ -362,7 +384,7 @@ bool ModListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action
|
||||
return false;
|
||||
}
|
||||
if ((row == -1) && (column == -1)) {
|
||||
return this->sourceModel()->dropMimeData(data, action, -1, -1, mapToSource(parent));
|
||||
return sourceModel()->dropMimeData(data, action, -1, -1, mapToSource(parent));
|
||||
}
|
||||
// in the regular model, when dropping between rows, the row-value passed to
|
||||
// the sourceModel is inconsistent between ascending and descending ordering.
|
||||
@@ -380,22 +402,32 @@ bool ModListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action
|
||||
void ModListSortProxy::setSourceModel(QAbstractItemModel *sourceModel)
|
||||
{
|
||||
QSortFilterProxyModel::setSourceModel(sourceModel);
|
||||
|
||||
connect(sourceModel, SIGNAL(aboutToChangeData()), this, SLOT(aboutToChangeData()));
|
||||
connect(sourceModel, SIGNAL(postDataChanged()), this, SLOT(postDataChanged()));
|
||||
QtGroupingProxy *proxy = qobject_cast<QtGroupingProxy*>(sourceModel);
|
||||
if (proxy != nullptr) {
|
||||
sourceModel = proxy->sourceModel();
|
||||
}
|
||||
connect(sourceModel, SIGNAL(aboutToChangeData()), this, SLOT(aboutToChangeData()), Qt::UniqueConnection);
|
||||
connect(sourceModel, SIGNAL(postDataChanged()), this, SLOT(postDataChanged()), Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
void ModListSortProxy::aboutToChangeData()
|
||||
{
|
||||
// having a filter active when dataChanged is called caused a crash
|
||||
// (at least with some Qt versions)
|
||||
// this may be related to the fact that the item being edited may disappear from the view as a
|
||||
// result of the edit
|
||||
m_PreChangeFilters = categoryFilter();
|
||||
setCategoryFilter(std::vector<int>());
|
||||
}
|
||||
|
||||
void ModListSortProxy::postDataChanged()
|
||||
{
|
||||
setCategoryFilter(m_PreChangeFilters);
|
||||
m_PreChangeFilters.clear();
|
||||
// if the filter is re-activated right away the editor can't be deleted but becomes invisible
|
||||
// or at least the view continues to think it's being edited. As a result no new editor can be
|
||||
// opened
|
||||
QTimer::singleShot(10, [this] () {
|
||||
setCategoryFilter(m_PreChangeFilters);
|
||||
m_PreChangeFilters.clear();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
unsigned long flagsId(const std::vector<ModInfo::EFlag> &flags) const;
|
||||
bool hasConflictFlag(const std::vector<ModInfo::EFlag> &flags) const;
|
||||
void updateFilterActive();
|
||||
bool filterMatchesModAnd(ModInfo::Ptr info, bool enabled) const;
|
||||
|
||||
+2
-12
@@ -391,8 +391,7 @@ void NexusInterface::nextRequest()
|
||||
return;
|
||||
}
|
||||
|
||||
if (!getAccessManager()->loggedIn()
|
||||
&& requiresLogin(m_RequestQueue.head())) {
|
||||
if (requiresLogin(m_RequestQueue.head()) && !getAccessManager()->loggedIn()) {
|
||||
if (!getAccessManager()->loginAttempted()) {
|
||||
emit needLogin();
|
||||
return;
|
||||
@@ -438,16 +437,7 @@ void NexusInterface::nextRequest()
|
||||
}
|
||||
QNetworkRequest request(url);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml");
|
||||
QStringList comments;
|
||||
comments << "compatible to Nexus Client v" + m_NMMVersion;
|
||||
if (!info.m_SubModule.isEmpty()) {
|
||||
comments << "module: " + info.m_SubModule;
|
||||
}
|
||||
|
||||
QString userAgent = QString("Mod Organizer v%1 (%2)")
|
||||
.arg(m_MOVersion.displayString())
|
||||
.arg(comments.join("; "));
|
||||
request.setRawHeader("User-Agent", userAgent.toUtf8());
|
||||
request.setRawHeader("User-Agent", m_AccessManager->userAgent(info.m_SubModule).toUtf8());
|
||||
|
||||
info.m_Reply = m_AccessManager->get(request);
|
||||
|
||||
|
||||
+149
-36
@@ -24,6 +24,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "selfupdater.h"
|
||||
#include "persistentcookiejar.h"
|
||||
#include "settings.h"
|
||||
#include <gameinfo.h>
|
||||
#include <json.h>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QNetworkProxy>
|
||||
@@ -32,24 +34,29 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include <QNetworkCookieJar>
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <gameinfo.h>
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
#include <QUrlQuery>
|
||||
#endif
|
||||
#include <QThread>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
|
||||
|
||||
using namespace MOBase;
|
||||
using namespace MOShared;
|
||||
|
||||
|
||||
// unfortunately Nexus doesn't seem to document these states, all I know is all these listed
|
||||
// are considered premium (27 should be lifetime premium)
|
||||
const std::set<int> NXMAccessManager::s_PremiumAccountStates { 4, 6, 13, 27, 31, 32 };
|
||||
|
||||
|
||||
NXMAccessManager::NXMAccessManager(QObject *parent, const QString &moVersion)
|
||||
: QNetworkAccessManager(parent)
|
||||
, m_LoginReply(nullptr)
|
||||
, m_ProgressDialog()
|
||||
, m_MOVersion(moVersion)
|
||||
, m_LoginAttempted(false)
|
||||
{
|
||||
|
||||
m_LoginTimeout.setSingleShot(true);
|
||||
m_LoginTimeout.setInterval(30000);
|
||||
setCookieJar(new PersistentCookieJar(
|
||||
QDir::fromNativeSeparators(Settings::instance().getCacheDirectory() + "/nexus_cookies.dat")));
|
||||
}
|
||||
@@ -88,20 +95,98 @@ QNetworkReply *NXMAccessManager::createRequest(
|
||||
}
|
||||
|
||||
|
||||
void NXMAccessManager::showCookies()
|
||||
void NXMAccessManager::showCookies() const
|
||||
{
|
||||
QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage()) + "/"));
|
||||
foreach (QNetworkCookie cookie, cookies) {
|
||||
qDebug("%s - %s", cookie.name().constData(), cookie.value().constData());
|
||||
QUrl url(ToQString(GameInfo::instance().getNexusPage()) + "/");
|
||||
|
||||
for (const QNetworkCookie &cookie : cookieJar()->cookiesForUrl(url)) {
|
||||
qDebug("%s - %s (expires: %s)",
|
||||
cookie.name().constData(), cookie.value().constData(),
|
||||
qPrintable(cookie.expirationDate().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NXMAccessManager::startLoginCheck()
|
||||
{
|
||||
if (hasLoginCookies()) {
|
||||
qDebug("validating login cookies");
|
||||
QNetworkRequest request(ToQString(GameInfo::instance().getNexusPage()) + "/Sessions/?Validate");
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
request.setRawHeader("User-Agent", userAgent().toUtf8());
|
||||
|
||||
m_LoginReply = get(request);
|
||||
m_LoginTimeout.start();
|
||||
m_LoginState = LOGIN_CHECKING;
|
||||
connect(m_LoginReply, SIGNAL(finished()), this, SLOT(loginChecked()));
|
||||
connect(m_LoginReply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
this, SLOT(loginError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NXMAccessManager::retrieveCredentials()
|
||||
{
|
||||
qDebug("retrieving credentials");
|
||||
QNetworkRequest request(ToQString(GameInfo::instance().getNexusPage())
|
||||
+ QString("/Core/Libs/Flamework/Entities/User?GetCredentials&game_id=%1"
|
||||
).arg(GameInfo::instance().getNexusGameID()));
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
request.setRawHeader("User-Agent", userAgent().toUtf8());
|
||||
|
||||
QNetworkReply *reply = get(request);
|
||||
QTimer timeout;
|
||||
connect(&timeout, &QTimer::timeout, [reply] () {
|
||||
reply->deleteLater();
|
||||
});
|
||||
timeout.start();
|
||||
|
||||
connect(reply, &QNetworkReply::finished, [reply, this] () {
|
||||
QJsonDocument jdoc = QJsonDocument::fromJson(reply->readAll());
|
||||
QJsonArray credentialsData = jdoc.array();
|
||||
emit credentialsReceived(credentialsData.at(2).toString(),
|
||||
s_PremiumAccountStates.find(credentialsData.at(1).toInt())
|
||||
!= s_PremiumAccountStates.end());
|
||||
reply->deleteLater();
|
||||
});
|
||||
|
||||
connect(reply, static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
|
||||
[=] (QNetworkReply::NetworkError) {
|
||||
qDebug("failed to retrieve account credentials: %s", qPrintable(reply->errorString()));
|
||||
reply->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool NXMAccessManager::loggedIn() const
|
||||
{
|
||||
return hasLoginCookies();
|
||||
if (m_LoginState == LOGIN_CHECKING) {
|
||||
QProgressDialog progress;
|
||||
progress.setLabelText(tr("Verifying Nexus login"));
|
||||
progress.show();
|
||||
while (m_LoginState == LOGIN_CHECKING) {
|
||||
QCoreApplication::processEvents();
|
||||
QThread::msleep(100);
|
||||
}
|
||||
progress.hide();
|
||||
}
|
||||
|
||||
return m_LoginState == LOGIN_VALID;
|
||||
}
|
||||
|
||||
|
||||
void NXMAccessManager::refuseLogin()
|
||||
{
|
||||
m_LoginState = LOGIN_REFUSED;
|
||||
}
|
||||
|
||||
|
||||
bool NXMAccessManager::loginAttempted() const
|
||||
{
|
||||
return m_LoginState != LOGIN_NOT_CHECKED;
|
||||
}
|
||||
|
||||
|
||||
bool NXMAccessManager::loginWaiting() const
|
||||
{
|
||||
return m_LoginReply != nullptr;
|
||||
@@ -114,21 +199,32 @@ void NXMAccessManager::login(const QString &username, const QString &password)
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasLoginCookies()) {
|
||||
if (m_LoginState == LOGIN_VALID) {
|
||||
emit loginSuccessful(false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_LoginAttempted = true;
|
||||
|
||||
m_Username = username;
|
||||
m_Password = password;
|
||||
pageLogin();
|
||||
}
|
||||
|
||||
|
||||
QString NXMAccessManager::userAgent(const QString &subModule) const
|
||||
{
|
||||
QStringList comments;
|
||||
comments << "compatible to Nexus Client v" + m_NMMVersion;
|
||||
if (!subModule.isEmpty()) {
|
||||
comments << "module: " + subModule;
|
||||
}
|
||||
|
||||
return QString("Mod Organizer v%1 (%2)").arg(m_MOVersion, comments.join("; "));
|
||||
}
|
||||
|
||||
|
||||
void NXMAccessManager::pageLogin()
|
||||
{
|
||||
qDebug("logging %s in on Nexus", qPrintable(m_Username));
|
||||
QString requestString = (ToQString(GameInfo::instance().getNexusPage()) + "/Sessions/?Login&uri=%1")
|
||||
.arg(QString(QUrl::toPercentEncoding(ToQString(GameInfo::instance().getNexusPage()))));
|
||||
|
||||
@@ -136,20 +232,12 @@ void NXMAccessManager::pageLogin()
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
QByteArray postDataQuery;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
|
||||
QUrlQuery postData;
|
||||
postData.addQueryItem("username", m_Username);
|
||||
postData.addQueryItem("password", m_Password);
|
||||
postDataQuery = postData.query(QUrl::FullyEncoded).toUtf8();
|
||||
#else
|
||||
QUrl postData;
|
||||
postData.addQueryItem("username", m_Username);
|
||||
postData.addQueryItem("password", m_Password);
|
||||
postDataQuery = postData.encodedQuery();
|
||||
#endif
|
||||
|
||||
QString userAgent = QString("Mod Organizer v%1 (compatible to Nexus Client v%2)").arg(m_MOVersion).arg(m_NMMVersion);
|
||||
request.setRawHeader("User-Agent", userAgent.toUtf8());
|
||||
request.setRawHeader("User-Agent", userAgent().toUtf8());
|
||||
|
||||
m_ProgressDialog.setLabelText(tr("Logging into Nexus"));
|
||||
QList<QPushButton*> buttons = m_ProgressDialog.findChildren<QPushButton*>();
|
||||
@@ -159,6 +247,7 @@ void NXMAccessManager::pageLogin()
|
||||
|
||||
m_LoginReply = post(request, postDataQuery);
|
||||
m_LoginTimeout.start();
|
||||
m_LoginState = LOGIN_CHECKING;
|
||||
connect(m_LoginReply, SIGNAL(finished()), this, SLOT(loginFinished()));
|
||||
connect(m_LoginReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(loginError(QNetworkReply::NetworkError)));
|
||||
}
|
||||
@@ -166,20 +255,25 @@ void NXMAccessManager::pageLogin()
|
||||
|
||||
void NXMAccessManager::loginTimeout()
|
||||
{
|
||||
emit loginFailed(tr("timeout"));
|
||||
m_LoginReply->deleteLater();
|
||||
m_LoginReply = nullptr;
|
||||
m_LoginAttempted = false; // this usually means we might have success later
|
||||
m_LoginTimeout.stop();
|
||||
m_Username.clear();
|
||||
m_Password.clear();
|
||||
m_LoginState = LOGIN_NOT_VALID;
|
||||
|
||||
emit loginFailed(tr("timeout"));
|
||||
}
|
||||
|
||||
|
||||
void NXMAccessManager::loginError(QNetworkReply::NetworkError)
|
||||
{
|
||||
qDebug("login error");
|
||||
m_ProgressDialog.hide();
|
||||
m_LoginTimeout.stop();
|
||||
m_Username.clear();
|
||||
m_Password.clear();
|
||||
m_LoginState = LOGIN_NOT_VALID;
|
||||
|
||||
if (m_LoginReply != nullptr) {
|
||||
emit loginFailed(m_LoginReply->errorString());
|
||||
m_LoginReply->deleteLater();
|
||||
@@ -187,16 +281,15 @@ void NXMAccessManager::loginError(QNetworkReply::NetworkError)
|
||||
} else {
|
||||
emit loginFailed(tr("Unknown error"));
|
||||
}
|
||||
m_Username.clear();
|
||||
m_Password.clear();
|
||||
}
|
||||
|
||||
|
||||
bool NXMAccessManager::hasLoginCookies() const
|
||||
{
|
||||
bool sidCookie = false;
|
||||
QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(QUrl(ToQString(GameInfo::instance().getNexusPage()) + "/"));
|
||||
foreach (QNetworkCookie cookie, cookies) {
|
||||
QUrl url(ToQString(GameInfo::instance().getNexusPage()) + "/");
|
||||
QList<QNetworkCookie> cookies = cookieJar()->cookiesForUrl(url);
|
||||
for (const QNetworkCookie &cookie : cookies) {
|
||||
if (cookie.name() == "sid") {
|
||||
sidCookie = true;
|
||||
}
|
||||
@@ -208,15 +301,35 @@ bool NXMAccessManager::hasLoginCookies() const
|
||||
void NXMAccessManager::loginFinished()
|
||||
{
|
||||
m_ProgressDialog.hide();
|
||||
if (hasLoginCookies()) {
|
||||
emit loginSuccessful(true);
|
||||
} else {
|
||||
emit loginFailed(tr("Please check your password"));
|
||||
}
|
||||
|
||||
m_LoginTimeout.stop();
|
||||
m_LoginReply->deleteLater();
|
||||
m_LoginReply = nullptr;
|
||||
m_Username.clear();
|
||||
m_Password.clear();
|
||||
|
||||
if (hasLoginCookies()) {
|
||||
m_LoginState = LOGIN_VALID;
|
||||
retrieveCredentials();
|
||||
emit loginSuccessful(true);
|
||||
} else {
|
||||
m_LoginState = LOGIN_NOT_VALID;
|
||||
emit loginFailed(tr("Please check your password"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NXMAccessManager::loginChecked()
|
||||
{
|
||||
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
|
||||
QByteArray data = reply->readAll();
|
||||
m_LoginState = data == "null" ? LOGIN_NOT_VALID
|
||||
: LOGIN_VALID;
|
||||
if (m_LoginState == LOGIN_VALID) {
|
||||
retrieveCredentials();
|
||||
} else {
|
||||
qDebug("cookies seem to be invalid");
|
||||
}
|
||||
m_LoginReply->deleteLater();
|
||||
m_LoginReply = nullptr;
|
||||
}
|
||||
|
||||
|
||||
+32
-8
@@ -25,6 +25,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include <QTimer>
|
||||
#include <QNetworkReply>
|
||||
#include <QProgressDialog>
|
||||
#include <set>
|
||||
|
||||
|
||||
/**
|
||||
@@ -43,12 +44,18 @@ public:
|
||||
|
||||
bool loggedIn() const;
|
||||
|
||||
bool loginAttempted() const { return m_LoginAttempted; }
|
||||
bool loginAttempted() const;
|
||||
bool loginWaiting() const;
|
||||
|
||||
void login(const QString &username, const QString &password);
|
||||
|
||||
void showCookies();
|
||||
void showCookies() const;
|
||||
|
||||
QString userAgent(const QString &subModule = QString()) const;
|
||||
|
||||
void startLoginCheck();
|
||||
|
||||
void refuseLogin();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -68,11 +75,14 @@ signals:
|
||||
|
||||
void loginFailed(const QString &message);
|
||||
|
||||
private slots:
|
||||
void credentialsReceived(const QString &userName, bool premium);
|
||||
|
||||
void loginFinished();
|
||||
void loginError(QNetworkReply::NetworkError errorCode);
|
||||
void loginTimeout();
|
||||
private slots:
|
||||
|
||||
void loginChecked();
|
||||
void loginFinished();
|
||||
void loginError(QNetworkReply::NetworkError errorCode);
|
||||
void loginTimeout();
|
||||
|
||||
public slots:
|
||||
|
||||
@@ -84,10 +94,16 @@ protected:
|
||||
|
||||
private:
|
||||
|
||||
void pageLogin();
|
||||
void pageLogin();
|
||||
// void dlLogin();
|
||||
|
||||
bool hasLoginCookies() const;
|
||||
bool hasLoginCookies() const;
|
||||
|
||||
void retrieveCredentials();
|
||||
|
||||
private:
|
||||
|
||||
static const std::set<int> s_PremiumAccountStates;
|
||||
|
||||
private:
|
||||
|
||||
@@ -102,6 +118,14 @@ private:
|
||||
QString m_Password;
|
||||
|
||||
bool m_LoginAttempted;
|
||||
enum {
|
||||
LOGIN_NOT_CHECKED,
|
||||
LOGIN_CHECKING,
|
||||
LOGIN_NOT_VALID,
|
||||
LOGIN_ATTEMPT_FAILED,
|
||||
LOGIN_REFUSED,
|
||||
LOGIN_VALID
|
||||
} m_LoginState = LOGIN_NOT_CHECKED;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -311,7 +311,7 @@ LIBS += -lmo_shared -luibase -lshell32 -lole32 -luser32 -ladvapi32 -lgdi32 -lPsa
|
||||
|
||||
LIBS += -L"$${ZLIBPATH}/build" -lzlibstatic
|
||||
|
||||
DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _SCL_SECURE_NO_WARNINGS NOMINMAX
|
||||
DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _SCL_SECURE_NO_WARNINGS NOMINMAX PSAPI_VERSION=1
|
||||
|
||||
DEFINES += BOOST_DISABLE_ASSERTS NDEBUG QT_MESSAGELOGCONTEXT
|
||||
#DEFINES += QMLJSDEBUGGER
|
||||
|
||||
+101
-30
@@ -222,9 +222,10 @@ QSettings::Status OrganizerCore::storeSettings(const QString &fileName)
|
||||
const Executable &item = *current;
|
||||
settings.setArrayIndex(count++);
|
||||
settings.setValue("title", item.m_Title);
|
||||
settings.setValue("custom", item.m_Custom);
|
||||
settings.setValue("toolbar", item.m_Toolbar);
|
||||
if (item.m_Custom) {
|
||||
settings.setValue("custom", item.isCustom());
|
||||
settings.setValue("toolbar", item.isShownOnToolbar());
|
||||
settings.setValue("ownicon", item.usesOwnIcon());
|
||||
if (item.isCustom()) {
|
||||
settings.setValue("binary", item.m_BinaryInfo.absoluteFilePath());
|
||||
settings.setValue("arguments", item.m_Arguments);
|
||||
settings.setValue("workingDirectory", item.m_WorkingDirectory);
|
||||
@@ -243,11 +244,13 @@ QSettings::Status OrganizerCore::storeSettings(const QString &fileName)
|
||||
void OrganizerCore::storeSettings()
|
||||
{
|
||||
QString iniFile = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::iniFileName());
|
||||
if (!shellCopy(iniFile, iniFile + ".new", true, qApp->activeWindow())) {
|
||||
QMessageBox::critical(qApp->activeWindow(), tr("Failed to write settings"),
|
||||
tr("An error occured trying to update MO settings to %1: %2").arg(
|
||||
iniFile, windowsErrorString(::GetLastError())));
|
||||
return;
|
||||
if (QFileInfo(iniFile).exists()) {
|
||||
if (!shellCopy(iniFile, iniFile + ".new", true, qApp->activeWindow())) {
|
||||
QMessageBox::critical(qApp->activeWindow(), tr("Failed to write settings"),
|
||||
tr("An error occured trying to update MO settings to %1: %2").arg(
|
||||
iniFile, windowsErrorString(::GetLastError())));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QSettings::Status result = storeSettings(iniFile + ".new");
|
||||
@@ -329,19 +332,23 @@ void OrganizerCore::updateExecutablesList(QSettings &settings)
|
||||
ExecutableInfo::CloseMOStyle closeMO =
|
||||
settings.value("closeOnStart").toBool() ? ExecutableInfo::CloseMOStyle::DEFAULT_CLOSE
|
||||
: ExecutableInfo::CloseMOStyle::DEFAULT_STAY;
|
||||
|
||||
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()) flags |= Executable::UseApplicationIcon;
|
||||
|
||||
m_ExecutablesList.addExecutable(settings.value("title").toString(),
|
||||
settings.value("binary").toString(),
|
||||
settings.value("arguments").toString(),
|
||||
settings.value("workingDirectory", "").toString(),
|
||||
closeMO,
|
||||
settings.value("steamAppID", "").toString(),
|
||||
settings.value("custom", true).toBool(),
|
||||
settings.value("toolbar", false).toBool());
|
||||
flags);
|
||||
}
|
||||
|
||||
settings.endArray();
|
||||
|
||||
|
||||
// TODO this has nothing to do with executables list move to an appropriate function!
|
||||
ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, m_Settings.displayForeign());
|
||||
}
|
||||
@@ -367,11 +374,13 @@ void OrganizerCore::setUserInterface(IUserInterface *userInterface, QWidget *wid
|
||||
m_InstallationManager.setParentWidget(widget);
|
||||
m_Updater.setUserInterface(widget);
|
||||
|
||||
// this currently wouldn't work reliably if the ui isn't initialized yet to display the result
|
||||
if (isOnline() && !m_Settings.offlineMode()) {
|
||||
m_Updater.testForUpdate();
|
||||
} else {
|
||||
qDebug("user doesn't seem to be connected to the internet");
|
||||
if (userInterface != nullptr) {
|
||||
// this currently wouldn't work reliably if the ui isn't initialized yet to display the result
|
||||
if (isOnline() && !m_Settings.offlineMode()) {
|
||||
m_Updater.testForUpdate();
|
||||
} else {
|
||||
qDebug("user doesn't seem to be connected to the internet");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,21 +424,28 @@ Settings &OrganizerCore::settings()
|
||||
return m_Settings;
|
||||
}
|
||||
|
||||
bool OrganizerCore::nexusLogin()
|
||||
bool OrganizerCore::nexusLogin(bool retry)
|
||||
{
|
||||
QString username, password;
|
||||
|
||||
NXMAccessManager *accessManager = NexusInterface::instance()->getAccessManager();
|
||||
|
||||
if (!accessManager->loginAttempted()
|
||||
&& !accessManager->loggedIn()
|
||||
&& (m_Settings.getNexusLogin(username, password)
|
||||
|| (m_AskForNexusPW
|
||||
&& queryLogin(username, password)))) {
|
||||
accessManager->login(username, password);
|
||||
return true;
|
||||
} else {
|
||||
if ((accessManager->loginAttempted()
|
||||
|| accessManager->loggedIn())
|
||||
&& !retry) {
|
||||
// previous attempt, maybe even successful
|
||||
return false;
|
||||
} else {
|
||||
QString username, password;
|
||||
if ((!retry && m_Settings.getNexusLogin(username, password))
|
||||
|| (m_AskForNexusPW && queryLogin(username, password))) {
|
||||
// credentials stored or user entered them manually
|
||||
qDebug("attempt login with username %s", qPrintable(username));
|
||||
accessManager->login(username, password);
|
||||
return true;
|
||||
} else {
|
||||
// no credentials stored and user didn't enter them
|
||||
accessManager->refuseLogin();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,6 +545,12 @@ void OrganizerCore::setCurrentProfile(const QString &profileName)
|
||||
m_CurrentProfile = newProfile;
|
||||
m_ModList.setProfile(newProfile);
|
||||
|
||||
if (m_CurrentProfile->invalidationActive(nullptr)) {
|
||||
m_CurrentProfile->activateInvalidation();
|
||||
} else {
|
||||
m_CurrentProfile->deactivateInvalidation();
|
||||
}
|
||||
|
||||
connect(m_CurrentProfile, SIGNAL(modStatusChanged(uint)), this, SLOT(modStatusChanged(uint)));
|
||||
refreshDirectoryStructure();
|
||||
}
|
||||
@@ -1003,7 +1025,7 @@ HANDLE OrganizerCore::startApplication(const QString &executable, const QStringL
|
||||
try {
|
||||
const Executable &exe = m_ExecutablesList.findByBinary(binary);
|
||||
steamAppID = exe.m_SteamAppID;
|
||||
} catch (const std::runtime_error&) {
|
||||
} catch (const std::runtime_error&) {
|
||||
// nop
|
||||
}
|
||||
} else {
|
||||
@@ -1372,6 +1394,9 @@ void OrganizerCore::modStatusChanged(unsigned int index)
|
||||
FilesOrigin &origin = m_DirectoryStructure->getOriginByName(ToWString(modInfo->name()));
|
||||
origin.enable(false);
|
||||
}
|
||||
if (m_UserInterface != nullptr) {
|
||||
m_UserInterface->archivesWriter().write();
|
||||
}
|
||||
}
|
||||
modInfo->clearCaches();
|
||||
|
||||
@@ -1418,9 +1443,15 @@ void OrganizerCore::loginSuccessfulUpdate(bool necessary)
|
||||
|
||||
void OrganizerCore::loginFailed(const QString &message)
|
||||
{
|
||||
if (QMessageBox::question(qApp->activeWindow(), tr("Login failed"), tr("Login failed, try again?")) == QMessageBox::Yes) {
|
||||
if (nexusLogin(true)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_PendingDownloads.isEmpty()) {
|
||||
MessageDialog::showMessage(tr("login failed: %1. Trying to download anyway").arg(message), qApp->activeWindow());
|
||||
foreach (QString url, m_PendingDownloads) {
|
||||
MessageDialog::showMessage(tr("login failed: %1. Download will not be associated with an account").arg(message), qApp->activeWindow());
|
||||
for (QString url : m_PendingDownloads) {
|
||||
downloadRequestedNXM(url);
|
||||
}
|
||||
m_PendingDownloads.clear();
|
||||
@@ -1540,3 +1571,43 @@ void OrganizerCore::prepareStart() {
|
||||
storeSettings();
|
||||
}
|
||||
|
||||
std::vector<std::pair<QString, QString>> OrganizerCore::fileMapping()
|
||||
{
|
||||
IPluginGame *game = qApp->property("managed_game").value<IPluginGame*>();
|
||||
return fileMapping(game->dataDirectory().absolutePath(),
|
||||
directoryStructure(),
|
||||
directoryStructure());
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<QString, QString>> OrganizerCore::fileMapping(
|
||||
const QString &dataPath,
|
||||
const DirectoryEntry *base,
|
||||
const DirectoryEntry *directoryEntry)
|
||||
{
|
||||
std::vector<std::pair<QString, QString>> result;
|
||||
|
||||
for (FileEntry::Ptr current : directoryEntry->getFiles()) {
|
||||
bool isArchive = false;
|
||||
int origin = current->getOrigin(isArchive);
|
||||
if (isArchive || (origin == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString fileName = ToQString(current->getRelativePath());
|
||||
QString source = ToQString(base->getOriginByID(origin).getPath()) + fileName;
|
||||
QString target = QDir::toNativeSeparators(dataPath) + fileName;
|
||||
result.push_back(std::make_pair(source, target));
|
||||
}
|
||||
|
||||
// recurse into subdirectories
|
||||
std::vector<DirectoryEntry*>::const_iterator current, end;
|
||||
directoryEntry->getSubDirectories(current, end);
|
||||
for (; current != end; ++current) {
|
||||
std::vector<std::pair<QString, QString>> subRes = fileMapping(dataPath, base, *current);
|
||||
result.insert(result.end(), subRes.begin(), subRes.end());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user