Compare commits

...
Author SHA1 Message Date
Tannin 4573d111a2 better sorting on the flag-column 2015-09-03 21:35:48 +02:00
Tannin 84ec954073 more intuitive sorting with grouping active 2015-09-03 21:35:04 +02:00
Tannin 63188eb6f3 Merge 2015-09-03 19:34:53 +02:00
Tannin 0a51fdf08e DLC bsas are now auto-enabled for pre-skyrim games 2015-09-03 19:33:33 +02:00
Tannin cb315b4179 bugfix: "add" button in editexecutables dialog didn't work 2015-09-03 19:32:40 +02:00
Tom Tanner d96d904765 Fix not recognising 'add' (1308) 2015-09-02 22:13:24 +01:00
Tannin 9588667404 Added tag release v1.3.10 for changeset 8638428b1ebc 2015-09-01 20:31:32 +02:00
Tannin b008753e1d workaround: after editing in a filtered view, if the edited item disappears as
a result of the edit, no further editor could be opened
2015-09-01 19:49:28 +02:00
Tannin 3c7ab3b668 Merge 2015-08-31 21:26:47 +02:00
Tannin d85019f4b1 auto-discovered executables aren't turned into custom ones if the user presses
"Modify" without doing any changes
2015-08-31 21:25:43 +02:00
Tannin 05edcf8840 code cleanup 2015-08-31 21:24:48 +02:00
Tannin 2a22461f14 bugfix: auto-discovered executables couldn't be customized 2015-08-31 21:24:04 +02:00
Tom Tanner 0c3c822d77 Stop the temp files to delete list growing on each install 2015-08-30 13:36:11 +01:00
Tom Tanner f0ee9c3e4d Merge 2015-08-28 20:04:15 +01:00
Tannin 83edfe0c96 Merge 2015-08-27 20:59:37 +02:00
Tannin 86d833bcc8 bugfix: crash on network connection problem 2015-08-27 20:58:39 +02:00
Tannin 58198a6bfd bugfix: update.bsa sorted incorrectly 2015-08-27 20:58:01 +02:00
Tom Tanner 32339addbf Fix nasty message when starting MO for first time 2015-08-22 22:53:13 +01:00
Tannin dd875c05de removed obsolete code 2015-08-17 20:56:53 +02:00
Tannin 17938b503a Merge 2015-08-17 20:52:12 +02:00
Tannin 269d4f6765 bugfix: wrong app ids used for oblivion and fallout 3
rewrote handling of different game variants: now resides in game plugin and works
2015-08-17 20:50:47 +02:00
Tom Tanner 0fa7f7469f Merge 2015-08-15 16:52:18 +01:00
Tannin ea1c2b46fe Merge 2015-08-15 12:37:19 +02:00
Tannin 89f3c0d65a more verbosity about login/logout to/from nexus 2015-08-15 12:06:26 +02:00
Tom Tanner 821f95b8ab Clean up of fixes for custom executable window 2015-08-13 22:37:33 +01:00
Tom Tanner 716dbcc97d Merge 2015-08-13 21:48:33 +01:00
Tom Tanner 8e7ac4e064 Fix the executable configuration flags going awry from Customise window 2015-08-13 20:07:21 +01:00
Tannin 69a8a40b99 bugfix: categories that reference each other in a cycle could send MO into an endless loop 2015-08-12 21:50:06 +02:00
Tannin 229161565e some fixes after reverting changes in c361167b9b75 2015-08-12 21:48:51 +02:00
Tom Tanner 9c3b390f08 Fix up merge mess 2015-08-11 21:53:00 +01:00
Tom Tanner 5244544424 Merge 2015-08-11 21:35:06 +01:00
Tannin 217ca284fe Merge 2015-08-11 19:31:48 +02:00
Tannin 0aecc3427a small bugfix 2015-08-11 19:23:12 +02:00
Tannin b6627ff5e0 small code cleanup 2015-08-09 17:11:18 +02:00
Tom Tanner b0d86c9e01 Merge 2015-08-09 15:56:39 +01:00
Tannin a7d6eb75c2 reverted most changes of changeset 626da419828a as I don't agree with them 2015-08-09 15:16:57 +02:00
Tannin 6f09cf2205 Merge 2015-08-09 13:51:17 +02:00
Tannin e6abe486a7 added some (currently unused) functionality to dump the list of file mappings
from real to virtual location
2015-08-09 13:04:01 +02:00
Tannin 52fcfaa292 some code cleanup 2015-08-09 12:52:30 +02:00
Tannin 4230313ef5 now displays currently logged in account in title bar 2015-08-09 12:51:19 +02:00
Tannin ff8057d34b - MO now validates session cookie on startup
- It also retrieves account status
- rewrote how MO decides when to log-in/when to give up on logging in
2015-08-09 12:40:57 +02:00
Tom Tanner b098214860 More work on the executables list
Apart from an off by one in the popup list which had crept back in, reworked
the underlying code even more, which now seems to get the list behaving in
a predictable way.
2015-08-05 22:40:49 +01:00
Tannin 8cf429a1dd - bugfix: manual installer now cancels drag&drop when dropping on a subitem
- bugfix: objects dropped on the modlist were treated as mods when they weren't urls
2015-08-05 22:48:34 +02:00
Tom Tanner 9f7bec01d0 Merge 2015-08-02 21:58:02 +01:00
Tannin 643a50ad5a - changed the way flags on executables are stored in memory
- some coding style fixes
2015-08-02 20:09:56 +02:00
Tom Tanner eaaa1b312d Extract multiple copies of file from archive
Had to rewrite the archive handling a bit. First pass, some tidyups required.
2015-08-02 16:25:32 +01:00
Tannin 9c17ecc87e Merge 2015-08-02 14:58:17 +02:00
Tannin 051589c38e bugfix: archive list wasn't saved after enabling/disabling a mod. 2015-07-31 14:18:44 +02:00
Tom Tanner 48fae44566 Fix up selection with exactly/at least/at most one 2015-07-18 20:57:37 +01:00
Tom Tanner e1c8d00979 Reworked executable lists to not use 3 bools in addExecutable
Think it's more readable
2015-07-18 13:34:10 +01:00
Tom Tanner 0503097494 Allow user to select app icon or mo icon for generated shortcuts
Also fixes 'x' for toolbar not disappearing when it should
Issue 701
2015-07-18 10:44:49 +01:00
Tom Tanner 3a39f90870 small patch to show which obsolete files are being removed 2015-07-17 22:12:46 +01:00
Tom Tanner 18ad656586 Merge 2015-07-09 18:58:57 +01:00
Tom Tanner eaafc489fc Reworking the Custom Executables list and the shortcut popup.
Certainly fixes the issue where the 'toolbar' entry doesn't lose the cross.

