mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
Compare commits
97
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 | ||
|
|
aa21a192cb | ||
|
|
d4d7ce1fd3 | ||
|
|
e31baf3ee6 | ||
|
|
5dcfca7c9d | ||
|
|
e9ee7e7f85 | ||
|
|
67d017fc98 | ||
|
|
a700e6a3ef | ||
|
|
c95de1b3aa | ||
|
|
351fc9ca9c | ||
|
|
53f0a2bc79 | ||
|
|
793dfb1637 | ||
|
|
d25a81071c | ||
|
|
ef9a61886c | ||
|
|
13a40b43c8 | ||
|
|
d7790cd717 | ||
|
|
84d632af6a | ||
|
|
b14b35349d | ||
|
|
83a4db2362 | ||
|
|
c797af8b76 | ||
|
|
ebf6ff3f76 | ||
|
|
02b5e73c2d | ||
|
|
b0b2f1d3a9 | ||
|
|
d8e406daa0 | ||
|
|
50dcc4c1d4 | ||
|
|
e21db0eaea | ||
|
|
859332e18d | ||
|
|
1fd8f44960 | ||
|
|
e0c655330d | ||
|
|
a0999cd42b |
+12
-6
@@ -46,7 +46,7 @@
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="about">
|
||||
<attribute name="title">
|
||||
@@ -83,7 +83,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string notr="true">Copyright 2011-2014 Sebastian Herbord</string>
|
||||
<string notr="true">Copyright 2011-2015 Sebastian Herbord</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -180,13 +180,19 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Ren (Korean)</string>
|
||||
<string notr="true">Ren (Korean)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string/>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>... more (Can't track)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string/>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -226,12 +232,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>GamerPoet</string>
|
||||
<string notr="true">GamerPoet</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Gopher</string>
|
||||
<string notr="true">Gopher</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -256,7 +262,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>thosrtanner</string>
|
||||
<string notr="true">thosrtanner</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
|
||||
+27
-5
@@ -166,22 +166,32 @@ void CategoryFactory::saveCategories()
|
||||
unsigned int CategoryFactory::countCategories(std::tr1::function<bool (const Category &category)> filter)
|
||||
{
|
||||
unsigned int result = 0;
|
||||
for (auto iter = m_Categories.begin(); iter != m_Categories.end(); ++iter) {
|
||||
if (filter(*iter)) {
|
||||
for (const Category &cat : m_Categories) {
|
||||
if (filter(cat)) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int CategoryFactory::addCategory(const QString &name, const std::vector<int> &nexusIDs, int parentID)
|
||||
{
|
||||
int id = 1;
|
||||
while (m_IDMap.find(id) != m_IDMap.end()) {
|
||||
++id;
|
||||
}
|
||||
addCategory(id, name, nexusIDs, parentID);
|
||||
|
||||
saveCategories();
|
||||
return id;
|
||||
}
|
||||
|
||||
void CategoryFactory::addCategory(int id, const QString &name, const std::vector<int> &nexusIDs, int parentID)
|
||||
{
|
||||
int index = m_Categories.size();
|
||||
m_Categories.push_back(Category(index, id, name, nexusIDs, parentID));
|
||||
for (std::vector<int>::const_iterator iter = nexusIDs.begin();
|
||||
iter != nexusIDs.end(); ++iter) {
|
||||
m_NexusMap[*iter] = index;
|
||||
for (int nexusID : nexusIDs) {
|
||||
m_NexusMap[nexusID] = index;
|
||||
}
|
||||
m_IDMap[id] = index;
|
||||
}
|
||||
@@ -305,6 +315,18 @@ int CategoryFactory::getCategoryIndex(int ID) const
|
||||
}
|
||||
|
||||
|
||||
int CategoryFactory::getCategoryID(const QString &name) const
|
||||
{
|
||||
auto iter = std::find_if(m_Categories.begin(), m_Categories.end(), [name] (const Category &cat) -> bool {
|
||||
return cat.m_Name == name;
|
||||
});
|
||||
if (iter != m_Categories.end()) {
|
||||
return iter->m_ID;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned int CategoryFactory::resolveNexusID(int nexusID) const
|
||||
{
|
||||
|
||||
@@ -80,6 +80,8 @@ public:
|
||||
**/
|
||||
void saveCategories();
|
||||
|
||||
int addCategory(const QString &name, const std::vector<int> &nexusIDs, int parentID);
|
||||
|
||||
/**
|
||||
* @brief retrieve the number of available categories
|
||||
*
|
||||
@@ -142,6 +144,12 @@ public:
|
||||
**/
|
||||
int getCategoryID(unsigned int index) const;
|
||||
|
||||
/**
|
||||
* @brief look up the id of a category by its name
|
||||
* @note O(n)
|
||||
*/
|
||||
int getCategoryID(const QString &name) const;
|
||||
|
||||
/**
|
||||
* @brief look up the index of a category by its id
|
||||
*
|
||||
|
||||
@@ -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,23 +94,27 @@ 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));
|
||||
|
||||
if (stealFiles.length() > 0) {
|
||||
// instead of adding all the files of the target directory, we just change the root of the specified
|
||||
// files to this mod
|
||||
FilesOrigin origin = directoryStructure->createOrigin(ToWString(modName), directoryW, priority);
|
||||
foreach (const QString &filename, stealFiles) {
|
||||
FilesOrigin &origin = directoryStructure->createOrigin(ToWString(modName), directoryW, priority);
|
||||
for (const QString &filename : stealFiles) {
|
||||
QFileInfo fileInfo(filename);
|
||||
FileEntry::Ptr file = directoryStructure->findFile(ToWString(fileInfo.fileName()));
|
||||
if (file.get() != nullptr) {
|
||||
if (file->getOrigin() == 0) {
|
||||
// replace data as the origin on this bsa
|
||||
file->removeOrigin(0);
|
||||
file->addOrigin(origin.getID(), file->getFileTime(), L"");
|
||||
}
|
||||
origin.addFile(file->getIndex());
|
||||
file->addOrigin(origin.getID(), file->getFileTime(), L"");
|
||||
} else {
|
||||
qWarning("%s not found", qPrintable(fileInfo.fileName()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -86,7 +86,7 @@ private:
|
||||
|
||||
quint32 m_TaskProgressId;
|
||||
|
||||
MOBase::ModRepositoryFileInfo *m_FileInfo;
|
||||
MOBase::ModRepositoryFileInfo *m_FileInfo { nullptr };
|
||||
|
||||
bool m_Hidden;
|
||||
|
||||
|
||||
@@ -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">
|
||||
@@ -26,7 +26,7 @@
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::MoveAction</enum>
|
||||
<enum>Qt::TargetMoveAction</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
@@ -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
|
||||
|
||||
+38
-43
@@ -114,22 +114,26 @@ void InstallationManager::queryPassword(LPSTR password)
|
||||
void InstallationManager::mapToArchive(const DirectoryTree::Node *node, std::wstring path, FileData * const *data)
|
||||
{
|
||||
if (path.length() > 0) {
|
||||
path.append(L"\\");
|
||||
// when using a long windows path (starting with \\?\) we apparently can have redundant
|
||||
// . components in the path. This wasn't a problem with "regular" path names.
|
||||
if (path == L".") {
|
||||
path.clear();
|
||||
} else {
|
||||
path.append(L"\\");
|
||||
}
|
||||
}
|
||||
|
||||
for (DirectoryTree::const_leaf_iterator iter = node->leafsBegin(); iter != node->leafsEnd(); ++iter) {
|
||||
data[iter->getIndex()]->setSkip(false);
|
||||
std::wstring temp = path.substr().append(ToWString(iter->getName()));
|
||||
data[iter->getIndex()]->setOutputFileName(temp.c_str());
|
||||
std::wstring temp = path + iter->getName().toStdWString();
|
||||
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.substr().append(ToWString((*iter)->getData().name));
|
||||
data[(*iter)->getData().index]->setOutputFileName(temp.c_str());
|
||||
data[(*iter)->getData().index]->addOutputFileName(temp.c_str());
|
||||
}
|
||||
mapToArchive(*iter, path.substr().append(ToWString((*iter)->getData().name)), data);
|
||||
mapToArchive(*iter, temp, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,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);
|
||||
@@ -163,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -245,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,8 +528,8 @@ bool InstallationManager::doInstall(GuessedValue<QString> &modName, int modID,
|
||||
return false;
|
||||
}
|
||||
|
||||
QString targetDirectoryNative = m_ModsDirectory + "\\" + modName;
|
||||
QString targetDirectory = QDir::fromNativeSeparators(targetDirectoryNative);
|
||||
QString targetDirectory = QDir(m_ModsDirectory + "/" + modName).canonicalPath();
|
||||
QString targetDirectoryNative = QDir::toNativeSeparators(targetDirectory);
|
||||
|
||||
qDebug("installing to \"%s\"", targetDirectoryNative.toUtf8().constData());
|
||||
|
||||
@@ -545,7 +538,7 @@ bool InstallationManager::doInstall(GuessedValue<QString> &modName, int modID,
|
||||
m_InstallationProgress.setValue(0);
|
||||
m_InstallationProgress.setWindowModality(Qt::WindowModal);
|
||||
m_InstallationProgress.show();
|
||||
if (!m_CurrentArchive->extract(ToWString("\\\\?\\" + QDir::toNativeSeparators(targetDirectory)).c_str(),
|
||||
if (!m_CurrentArchive->extract(ToWString("\\\\?\\" + targetDirectoryNative).c_str(),
|
||||
new MethodCallback<InstallationManager, void, float>(this, &InstallationManager::updateProgress),
|
||||
new MethodCallback<InstallationManager, void, LPCWSTR>(this, &InstallationManager::updateProgressFile),
|
||||
new MethodCallback<InstallationManager, void, LPCWSTR>(this, &InstallationManager::report7ZipError))) {
|
||||
@@ -598,7 +591,7 @@ bool InstallationManager::wasCancelled()
|
||||
}
|
||||
|
||||
|
||||
void InstallationManager::postInstallCleanup() const
|
||||
void InstallationManager::postInstallCleanup()
|
||||
{
|
||||
m_CurrentArchive->close();
|
||||
|
||||
@@ -612,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);
|
||||
@@ -629,7 +624,7 @@ bool InstallationManager::install(const QString &fileName, GuessedValue<QString>
|
||||
{
|
||||
QFileInfo fileInfo(fileName);
|
||||
if (m_SupportedExtensions.find(fileInfo.suffix()) == m_SupportedExtensions.end()) {
|
||||
reportError(tr("File format \"%1\" not supported").arg(fileInfo.completeSuffix()));
|
||||
reportError(tr("File format \"%1\" not supported").arg(fileInfo.suffix()));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ private:
|
||||
|
||||
bool ensureValidModName(MOBase::GuessedValue<QString> &name) const;
|
||||
|
||||
void postInstallCleanup() const;
|
||||
void postInstallCleanup();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
+54
-42
@@ -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>
|
||||
@@ -172,9 +173,12 @@ void cleanupDir()
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,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);
|
||||
@@ -246,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;
|
||||
@@ -338,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()) {
|
||||
@@ -367,23 +368,29 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!bootstrap()) { // requires gameinfo to be initialised!
|
||||
return -1;
|
||||
}
|
||||
|
||||
LogBuffer::init(100, QtDebugMsg, qApp->property("dataPath").toString() + "/logs/mo_interface.log");
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
qDebug("ssl support: %d", QSslSocket::supportsSsl());
|
||||
#endif
|
||||
|
||||
qDebug("Working directory: %s", qPrintable(QDir::toNativeSeparators(QDir::currentPath())));
|
||||
qDebug("MO at: %s", qPrintable(QDir::toNativeSeparators(application.applicationDirPath())));
|
||||
QPixmap pixmap(":/MO/gui/splash");
|
||||
QSplashScreen splash(pixmap);
|
||||
splash.show();
|
||||
|
||||
cleanupDir();
|
||||
try {
|
||||
if (!bootstrap()) { // requires gameinfo to be initialised!
|
||||
return -1;
|
||||
}
|
||||
|
||||
LogBuffer::init(100, QtDebugMsg, qApp->property("dataPath").toString() + "/logs/mo_interface.log");
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
qDebug("ssl support: %d", QSslSocket::supportsSsl());
|
||||
#endif
|
||||
|
||||
qDebug("Working directory: %s", qPrintable(QDir::toNativeSeparators(QDir::currentPath())));
|
||||
qDebug("MO at: %s", qPrintable(QDir::toNativeSeparators(application.applicationDirPath())));
|
||||
splash.show();
|
||||
|
||||
cleanupDir();
|
||||
} catch (const std::exception &e) {
|
||||
reportError(e.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
{ // extend path to include dll directory so plugins don't need a manifest
|
||||
// (using AddDllDirectory would be an alternative to this but it seems fairly complicated esp.
|
||||
@@ -406,8 +413,6 @@ int main(int argc, char *argv[])
|
||||
::SetEnvironmentVariableW(L"PATH", newPath.c_str());
|
||||
}
|
||||
|
||||
registerMetaTypes();
|
||||
|
||||
QStringList arguments = application.arguments();
|
||||
|
||||
bool forcePrimary = false;
|
||||
@@ -430,9 +435,9 @@ int main(int argc, char *argv[])
|
||||
} // we continue for the primary instance OR if MO has been called with parameters
|
||||
|
||||
QSettings settings(dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName()), QSettings::IniFormat);
|
||||
|
||||
qDebug("initializing core");
|
||||
OrganizerCore organizer(settings);
|
||||
|
||||
qDebug("initialize plugins");
|
||||
PluginContainer pluginContainer(&organizer);
|
||||
pluginContainer.loadPlugins();
|
||||
|
||||
@@ -491,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)));
|
||||
|
||||
@@ -532,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);
|
||||
|
||||
+184
-119
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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
+65
-27
@@ -18,6 +18,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "modinfo.h"
|
||||
|
||||
#include "utility.h"
|
||||
#include "installationtester.h"
|
||||
#include "categories.h"
|
||||
@@ -26,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "overwriteinfodialog.h"
|
||||
#include "json.h"
|
||||
#include "messagedialog.h"
|
||||
#include "filenamestring.h"
|
||||
|
||||
#include <gameinfo.h>
|
||||
#include <iplugingame.h>
|
||||
@@ -301,6 +303,40 @@ void ModInfo::setVersion(const VersionInfo &version)
|
||||
m_Version = version;
|
||||
}
|
||||
|
||||
void ModInfo::addCategory(const QString &categoryName)
|
||||
{
|
||||
int id = CategoryFactory::instance().getCategoryID(categoryName);
|
||||
if (id == -1) {
|
||||
id = CategoryFactory::instance().addCategory(categoryName, std::vector<int>(), 0);
|
||||
}
|
||||
setCategory(id, true);
|
||||
}
|
||||
|
||||
bool ModInfo::removeCategory(const QString &categoryName)
|
||||
{
|
||||
int id = CategoryFactory::instance().getCategoryID(categoryName);
|
||||
if (id == -1) {
|
||||
return false;
|
||||
}
|
||||
if (!categorySet(id)) {
|
||||
return false;
|
||||
}
|
||||
setCategory(id, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList ModInfo::categories()
|
||||
{
|
||||
QStringList result;
|
||||
|
||||
CategoryFactory &catFac = CategoryFactory::instance();
|
||||
for (int id : m_Categories) {
|
||||
result.append(catFac.getCategoryName(catFac.getCategoryIndex(id)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ModInfo::hasFlag(ModInfo::EFlag flag) const
|
||||
{
|
||||
std::vector<EFlag> flags = getFlags();
|
||||
@@ -386,7 +422,8 @@ void ModInfoWithConflictInfo::doConflictCheck() const
|
||||
{
|
||||
m_OverwriteList.clear();
|
||||
m_OverwrittenList.clear();
|
||||
bool regular = false;
|
||||
|
||||
bool providesAnything = false;
|
||||
|
||||
int dataID = 0;
|
||||
if ((*m_DirectoryStructure)->originExists(L"data")) {
|
||||
@@ -394,26 +431,31 @@ void ModInfoWithConflictInfo::doConflictCheck() const
|
||||
}
|
||||
|
||||
std::wstring name = ToWString(this->name());
|
||||
|
||||
m_CurrentConflictState = CONFLICT_NONE;
|
||||
|
||||
if ((*m_DirectoryStructure)->originExists(name)) {
|
||||
FilesOrigin &origin = (*m_DirectoryStructure)->getOriginByName(name);
|
||||
std::vector<FileEntry::Ptr> files = origin.getFiles();
|
||||
// for all files in this origin
|
||||
for (auto iter = files.begin(); iter != files.end(); ++iter) {
|
||||
const std::vector<int> &alternatives = (*iter)->getAlternatives();
|
||||
if ((alternatives.size() == 0)
|
||||
|| (alternatives[0] == dataID)) {
|
||||
for (FileEntry::Ptr file : files) {
|
||||
const std::vector<int> &alternatives = file->getAlternatives();
|
||||
if ((alternatives.size() == 0) || (alternatives[0] == dataID)) {
|
||||
// no alternatives -> no conflict
|
||||
regular = true;
|
||||
providesAnything = true;
|
||||
} else {
|
||||
if ((*iter)->getOrigin() != origin.getID()) {
|
||||
FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID((*iter)->getOrigin());
|
||||
if (file->getOrigin() != origin.getID()) {
|
||||
FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(file->getOrigin());
|
||||
unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName()));
|
||||
m_OverwrittenList.insert(altIndex);
|
||||
} else {
|
||||
providesAnything = true;
|
||||
}
|
||||
|
||||
// for all non-providing alternative origins
|
||||
for (auto altIter = alternatives.begin(); altIter != alternatives.end(); ++altIter) {
|
||||
if ((*altIter != dataID) && (*altIter != origin.getID())) {
|
||||
FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(*altIter);
|
||||
for (int altId : alternatives) {
|
||||
if ((altId != dataID) && (altId != origin.getID())) {
|
||||
FilesOrigin &altOrigin = (*m_DirectoryStructure)->getOriginByID(altId);
|
||||
unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName()));
|
||||
if (origin.getPriority() > altOrigin.getPriority()) {
|
||||
m_OverwriteList.insert(altIndex);
|
||||
@@ -424,22 +466,19 @@ void ModInfoWithConflictInfo::doConflictCheck() const
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_LastConflictCheck = QTime::currentTime();
|
||||
|
||||
m_LastConflictCheck = QTime::currentTime();
|
||||
|
||||
if (!m_OverwriteList.empty() && !m_OverwrittenList.empty())
|
||||
m_CurrentConflictState = CONFLICT_MIXED;
|
||||
else if (!m_OverwriteList.empty())
|
||||
m_CurrentConflictState = CONFLICT_OVERWRITE;
|
||||
else if (!m_OverwrittenList.empty()) {
|
||||
if (!regular) {
|
||||
m_CurrentConflictState = CONFLICT_REDUNDANT;
|
||||
} else {
|
||||
m_CurrentConflictState = CONFLICT_OVERWRITTEN;
|
||||
if (files.size() != 0) {
|
||||
if (!providesAnything)
|
||||
m_CurrentConflictState = CONFLICT_REDUNDANT;
|
||||
else if (!m_OverwriteList.empty() && !m_OverwrittenList.empty())
|
||||
m_CurrentConflictState = CONFLICT_MIXED;
|
||||
else if (!m_OverwriteList.empty())
|
||||
m_CurrentConflictState = CONFLICT_OVERWRITE;
|
||||
else if (!m_OverwrittenList.empty())
|
||||
m_CurrentConflictState = CONFLICT_OVERWRITTEN;
|
||||
}
|
||||
}
|
||||
else m_CurrentConflictState = CONFLICT_NONE;
|
||||
}
|
||||
|
||||
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isConflicted() const
|
||||
@@ -971,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;
|
||||
@@ -1027,7 +1066,6 @@ ModInfoBackup::ModInfoBackup(const QDir &path, DirectoryEntry **directoryStructu
|
||||
|
||||
|
||||
ModInfoOverwrite::ModInfoOverwrite()
|
||||
: m_StartupTime(QDateTime::currentDateTime())
|
||||
{
|
||||
testValid();
|
||||
}
|
||||
@@ -1069,7 +1107,7 @@ QStringList ModInfoOverwrite::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;
|
||||
|
||||
+5
-5
@@ -300,6 +300,10 @@ public:
|
||||
*/
|
||||
virtual void addNexusCategory(int categoryID) = 0;
|
||||
|
||||
virtual void addCategory(const QString &categoryName) override;
|
||||
virtual bool removeCategory(const QString &categoryName) override;
|
||||
virtual QStringList categories() override;
|
||||
|
||||
/**
|
||||
* update the endorsement state for the mod. This only changes the
|
||||
* buffered state, it does not sync with Nexus
|
||||
@@ -1041,7 +1045,7 @@ public:
|
||||
virtual bool isEmpty() const;
|
||||
virtual QString name() const { return "Overwrite"; }
|
||||
virtual QString notes() const { return ""; }
|
||||
virtual QDateTime creationTime() const { return m_StartupTime; }
|
||||
virtual QDateTime creationTime() const { return QDateTime(); }
|
||||
virtual QString absolutePath() const;
|
||||
virtual MOBase::VersionInfo getNewestVersion() const { return ""; }
|
||||
virtual QString getInstallationFile() const { return ""; }
|
||||
@@ -1060,10 +1064,6 @@ private:
|
||||
|
||||
ModInfoOverwrite();
|
||||
|
||||
private:
|
||||
|
||||
QDateTime m_StartupTime;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -395,7 +395,6 @@ const int ModInfoDialog::getModID() const
|
||||
return m_Settings->value("modid", 0).toInt();
|
||||
}
|
||||
|
||||
|
||||
void ModInfoDialog::openTab(int tab)
|
||||
{
|
||||
QTabWidget *tabWidget = findChild<QTabWidget*>("tabWidget");
|
||||
@@ -407,16 +406,16 @@ void ModInfoDialog::openTab(int tab)
|
||||
void ModInfoDialog::thumbnailClicked(const QString &fileName)
|
||||
{
|
||||
QLabel *imageLabel = findChild<QLabel*>("imageLabel");
|
||||
imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
|
||||
QImage image(fileName);
|
||||
if (static_cast<float>(image.width()) / static_cast<float>(image.height()) > 1.34) {
|
||||
image = image.scaledToWidth(imageLabel->width());
|
||||
image = image.scaledToWidth(imageLabel->geometry().width());
|
||||
} else {
|
||||
image = image.scaledToHeight(imageLabel->height());
|
||||
image = image.scaledToHeight(imageLabel->geometry().height());
|
||||
}
|
||||
imageLabel->setPixmap(QPixmap::fromImage(image));
|
||||
}
|
||||
|
||||
|
||||
bool ModInfoDialog::allowNavigateFromTXT()
|
||||
{
|
||||
if (ui->saveTXTButton->isEnabled()) {
|
||||
|
||||
+35
-13
@@ -23,6 +23,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "installationtester.h"
|
||||
#include "qtgroupingproxy.h"
|
||||
#include "viewmarkingscrollbar.h"
|
||||
#include "modlistsortproxy.h"
|
||||
#include <gameinfo.h>
|
||||
#include <appconfig.h>
|
||||
#include <utility.h>
|
||||
@@ -246,7 +247,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
|
||||
}
|
||||
} else if (column == COL_INSTALLTIME) {
|
||||
// display installation time for mods that can be updated
|
||||
if (modInfo->canBeUpdated()) {
|
||||
if (modInfo->creationTime().isValid()) {
|
||||
return modInfo->creationTime();
|
||||
} else {
|
||||
return QVariant();
|
||||
@@ -360,11 +361,9 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
|
||||
if (column == COL_FLAGS) {
|
||||
QString result;
|
||||
|
||||
std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
|
||||
|
||||
for (auto iter = flags.begin(); iter != flags.end(); ++iter) {
|
||||
for (ModInfo::EFlag flag : modInfo->getFlags()) {
|
||||
if (result.length() != 0) result += "<br>";
|
||||
result += getFlagText(*iter, modInfo);
|
||||
result += getFlagText(flag, modInfo);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -452,6 +451,8 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
|
||||
bool result = false;
|
||||
|
||||
emit aboutToChangeData();
|
||||
|
||||
if (role == Qt::CheckStateRole) {
|
||||
bool enabled = value.toInt() == Qt::Checked;
|
||||
if (m_Profile->modEnabled(modID) != enabled) {
|
||||
@@ -511,6 +512,8 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
}
|
||||
}
|
||||
|
||||
emit postDataChanged();
|
||||
|
||||
IModList::ModStates newState = state(modID);
|
||||
if (oldState != newState) {
|
||||
try {
|
||||
@@ -566,7 +569,8 @@ Qt::ItemFlags ModList::flags(const QModelIndex &modelIndex) const
|
||||
}
|
||||
}
|
||||
std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
|
||||
if ((m_DropOnItems) && (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) == flags.end())) {
|
||||
if (m_DropOnItems
|
||||
&& (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) == flags.end())) {
|
||||
result |= Qt::ItemIsDropEnabled;
|
||||
}
|
||||
} else {
|
||||
@@ -583,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)
|
||||
{
|
||||
@@ -655,6 +665,7 @@ void ModList::modInfoChanged(ModInfo::Ptr info)
|
||||
if (m_ChangeInfo.state != newState) {
|
||||
m_ModStateChanged(info->name(), newState);
|
||||
}
|
||||
|
||||
int row = ModInfo::getIndex(info->name());
|
||||
info->testValid();
|
||||
emit dataChanged(index(row, 0), index(row, columnCount()));
|
||||
@@ -819,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)
|
||||
{
|
||||
|
||||
@@ -872,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1090,15 +1103,23 @@ bool ModList::deleteSelection(QAbstractItemView *itemView)
|
||||
|
||||
bool ModList::toggleSelection(QAbstractItemView *itemView)
|
||||
{
|
||||
QAbstractItemModel *model = itemView->model();
|
||||
emit aboutToChangeData();
|
||||
|
||||
QItemSelectionModel *selectionModel = itemView->selectionModel();
|
||||
|
||||
for (QModelIndex idx : selectionModel->selectedRows()) {
|
||||
int oldState = idx.data(Qt::CheckStateRole).toInt();
|
||||
model->setData(idx, oldState == Qt::Unchecked ? Qt::Checked
|
||||
: Qt::Unchecked,
|
||||
Qt::CheckStateRole);
|
||||
int modId = idx.data(Qt::UserRole + 1).toInt();
|
||||
m_Profile->setModEnabled(modId, !m_Profile->modEnabled(modId));
|
||||
emit modlist_changed(idx, 0);
|
||||
}
|
||||
|
||||
m_Modified = true;
|
||||
m_LastCheck.restart();
|
||||
|
||||
emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
|
||||
|
||||
emit postDataChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1128,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);
|
||||
|
||||
@@ -237,6 +238,10 @@ signals:
|
||||
*/
|
||||
void fileMoved(const QString &relativePath, const QString &oldOriginName, const QString &newOriginName);
|
||||
|
||||
void aboutToChangeData();
|
||||
|
||||
void postDataChanged();
|
||||
|
||||
protected:
|
||||
|
||||
// event filter, handles event from the header and the tree view itself
|
||||
|
||||
@@ -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.
|
||||
@@ -376,3 +398,36 @@ bool ModListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action
|
||||
return this->sourceModel()->dropMimeData(data, action, sourceIndex.row(), sourceIndex.column(),
|
||||
sourceIndex.parent());
|
||||
}
|
||||
|
||||
void ModListSortProxy::setSourceModel(QAbstractItemModel *sourceModel)
|
||||
{
|
||||
QSortFilterProxyModel::setSourceModel(sourceModel);
|
||||
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()
|
||||
{
|
||||
// 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();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user