One of the 'find' methods ignored case but none of the others did, so I've
made it not ignore case either.

Also made the code to get the desktop/startmenu paths use a more up-to-date API
2015-07-09 18:58:34 +01:00
Tannin 7799c348ba defining PSAPI_VERSION to 1 may allow MO 1.3 to run on Vista again. 2015-07-07 20:57:20 +02:00
Tannin 0a1f3efac5 filter names in the filter pane now also show up as tooltips 2015-07-07 20:54:18 +02:00
Tannin 1eea37e4e1 small code beautification 2015-07-07 20:53:38 +02:00
Tannin 9a5965afa7 bugfix: invalidation bsa wasn't created when switching to a profile that needed it
invalidation bsa is now always placed as the first bsa
2015-07-07 20:52:46 +02:00
Tannin 7853ba5f9a Merge 2015-06-30 19:57:59 +02:00
Tannin 90dcc81715 incremented version number 2015-06-30 19:57:46 +02:00
Tannin 1e75fd8dae bugfix: dropping files from overwrite on mod failed whien mod grouping was active 2015-06-29 20:38:16 +02:00
Tannin 61033814fd bugfix: aboutToChangeData and postDataChanged signals were not correctly set up when mod grouping was enabled 2015-06-29 20:28:56 +02:00
Tom Tanner ca591dbd23 and now I find out I don't need to do it at all 2015-06-23 20:27:38 +01:00
Tom Tanner 7602fef6e7 Finish refactoring all the tab code into classes 2015-06-21 18:27:03 +01:00
Tom Tanner 67b1f9dca2 Merge 2015-06-21 07:51:49 +01:00
Tom Tanner 5ae279d8bb More refactoring, slightly easier to use classes 2015-06-21 07:51:05 +01:00
Tom Tanner 81ef3d0a81 Factor out general, nexis, plugins tab code into individual classes
Add some code and page to control what is/isn't significant in overwrite
2015-06-20 15:37:32 +01:00
Tannin dc0e67a8b3 Added tag release v1.3.8 for changeset 04197ab95dcd 2015-06-20 13:55:39 +02:00
Tannin aa21a192cb bugfix: fix in changeset f4c306711386 wasn't fully working 2015-06-20 13:55:29 +02:00
Tannin d4d7ce1fd3 small stuff 2015-06-17 18:33:28 +02:00
Tannin e31baf3ee6 - bugfix: MO crashed without error message when "bootstrapping" failed 2015-06-17 18:33:16 +02:00
Tannin 5dcfca7c9d bugfix: disabled mods were marked "redundant" 2015-06-15 20:01:28 +02:00
Tannin e9ee7e7f85 bugfix: dialog-memory wasn't initialized when starting through a desktop link so
access (in case steam wasn't running) caused a crash
2015-06-15 20:00:27 +02:00
Tannin 67d017fc98 fix: some names in about dialog were marked translatable 2015-06-15 19:58:57 +02:00
Tannin a700e6a3ef Added tag release v1.3.7 for changeset e857b583c68f 2015-06-13 16:21:21 +02:00
Tannin c95de1b3aa small stuff 2015-06-12 18:32:50 +02:00
Tannin 351fc9ca9c fixed display of installation time for mods without nexus id 2015-06-11 18:54:59 +02:00
Tannin 53f0a2bc79 bugfix: opening a foreign mod info dialog caused the content to be "forgotten" 2015-06-11 18:54:20 +02:00
Tannin 793dfb1637 added plugin interface to add/remove/list categories to/from/of mods 2015-06-10 22:09:32 +02:00
Tannin d25a81071c more generic solution to the problem fixed in changeset bb74f8eb639c, now also applied in another location 2015-06-10 21:22:54 +02:00
Tannin ef9a61886c a mod that overwrites another yet is completely overwritten by another is now displayed as "redundant" 2015-06-10 21:21:32 +02:00
Tannin 13a40b43c8 bugfix: files weren't correctly assigned to unmanaged mods. 2015-06-10 21:20:31 +02:00
Tannin d7790cd717 incorrect use of "complete" file suffixes 2015-06-09 20:40:13 +02:00
Tannin 84d632af6a fixes regarding path name generation during mod installation 2015-06-09 20:39:17 +02:00
Tannin b14b35349d post-merge cleanup 2015-06-08 19:55:00 +02:00
Tannin 83a4db2362 Merge 2015-06-08 18:29:49 +02:00
Tannin c797af8b76 increased ui responsiveness while integrated loot is running 2015-06-08 18:28:59 +02:00
Tannin ebf6ff3f76 workaround to prevent crashes when toggling mods causes the filtered list to change 2015-06-08 18:28:20 +02:00
Tannin 02b5e73c2d bugfix: potential null-pointer access when something goes wrong enabling a mod 2015-06-08 17:27:34 +02:00
Tom Tanner b0b2f1d3a9 Make the about dialog come up on the first tab 2015-06-07 13:37:41 +01:00
Tom Tanner d8e406daa0 Merge 2015-06-06 15:58:51 +01:00
Tom Tanner 50dcc4c1d4 Addition of FileNameString clase which ignores case during compares.
Affects rather a lot of stuff.
2015-06-06 15:56:44 +01:00
Tannin e21db0eaea bugfix: moving an executable in the executable list caused that executable
to disappear
2015-06-04 12:50:35 +02:00
Tannin 859332e18d a little more logging during startup 2015-06-04 12:50:11 +02:00
Tannin 1fd8f44960 bugfix: selecting an image in the modinfo dialog caused the image label
to grow slightly each time
2015-06-04 12:49:47 +02:00
Tannin e0c655330d Added tag release v1.3.6 for changeset 7bed8faa975a 2015-05-31 00:36:58 +02:00
Tannin a0999cd42b Removed tag release v1.3.6 2015-05-31 00:36:56 +02:00
40 changed files with 1404 additions and 807 deletions
+12 -6
View File
@@ -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
View File
@@ -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
{
+8
View File
@@ -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
*
+11 -6
View File
@@ -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 {
+1 -1
View File
@@ -86,7 +86,7 @@ private:
quint32 m_TaskProgressId;
MOBase::ModRepositoryFileInfo *m_FileInfo;
MOBase::ModRepositoryFileInfo *m_FileInfo { nullptr };
bool m_Hidden;
+37 -25
View File
@@ -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());
}
}
+26 -15
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -177,7 +177,7 @@ private:
bool ensureValidModName(MOBase::GuessedValue<QString> &name) const;
void postInstallCleanup() const;
void postInstallCleanup();
private:
+54 -42
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+12 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
+3 -4
View File
@@ -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
View File
@@ -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);
}
+5
View File
@@ -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
+59 -4
View File
@@ -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