Compare commits

...
Author SHA1 Message Date
Tannin c57b172204 - removed some obsolete code
- MO will no longer start an application while the directory structure is being refreshed because MO may need to access profile files afterwards
- bugfix: the overwrite info-dialog was not destroyed and could thus keep a lock on files thus preventing those files from being moved/deleted
2013-09-22 13:10:21 +02:00
Tannin dfca8be71b - esp reader now handles invalid files more gracefully
- files moved will now also be treated as "deleted" in the old location so a newly created file with that same name is not created in overwrite
- introduced a mechanism by which MO can recognize if it crashed before when attempting to load a plugin. That plugin can be blacklisted so it doesn't get loaded again
- plugins can now programaticaly change their settings
- plugins can now store data persistently without exposing that data as settings
- requesting an unset-setting from a plugin is no longer treated as a bug
- clarified warning message for when files are in overwrite directory
- the proxyPython plugin will now discover if python initialization crashed MO on a previous session and give the user a chance to fix it or disable the plugin
- bugfix: GetModuleFileName modified the buffer past the zero termination. While this doesn't violate the API documentation it is different from the regular windows implementation
- bugfix: proxy plugins couldn't access the parent widget
- bugfix: when moving a file from overwrite to a mod the in-memory file structure wasn't updated
- bugfix: name input dialog for profiles allowed names that weren't valid directory names
- bugfix: profile dialog wasn't able to delete profiles if the name started or ended in whitespaces
- bugfix: The name-cells for plugin settings could be changed (without effect)
- removed a few obsolete files from the repository
2013-09-21 19:25:33 +02:00
Tannin 08037bf876 Added tag release v1.0.1 for changeset b90a2e9174c0 2013-09-21 18:59:47 +02:00
Tannin 500ab7f706 - bugfix: testing for missing masters at the wrong time seems to have caused crashes
- bugfix: mod list is now written to a temporary file first. Only on success is the original file overwritten
- bugfix: moving a mod priority to just above the overwrite could cause a crash or error message
- bugfix: versions with a release candidate number weren't sorted correctly (woops)
- bugfix: staging script didn't include archive.dll and dlls.manifest
- installation time on overwrite no longer updates constantly
2013-09-16 22:32:42 +02:00
Tannin 08309b71ba Added tag release v1.0.0rc1 for changeset 438584941a82 2013-09-15 16:11:16 +02:00
Tannin dce78b62b8 - bugfix: when installing mods through the nmm importer if no other mods were previously installed
the correct installation directory was never set
- bugfix: the nmm importer didn't "sanitize" mod names and potentially tried to create invalid directories
- bugfix: 0.99.x packages didn't contain qt plugins to handle certain image formats
2013-09-12 21:45:57 +02:00
Tannin af6e1c3ab4 - when installing mods from outside the download directory the absolute path is now stored
- added a context menu to the toolbar buttons so tool icons can be removed directly
- initweaks modinfo tab is now always available and allows new ini tweaks to be created
- fake esms are now treated as masters (as they should)
- MO will now display a warning if not all masters of an esp are enabled. The tooltip gives a list of required masters
- bugfix: path returned by getfullpathname was sometimes not correctly terminated
- bugfix: path after reverse-rerouting was sometimes incorrect, missing a path separator
- bugfix: change of current directory sometimes used a fake directory without need
- bugfix: icons in shortcut menu were not alwayscorrectly updated
2013-09-11 22:54:45 +02:00
Tannin 50325c8fc2 Added tag release v0.99.6 for changeset d9dedb857683 2013-09-05 21:14:19 +02:00
Tannin 7a382eab75 updated versions 2013-09-05 21:14:08 +02:00
Tannin 8e6868bf88 - bugfix: automatically removes a file from old NCC release that was interfering with the current version
- bugfix: fomod installer didn't find fomod files in nested folder
- bugfix: python proxy will now not even try to initialize python if python_dir contains no python.
This is necessary because the python interpreter crashes the application if the path is invalid
2013-09-05 21:04:20 +02:00
Tannin a891146446 Added tag release v0.99.5 for changeset f75473410ed3 2013-09-02 19:28:24 +02:00
Tannin 72f14df8a7 - bugfix: "overwrite" is no longer a localizable string, at least for now, because some pieces of code rely on the name 2013-09-02 19:22:40 +02:00
Tannin 0fa7155eb8 - bugfix: division-by-zero error in the newly introduced server-speed calculation
- bugfix: temp files extracted during fomod installation were not cleaned up. This still doesn't remove directories
- bugfix: fomod installer didn't find the installer xmls because they are in a subdirectory since release 0.99.4
2013-09-02 18:51:17 +02:00
Tannin c6c61ce792 Added tag release v0.99.4 for changeset a2ccfea95ef0 2013-09-02 18:48:52 +02:00
Tannin ef801879ab - separated python proxy into two dlls. One is a wrapper without external
dependencies that fulfills the plugin interface. The other contains the actual python
functionality. This way the outer dll can always be loaded and report issues.
- The build process embeds the second dll into the first, this way only one dll has to be shipped
2013-09-01 18:36:43 +02:00
Tannin 1e6c5f7c25 - added a new column for not-yet-endorsed mods
- set categories menu no longer closes when the mouse cursor leaves the menu
- MO will no longer change the endorsement flag if an update doesn't contain it
- the column selection for the mod list can now only be accessed by mouse,
hotkeys open the context menu of the mod
- now displaying a progress dialog during login. For unknown reasons MO hangs during that time
2013-09-01 13:40:44 +02:00
Tannin 49e1dd23b6 - mod list can now be sorted by install time
- the sorting of download archives wasn't actually by index instead of file time
- bugfix: some of the plugins crashed if they failed to create a mod
2013-08-31 17:11:17 +02:00
Tannin 9c31cfa915 - the download manager now registers download speed. Right now this is only used
to display an average speed on the settings menu
- added a python27.dll compiled with vc100. This can now be bundled without introducing more dependencies
- bugfix: extracting now stops after an error
- bugfix: the way hook.dll caused CREATE_ALWAYS/CREATE_NEW to always write into overwrite could lead to the
file being created when the call should have failed (because the file existed and was protected)
- bugfix: GetPrivateProfileString does NOT properly report files as missing. This means that
the ini-query optimization could optimize away requests that should work
- bugfix: fomod installer couldn't display images because they were unpacked to the wrong temporary location
- bugfix: When disabling local saves and choosing to delete the saves nothing happened
- bugfix: the python plugin couldn't find the pyqt libraries
2013-08-30 20:59:12 +02:00
Tannin 91dd38cb29 Added tag release v0.99.3 for changeset bf57300454f1 2013-08-25 13:32:33 +02:00
70 changed files with 4416 additions and 3800 deletions
+3 -1
View File
@@ -12,7 +12,9 @@ SUBDIRS = bsatk \
plugins \
proxydll \
nxmhandler \
BossDummy
BossDummy \
pythonRunner \
esptk
hookdll.depends = shared
organizer.depends = shared, uibase, plugins
-5
View File
@@ -65,11 +65,6 @@ ActivateModsDialog::~ActivateModsDialog()
}
void ActivateModsDialog::on_buttonBox_accepted()
{
}
std::set<QString> ActivateModsDialog::getModsToActivate()
{
std::set<QString> result;
-1
View File
@@ -62,7 +62,6 @@ public:
std::set<QString> getESPsToActivate();
private slots:
void on_buttonBox_accepted();
private:
Ui::ActivateModsDialog *ui;
-98
View File
@@ -1,98 +0,0 @@
/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "archivetree.h"
#include <QDragMoveEvent>
ArchiveTree::ArchiveTree(QWidget *parent) :
QTreeWidget(parent)
{
}
bool ArchiveTree::testMovePossible(QTreeWidgetItem *source, QTreeWidgetItem *target)
{
if ((target == NULL) ||
(source == NULL)) {
return false;
}
if ((source == target) ||
(source->parent() == target)) {
return false;
}
return true;
}
void ArchiveTree::dragEnterEvent(QDragEnterEvent *event)
{
QTreeWidgetItem *source = this->currentItem();
if ((source == NULL) || (source->parent() == NULL)) {
// can't change top level
event->ignore();
return;
} else {
QTreeWidget::dragEnterEvent(event);
}
}
void ArchiveTree::dragMoveEvent(QDragMoveEvent *event)
{
if (!testMovePossible(this->currentItem(), itemAt(event->pos()))) {
event->ignore();
} else {
QTreeWidget::dragMoveEvent(event);
}
}
void ArchiveTree::dropEvent(QDropEvent *event)
{
event->ignore();
QTreeWidgetItem *target = itemAt(event->pos());
QList<QTreeWidgetItem*> sourceItems = this->selectedItems();
for (QList<QTreeWidgetItem*>::iterator iter = sourceItems.begin();
iter != sourceItems.end(); ++iter) {
QTreeWidgetItem *source = *iter;
if ((source->parent() != NULL) &&
testMovePossible(source, target)) {
source->parent()->removeChild(source);
if (target->data(0, Qt::UserRole).toInt() != 0) {
// target is a file
if (target->parent() == NULL) {
// this should really not happen, how should a
// file get to the top level?
return;
}
int index = target->parent()->indexOfChild(target);
target->parent()->insertChild(index, source);
emit changed();
} else {
// target is a directory
target->insertChild(target->childCount(), source);
emit changed();
}
}
}
}
-55
View File
@@ -1,55 +0,0 @@
/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ARCHIVETREE_H
#define ARCHIVETREE_H
#include <QTreeWidget>
/**
* @brief QT tree widget used to display the content of an archive in the manual installation dialog
**/
class ArchiveTree : public QTreeWidget
{
Q_OBJECT
public:
explicit ArchiveTree(QWidget *parent = 0);
signals:
void changed();
public slots:
protected:
virtual void dragEnterEvent(QDragEnterEvent *event);
virtual void dragMoveEvent(QDragMoveEvent *event);
virtual void dropEvent(QDropEvent *event);
private:
bool testMovePossible(QTreeWidgetItem *source, QTreeWidgetItem *target);
};
#endif // ARCHIVETREE_H
+1
View File
@@ -46,6 +46,7 @@ public:
static const int CATEGORY_SPECIAL_UPDATEAVAILABLE = 10002;
static const int CATEGORY_SPECIAL_NOCATEGORY = 10003;
static const int CATEGORY_SPECIAL_CONFLICT = 10004;
static const int CATEGORY_SPECIAL_NOTENDORSED = 10005;
public:
+2 -2
View File
@@ -60,8 +60,8 @@ QVariant DownloadList::headerData(int section, Qt::Orientation orientation, int
if ((role == Qt::DisplayRole) &&
(orientation == Qt::Horizontal)) {
switch (section) {
case 0: return tr("Name");
case 1: return tr("Filetime");
case COL_NAME: return tr("Name");
case COL_FILETIME: return tr("Filetime");
default: return tr("Done");
}
} else {
+8
View File
@@ -34,6 +34,14 @@ class DownloadList : public QAbstractTableModel
Q_OBJECT
public:
enum EColumn {
COL_NAME = 0,
COL_FILETIME,
COL_STATUS
};
public:
/**
+8 -4
View File
@@ -18,6 +18,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "downloadlistsortproxy.h"
#include "downloadlist.h"
DownloadListSortProxy::DownloadListSortProxy(const DownloadManager *manager, QObject *parent)
: QSortFilterProxyModel(parent), m_Manager(manager), m_CurrentFilter()
@@ -30,18 +31,21 @@ void DownloadListSortProxy::updateFilter(const QString &filter)
invalidateFilter();
}
bool DownloadListSortProxy::lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
int leftIndex = sourceModel()->data(left).toInt();
int rightIndex = sourceModel()->data(right).toInt();
if (left.column() == 0) {
if (left.column() == DownloadList::COL_NAME) {
return m_Manager->getFileName(leftIndex).compare(m_Manager->getFileName(rightIndex), Qt::CaseInsensitive) < 0;
} else if (left.column() == 1) {
return leftIndex < rightIndex;
} else {
} else if (left.column() == DownloadList::COL_FILETIME) {
return m_Manager->getFileTime(leftIndex) < m_Manager->getFileTime(rightIndex);
} else if (left.column() == DownloadList::COL_STATUS) {
return m_Manager->getState(leftIndex) < m_Manager->getState(rightIndex);
} else {
return leftIndex < rightIndex;
}
}
+1 -1
View File
@@ -150,7 +150,7 @@ void DownloadListWidgetDelegate::paint(QPainter *painter, const QStyleOptionView
}
m_InstallLabel->setPalette(labelPalette);
if (m_Manager->isInfoIncomplete(downloadIndex)) {
m_NameLabel->setText("<img src=\":/MO/gui/resources/dialog-warning_16.png\" /> " + m_NameLabel->text());
m_NameLabel->setText("<img src=\":/MO/gui/warning_16\" /> " + m_NameLabel->text());
}
} else {
m_InstallLabel->setVisible(false);
+1 -1
View File
@@ -137,7 +137,7 @@ void DownloadListWidgetCompactDelegate::paint(QPainter *painter, const QStyleOpt
m_DoneLabel->setForegroundRole(QPalette::WindowText);
}
if (m_Manager->isInfoIncomplete(downloadIndex)) {
m_NameLabel->setText("<img src=\":/MO/gui/resources/dialog-warning_16.png\"/> " + m_NameLabel->text());
m_NameLabel->setText("<img src=\":/MO/gui/warning_16\"/> " + m_NameLabel->text());
}
} else {
m_DoneLabel->setVisible(false);
+36
View File
@@ -53,6 +53,7 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const Ne
DownloadInfo *info = new DownloadInfo;
info->m_DownloadID = s_NextDownloadID++;
info->m_StartTime.start();
info->m_PreResumeSize = 0LL;
info->m_Progress = 0;
info->m_ResumePos = 0;
info->m_ModID = modID;
@@ -98,6 +99,7 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(con
info->m_DownloadID = s_NextDownloadID++;
info->m_Output.setFileName(filePath);
info->m_TotalSize = QFileInfo(filePath).size();
info->m_PreResumeSize = info->m_TotalSize;
info->m_ModID = metaFile.value("modID", 0).toInt();
info->m_FileID = metaFile.value("fileID", 0).toInt();
info->m_CurrentUrl = 0;
@@ -283,6 +285,7 @@ bool DownloadManager::addDownload(const QStringList &URLs,
return addDownload(m_NexusInterface->getAccessManager()->get(request), URLs, fileName, modID, fileID, nexusInfo);
}
bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName,
int modID, int fileID, const NexusInfo &nexusInfo)
{
@@ -331,6 +334,8 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl
mode |= QIODevice::Append;
}
newDownload->m_StartTime.start();
if (!newDownload->m_Output.open(mode)) {
reportError(tr("failed to download %1: could not open output file: %2")
.arg(reply->url().toString()).arg(newDownload->m_Output.fileName()));
@@ -343,6 +348,8 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl
connect(newDownload->m_Reply, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));
if (!resume) {
newDownload->m_PreResumeSize = newDownload->m_Output.size();
emit aboutToUpdate();
m_ActiveDownloads.append(newDownload);
@@ -597,6 +604,20 @@ QString DownloadManager::getFileName(int index) const
return m_ActiveDownloads.at(index)->m_FileName;
}
QDateTime DownloadManager::getFileTime(int index) const
{
if ((index < 0) || (index >= m_ActiveDownloads.size())) {
throw MyException(tr("invalid index"));
}
DownloadInfo *info = m_ActiveDownloads.at(index);
if (!info->m_Created.isValid()) {
info->m_Created = QFileInfo(info->m_Output).created();
}
return info->m_Created;
}
qint64 DownloadManager::getFileSize(int index) const
{
if ((index < 0) || (index >= m_ActiveDownloads.size())) {
@@ -1034,6 +1055,8 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u
std::sort(resultList.begin(), resultList.end(), boost::bind(&DownloadManager::ServerByPreference, m_PreferredServers, _1, _2));
info.m_DownloadMap = resultList;
QStringList URLs;
foreach (const QVariant &server, resultList) {
@@ -1121,6 +1144,19 @@ void DownloadManager::downloadFinished()
createMetaFile(info);
emit update(index);
} else {
QString url = info->m_Urls[info->m_CurrentUrl];
foreach (const QVariant &server, info->m_NexusInfo.m_DownloadMap) {
QVariantMap serverMap = server.toMap();
if (serverMap["URI"].toString() == url) {
int deltaTime = info->m_StartTime.secsTo(QTime::currentTime());
if (deltaTime > 5) {
emit downloadSpeed(serverMap["Name"].toString(), (info->m_TotalSize - info->m_PreResumeSize) / deltaTime);
} // no division by zero please! Also, if the download is shorter than a few seconds, the result is way to inprecise
break;
}
}
setState(info, STATE_FETCHINGMODINFO); // need to set this state before changing the file name, otherwise .unfinished is appended
QString newName = getFileNameFromNetworkReply(reply);
+18
View File
@@ -45,6 +45,7 @@ struct NexusInfo {
QString m_Version;
QString m_NewestVersion;
QString m_FileName;
QVariantList m_DownloadMap;
bool m_Set;
};
Q_DECLARE_METATYPE(NexusInfo)
@@ -82,6 +83,7 @@ private:
QFile m_Output;
QNetworkReply *m_Reply;
QTime m_StartTime;
qint64 m_PreResumeSize;
int m_Progress;
int m_ModID;
int m_FileID;
@@ -90,6 +92,9 @@ private:
QStringList m_Urls;
qint64 m_ResumePos;
qint64 m_TotalSize;
QDateTime m_Created; // used as a cache in DownloadManager::getFileTime, may not be valid elsewhere
int m_Tries;
bool m_ReQueried;
@@ -214,6 +219,13 @@ public:
*/
qint64 getFileSize(int index) const;
/**
* @brief retrieve the creation time of the download specified by index
* @param index index of the file to look up
* @return size of the file (total size during download)
*/
QDateTime getFileTime(int index) const;
/**
* @brief retrieve the current progress of the download specified by index
*
@@ -297,6 +309,7 @@ public:
int indexByName(const QString &fileName) const;
void pauseAll();
signals:
void aboutToUpdate();
@@ -322,6 +335,11 @@ signals:
*/
void stateChanged(int row, DownloadManager::DownloadState state);
/**
* @brief emitted whenever a download completes successfully, reporting the download speed for the server used
*/
void downloadSpeed(const QString &serverName, int bytesPerSecond);
public slots:
/**
-10
View File
@@ -172,16 +172,6 @@ void ExecutablesList::addExecutable(const QString &title, const QString &executa
}
}
/*void ExecutablesList::remove(const QString &executableName)
{
for (std::vector<Executable>::iterator iter = m_Executables.begin(); iter != m_Executables.end(); ++iter) {
if (iter->m_Custom && (iter->m_BinaryInfo.absoluteFilePath() == executableName)) {
m_Executables.erase(iter);
break;
}
}
}*/
void ExecutablesList::remove(const QString &title)
{
-48
View File
@@ -1,48 +0,0 @@
/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "finddialog.h"
#include "ui_finddialog.h"
FindDialog::FindDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::FindDialog)
{
ui->setupUi(this);
}
FindDialog::~FindDialog()
{
delete ui;
}
void FindDialog::on_nextBtn_clicked()
{
emit findNext();
}
void FindDialog::on_patternEdit_textChanged(const QString &pattern)
{
emit patternChanged(pattern);
}
void FindDialog::on_closeBtn_clicked()
{
this->close();
}
-73
View File
@@ -1,73 +0,0 @@
/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FINDDIALOG_H
#define FINDDIALOG_H
#include <QDialog>
namespace Ui {
class FindDialog;
}
/**
* @brief Find dialog used in the TextView dialog
**/
class FindDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief constructor
*
* @param parent parent widget
**/
explicit FindDialog(QWidget *parent = 0);
~FindDialog();
signals:
/**
* @brief emitted when the user wants to jump to the next location matching the pattern
**/
void findNext();
/**
* @brief emitted when the user changes the pattern to search for
*
* @param pattern the new search pattern
**/
void patternChanged(const QString &pattern);
private slots:
void on_nextBtn_clicked();
void on_patternEdit_textChanged(const QString &arg1);
void on_closeBtn_clicked();
private:
Ui::FindDialog *ui;
};
#endif // FINDDIALOG_H
+35 -15
View File
@@ -114,7 +114,8 @@ void InstallationManager::mapToArchive(const DirectoryTree::Node *node, std::wst
for (DirectoryTree::const_node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) {
if ((*iter)->getData().index != -1) {
data[(*iter)->getData().index]->setSkip(false);
data[(*iter)->getData().index]->setOutputFileName(path.substr().append(ToWString((*iter)->getData().name)).c_str());
std::wstring temp = path.substr().append(ToWString((*iter)->getData().name));
data[(*iter)->getData().index]->setOutputFileName(temp.c_str());
}
mapToArchive(*iter, path.substr().append(ToWString((*iter)->getData().name)), data);
}
@@ -206,7 +207,7 @@ QString canonicalize(const QString &name)
}
QStringList InstallationManager::extractFiles(const QStringList &filesOrig)
QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool flatten)
{
QStringList files;
@@ -222,20 +223,26 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig)
for (size_t i = 0; i < size; ++i) {
if (files.contains(ToQString(data[i]->getFileName()), Qt::CaseInsensitive)) {
const wchar_t *baseName = wcsrchr(data[i]->getFileName(), '\\');
if (baseName == NULL) {
baseName = wcsrchr(data[i]->getFileName(), '/');
const wchar_t *targetFile = data[i]->getFileName();
if (flatten) {
targetFile = wcsrchr(data[i]->getFileName(), '\\');
if (targetFile == NULL) {
targetFile = wcsrchr(data[i]->getFileName(), '/');
}
if (targetFile == NULL) {
qCritical("failed to find backslash in %ls", data[i]->getFileName());
continue;
} else {
// skip the slash
++targetFile;
}
}
if (baseName == NULL) {
qCritical("failed to find backslash in %ls", data[i]->getFileName());
continue;
}
data[i]->setOutputFileName(baseName);
data[i]->setOutputFileName(targetFile);
result.append(QDir::tempPath().append("/").append(ToQString(baseName)));
result.append(QDir::tempPath().append("/").append(ToQString(targetFile)));
data[i]->setSkip(false);
m_TempFilesToDelete.insert(ToQString(baseName));
m_TempFilesToDelete.insert(ToQString(targetFile));
} else {
data[i]->setSkip(true);
}
@@ -252,6 +259,7 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig)
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();
throw std::runtime_error("extracting failed");
}
@@ -401,6 +409,7 @@ void InstallationManager::report7ZipError(LPCWSTR errorMessage)
#else
reportError(QString::fromUtf16(errorMessage));
#endif
m_CurrentArchive->cancel();
}
@@ -422,7 +431,7 @@ QString InstallationManager::generateBackupName(const QString &directoryName) co
bool InstallationManager::testOverwrite(GuessedValue<QString> &modName) const
{
QString targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory.mid(0).append("\\").append(modName));
QString targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory + "\\" + modName);
while (QDir(targetDirectory).exists()) {
QueryOverwriteDialog overwriteDialog(m_ParentWidget);
@@ -527,6 +536,7 @@ bool InstallationManager::doInstall(GuessedValue<QString> &modName, int modID,
new MethodCallback<InstallationManager, void, float>(this, &InstallationManager::updateProgress),
new MethodCallback<InstallationManager, void, LPCWSTR>(this, &InstallationManager::updateProgressFile),
new MethodCallback<InstallationManager, void, LPCWSTR>(this, &InstallationManager::report7ZipError))) {
m_InstallationProgress.hide();
if (m_CurrentArchive->getLastError() == Archive::ERROR_EXTRACT_CANCELLED) {
return false;
} else {
@@ -654,8 +664,11 @@ bool InstallationManager::install(const QString &fileName, GuessedValue<QString>
modName.update(guessedModName, GUESS_GOOD);
}
qDebug("using mod name \"%s\" (id %d)", modName->toUtf8().constData(), modID);
m_CurrentFile = fileInfo.fileName();
m_CurrentFile = fileInfo.absoluteFilePath();
if (fileInfo.dir() == QDir(ToQString(GameInfo::instance().getDownloadDir()))) {
m_CurrentFile = fileInfo.fileName();
}
qDebug("using mod name \"%s\" (id %d) -> %s", modName->toUtf8().constData(), modID, qPrintable(m_CurrentFile));
// open the archive and construct the directory tree the installers work on
bool archiveOpen = m_CurrentArchive->open(ToWString(QDir::toNativeSeparators(fileName)).c_str(),
@@ -716,6 +729,13 @@ bool InstallationManager::install(const QString &fileName, GuessedValue<QString>
qPrintable(installer->name()), e.what());
}
// 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) {
QFile::remove(QDir::tempPath() + "/" + tempFile);
}
// act upon the installation result. at this point the files have already been
// extracted to the correct location
switch (installResult) {
+6 -1
View File
@@ -57,6 +57,11 @@ public:
~InstallationManager();
/**
* @brief update the directory where mods are to be installed
* @param modsDirectory the mod directory
* @note this is called a lot, probably redundantly
*/
void setModsDirectory(const QString &modsDirectory) { m_ModsDirectory = modsDirectory; }
/**
@@ -114,7 +119,7 @@ public:
* @note the temporary file is automatically cleaned up after the installation
* @note This call can be very slow if the archive is large and "solid"
*/
virtual QStringList extractFiles(const QStringList &files);
virtual QStringList extractFiles(const QStringList &files, bool flatten);
/**
* @brief installs an archive
+7 -16
View File
@@ -182,16 +182,7 @@ void cleanupDir()
"QtXml4.dll",
"QtWebKit4.dll",
"qjpeg4.dll",
/* "dlls/phonon4.dll",
"dlls/QtCore4.dll",
"dlls/QtGui4.dll",
"dlls/QtNetwork4.dll",
"dlls/QtXml4.dll",
"dlls/QtXmlPatterns4.dll",
"dlls/QtWebKit4.dll",
"dlls/QtDeclarative4.dll",
"dlls/QtScript4.dll",
"dlls/QtSql4.dll"*/
"NCC/GamebryoBase.dll"
};
static const int NUM_FILES = sizeof(fileNames) / sizeof(QString);
@@ -283,15 +274,15 @@ void registerMetaTypes()
int main(int argc, char *argv[])
{
MOApplication application(argc, argv);
qApp->addLibraryPath(application.applicationDirPath() + "/dlls");
application.addLibraryPath(application.applicationDirPath() + "/dlls");
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
LogBuffer::init(20, QtDebugMsg, application.applicationDirPath() + "/logs/mo_interface.log");
qDebug("Working directory: %s", qPrintable(QDir::currentPath()));
qDebug("MO at: %s", qPrintable(application.applicationDirPath()));
qDebug("user name: %s", getenv("USERNAME"));
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();
@@ -352,7 +343,7 @@ int main(int argc, char *argv[])
bool done = false;
while (!done) {
if (!GameInfo::init(moPath, ToWString(gamePath))) {
if (!GameInfo::init(moPath, ToWString(QDir::toNativeSeparators(gamePath)))) {
if (!gamePath.isEmpty()) {
reportError(QObject::tr("No game identified in \"%1\". The directory is required to contain "
"the game binary and its launcher.").arg(gamePath));
@@ -404,7 +395,7 @@ int main(int argc, char *argv[])
settings.setValue("gamePath", gamePath.toUtf8().constData());
}
qDebug("managing game at %s", qPrintable(gamePath));
qDebug("managing game at %s", qPrintable(QDir::toNativeSeparators(gamePath)));
ExecutablesList executablesList;
+187 -35
View File
@@ -98,12 +98,14 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QNetworkInterface>
#include <QNetworkProxy>
#include <QtConcurrentRun>
#include <QCoreApplication>
#ifdef TEST_MODELS
#include "modeltest.h"
#endif // TEST_MODELS
#pragma warning( disable : 4428 )
using namespace MOBase;
using namespace MOShared;
@@ -143,7 +145,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
m_InstallationManager(this), m_Translator(NULL), m_TranslatorQt(NULL),
m_Updater(NexusInterface::instance(), this), m_CategoryFactory(CategoryFactory::instance()),
m_CurrentProfile(NULL), m_AskForNexusPW(false), m_LoginAttempted(false),
m_ArchivesInit(false), m_ContextItem(NULL), m_CurrentSaveView(NULL),
m_ArchivesInit(false), m_ContextItem(NULL), m_ContextAction(NULL), m_CurrentSaveView(NULL),
m_GameInfo(new GameInfoImpl())
{
ui->setupUi(this);
@@ -212,12 +214,15 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
NexusInterface::instance()->setCacheDirectory(m_Settings.getCacheDirectory());
NexusInterface::instance()->setNMMVersion(m_Settings.getNMMVersion());
m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
updateDownloadListDelegate();
ui->savegameList->installEventFilter(this);
ui->savegameList->setMouseTracking(true);
connect(&m_DownloadManager, SIGNAL(showMessage(QString)), this, SLOT(showMessage(QString)));
connect(&m_DownloadManager, SIGNAL(downloadSpeed(QString,int)), this, SLOT(downloadSpeed(QString,int)));
connect(ui->savegameList, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(saveSelectionChanged(QListWidgetItem*)));
@@ -229,6 +234,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), this, SLOT(modlistChanged(QModelIndex, int)));
connect(&m_ModList, SIGNAL(removeSelectedMods()), this, SLOT(removeMod_clicked()));
connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), this, SLOT(displayColumnSelection(QPoint)));
connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), this, SLOT(fileMoved(QString, QString, QString)));
connect(ui->modList, SIGNAL(dropModeUpdate(bool)), &m_ModList, SLOT(dropModeUpdate(bool)));
connect(m_ModListSortProxy, SIGNAL(filterActive(bool)), this, SLOT(modFilterActive(bool)));
connect(ui->modFilterEdit, SIGNAL(textChanged(QString)), m_ModListSortProxy, SLOT(updateFilter(QString)));
@@ -259,6 +265,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString)));
connect(ui->toolBar, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(toolBar_customContextMenuRequested(QPoint)));
connect(this, SIGNAL(styleChanged(QString)), this, SLOT(updateStyle(QString)));
m_CheckBSATimer.setSingleShot(true);
@@ -991,6 +999,7 @@ bool MainWindow::registerPlugin(QObject *plugin)
{ // proxy plugins
IPluginProxy *proxy = qobject_cast<IPluginProxy*>(plugin);
if (verifyPlugin(proxy)) {
proxy->setParentWidget(this);
QStringList pluginNames = proxy->pluginList(QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()));
foreach (const QString &pluginName, pluginNames) {
try {
@@ -1034,11 +1043,38 @@ void MainWindow::loadPlugins()
registerPlugin(plugin);
}
QFile loadCheck(QCoreApplication::applicationDirPath() + "/plugin_loadcheck.tmp");
if (loadCheck.exists() && loadCheck.open(QIODevice::ReadOnly)) {
// oh, there was a failed plugin load last time. Find out which plugin was loaded last
QString fileName;
while (!loadCheck.atEnd()) {
fileName = QString::fromUtf8(loadCheck.readLine().constData()).trimmed();
}
if (QMessageBox::question(this, tr("Plugin error"),
tr("It appears the plugin \"%1\" failed to load last startup and caused MO to crash. Do you want to disable it?\n"
"(Please note: If this is the first time you see this message for this plugin you may want to give it another try. "
"The plugin may be able to recover from the problem)").arg(fileName),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) {
m_Settings.addBlacklistPlugin(fileName);
}
loadCheck.close();
}
loadCheck.open(QIODevice::WriteOnly);
QString pluginPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())) + "/" + ToQString(AppConfig::pluginPath());
qDebug("looking for plugins in %s", pluginPath.toUtf8().constData());
qDebug("looking for plugins in %s", QDir::toNativeSeparators(pluginPath).toUtf8().constData());
QDirIterator iter(pluginPath, QDir::Files | QDir::NoDotAndDotDot);
while (iter.hasNext()) {
iter.next();
if (m_Settings.pluginBlacklisted(iter.fileName())) {
qDebug("plugin \"%s\" blacklisted", qPrintable(iter.fileName()));
continue;
}
loadCheck.write(iter.fileName().toUtf8());
loadCheck.write("\n");
loadCheck.flush();
QString pluginName = iter.filePath();
if (QLibrary::isLibrary(pluginName)) {
QPluginLoader pluginLoader(pluginName);
@@ -1057,6 +1093,9 @@ void MainWindow::loadPlugins()
}
}
// remove the load check file on success
loadCheck.remove();
m_DownloadManager.setSupportedExtensions(m_InstallationManager.getSupportedExtensions());
m_DiagnosisPlugins.push_back(this);
@@ -1120,6 +1159,8 @@ IModInterface *MainWindow::createMod(GuessedValue<QString> &name)
return NULL;
}
m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
/* QString fixedName = name;
fixDirectoryName(fixedName);
unsigned int index = ModInfo::getIndex(fixedName);
@@ -1164,6 +1205,21 @@ QVariant MainWindow::pluginSetting(const QString &pluginName, const QString &key
return m_Settings.pluginSetting(pluginName, key);
}
void MainWindow::setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value)
{
m_Settings.setPluginSetting(pluginName, key, value);
}
QVariant MainWindow::persistent(const QString &pluginName, const QString &key, const QVariant &def) const
{
return m_Settings.pluginPersistent(pluginName, key, def);
}
void MainWindow::setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync)
{
m_Settings.setPluginPersistent(pluginName, key, value, sync);
}
QString MainWindow::pluginDataPath() const
{
QString pluginPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())) + "/" + ToQString(AppConfig::pluginPath());
@@ -1216,6 +1272,11 @@ HANDLE MainWindow::spawnBinaryDirect(const QFileInfo &binary, const QString &arg
}
}
while (m_RefreshProgress->isVisible()) {
::Sleep(1000);
QCoreApplication::processEvents();
}
return startBinary(binary, arguments, profileName, m_Settings.logLevel(), currentDirectory, true);
}
@@ -1265,11 +1326,9 @@ void MainWindow::spawnBinary(const QFileInfo &binary, const QString &arguments,
this->setEnabled(true);
refreshDirectoryStructure();
refreshDataTree();
if (GameInfo::instance().getLoadOrderMechanism() == GameInfo::TYPE_FILETIME) {
QFile::remove(m_CurrentProfile->getLoadOrderFileName());
}
refreshLists();
dialog->hide();
}
}
@@ -1323,17 +1382,6 @@ void MainWindow::setExecutableIndex(int index)
executableBox->setCurrentIndex(1);
}
const Executable &selectedExecutable = executableBox->itemData(executableBox->currentIndex()).value<Executable>();
QIcon addIcon(":/MO/gui/link");
QIcon removeIcon(":/MO/gui/remove");
QFileInfo linkDesktopFile(QDir::fromNativeSeparators(getDesktopDirectory()) + "/" + selectedExecutable.m_Title + ".lnk");
QFileInfo linkMenuFile(QDir::fromNativeSeparators(getStartMenuDirectory()) + "/" + selectedExecutable.m_Title + ".lnk");
ui->linkButton->menu()->actions().at(0)->setIcon(selectedExecutable.m_Toolbar ? removeIcon : addIcon);
ui->linkButton->menu()->actions().at(1)->setIcon(linkDesktopFile.exists() ? removeIcon : addIcon);
ui->linkButton->menu()->actions().at(2)->setIcon(linkMenuFile.exists() ? removeIcon : addIcon);
}
@@ -1625,11 +1673,15 @@ void MainWindow::refreshESPList()
m_CurrentProfile->writeModlist();
// clear list
m_PluginList.refresh(m_CurrentProfile->getName(),
*m_DirectoryStructure,
m_CurrentProfile->getPluginsFileName(),
m_CurrentProfile->getLoadOrderFileName(),
m_CurrentProfile->getLockedOrderFileName());
try {
m_PluginList.refresh(m_CurrentProfile->getName(),
*m_DirectoryStructure,
m_CurrentProfile->getPluginsFileName(),
m_CurrentProfile->getLoadOrderFileName(),
m_CurrentProfile->getLockedOrderFileName());
} catch (const std::exception &e) {
reportError(tr("Failed to refresh list of esps: %s").arg(e.what()));
}
}
@@ -1766,14 +1818,14 @@ void MainWindow::checkBSAList()
if (item->checkState(0) == Qt::Unchecked) {
if (m_DefaultArchives.contains(filename)) {
item->setIcon(0, QIcon(":/MO/gui/resources/dialog-warning.png"));
item->setIcon(0, QIcon(":/MO/gui/warning"));
item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!"));
modWarning = true;
} else {
QString espName = filename.mid(0, filename.length() - 3).append("esp").toLower();
QString esmName = filename.mid(0, filename.length() - 3).append("esm").toLower();
if (m_PluginList.isEnabled(espName) || m_PluginList.isEnabled(esmName)) {
item->setIcon(0, QIcon(":/MO/gui/resources/dialog-warning.png"));
item->setIcon(0, QIcon(":/MO/gui/warning"));
item->setToolTip(0, tr("This archive will still be loaded since there is a plugin of the same name but "
"its files will not follow installation order!"));
modWarning = true;
@@ -1788,7 +1840,7 @@ void MainWindow::checkBSAList()
}
if (warning) {
ui->tabWidget->setTabIcon(1, QIcon(":/MO/gui/resources/dialog-warning.png"));
ui->tabWidget->setTabIcon(1, QIcon(":/MO/gui/warning"));
} else {
ui->tabWidget->setTabIcon(1, QIcon());
}
@@ -1929,7 +1981,6 @@ void MainWindow::on_btnRefreshData_clicked()
{
if (!m_DirectoryUpdate) {
refreshDirectoryStructure();
refreshDataTree();
} else {
qDebug("directory update");
}
@@ -2334,8 +2385,12 @@ void MainWindow::directory_refreshed()
{
DirectoryEntry *newStructure = m_DirectoryRefresher.getDirectoryStructure();
if (newStructure != NULL) {
delete m_DirectoryStructure;
DirectoryEntry *oldStructure = m_DirectoryStructure;
m_DirectoryStructure = newStructure;
delete oldStructure;
refreshDataTree();
refreshLists();
} else {
// TODO: don't know why this happens, this slot seems to get called twice with only one emit
return;
@@ -2544,6 +2599,29 @@ void MainWindow::modlistChanged(int)
}
void MainWindow::fileMoved(const QString &filePath, const QString &oldOriginName, const QString &newOriginName)
{
const FileEntry::Ptr filePtr = m_DirectoryStructure->findFile(ToWString(filePath));
if (filePtr.get() != NULL) {
try {
FilesOrigin &oldOrigin = m_DirectoryStructure->getOriginByName(ToWString(oldOriginName));
FilesOrigin &newOrigin = m_DirectoryStructure->getOriginByName(ToWString(newOriginName));
QString fullNewPath = ToQString(newOrigin.getPath()) + "\\" + filePath;
WIN32_FIND_DATAW findData;
::FindFirstFileW(ToWString(fullNewPath).c_str(), &findData);
filePtr->addOrigin(newOrigin.getID(), findData.ftCreationTime, L"");
filePtr->removeOrigin(oldOrigin.getID());
} catch (const std::exception &e) {
reportError(tr("Failed to move \"%1\" from mod \"%2\" to \"%3\": %4").arg(filePath).arg(oldOriginName).arg(newOriginName).arg(e.what()));
}
} else {
// this is probably not an error, the specified path is likely a directory
}
}
QTreeWidgetItem *MainWindow::addFilterItem(QTreeWidgetItem *root, const QString &name, int categoryID)
{
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(name));
@@ -2590,6 +2668,7 @@ void MainWindow::refreshFilters()
addFilterItem(NULL, tr("<Update>"), CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE);
addFilterItem(NULL, tr("<No category>"), CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY);
addFilterItem(NULL, tr("<Conflicted>"), CategoryFactory::CATEGORY_SPECIAL_CONFLICT);
addFilterItem(NULL, tr("<Not Endorsed>"), CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED);
std::set<int> categoriesUsed;
for (unsigned int modIdx = 0; modIdx < ModInfo::getNumMods(); ++modIdx) {
@@ -2732,8 +2811,13 @@ void MainWindow::reinstallMod_clicked()
QString installationFile = modInfo->getInstallationFile();
if (installationFile.length() != 0) {
QString fullInstallationFile;
if (QFileInfo(installationFile).isAbsolute()) {
fullInstallationFile = installationFile;
QFileInfo fileInfo(installationFile);
if (fileInfo.isAbsolute()) {
if (fileInfo.exists()) {
fullInstallationFile = installationFile;
} else {
fullInstallationFile = m_DownloadManager.getOutputDirectory().append("/").append(fileInfo.fileName());
}
} else {
fullInstallationFile = m_DownloadManager.getOutputDirectory().append("/").append(installationFile);
}
@@ -2792,6 +2876,15 @@ void MainWindow::unendorse_clicked()
}
void MainWindow::overwriteClosed(int)
{
QDialog *dialog = this->findChild<QDialog*>("__overwriteDialog");
if (dialog != NULL) {
dialog->deleteLater();
}
}
void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab)
{
std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
@@ -2804,6 +2897,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index,
dialog->show();
dialog->raise();
dialog->activateWindow();
connect(dialog, SIGNAL(finished(int)), this, SLOT(overwriteClosed(int)));
} else {
ModInfoDialog dialog(modInfo, m_DirectoryStructure, this);
connect(&dialog, SIGNAL(nexusLinkActivated(QString)), this, SLOT(nexusLinkActivated(QString)));
@@ -3016,10 +3110,13 @@ void MainWindow::createModFromOverwrite()
}
IModInterface *newMod = createMod(name);
if (newMod == NULL) {
return;
}
ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(m_ContextRow);
shellMove(QStringList(overwriteInfo->absolutePath() + "\\*"), QStringList(newMod->absolutePath()), this);
shellMove(QStringList(QDir::toNativeSeparators(overwriteInfo->absolutePath()) + "\\*"),
QStringList(QDir::toNativeSeparators(newMod->absolutePath())), this);
refreshModList();
}
@@ -3306,6 +3403,17 @@ void MainWindow::exportModListCSV()
}
}
void addMenuAsPushButton(QMenu *menu, QMenu *subMenu)
{
QPushButton *pushBtn = new QPushButton(subMenu->title());
pushBtn->setMenu(subMenu);
QWidgetAction *action = new QWidgetAction(menu);
action->setDefaultWidget(pushBtn);
menu->addAction(action);
}
void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
{
try {
@@ -3314,6 +3422,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
m_ContextRow = mapToModel(&m_ModList, modList->indexAt(pos)).row();
QMenu menu;
menu.addAction(tr("Install Mod..."), this, SLOT(installMod_clicked()));
menu.addAction(tr("Enable all visible"), this, SLOT(enableVisibleMods()));
@@ -3338,13 +3447,16 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
menu.addAction(tr("Restore Backup"), this, SLOT(restoreBackup_clicked()));
menu.addAction(tr("Remove Backup..."), this, SLOT(removeMod_clicked()));
} else {
QMenu *addCategoryMenu = menu.addMenu(tr("Set Category"));
// Set categories is a separate menu connected to a push button. This way it doesn't simply close every time you hover the mouse outside
QMenu *addCategoryMenu = new QMenu(tr("Set Category"));
addCategories(addCategoryMenu, 0);
connect(addCategoryMenu, SIGNAL(aboutToHide()), this, SLOT(saveCategories()));
addMenuAsPushButton(&menu, addCategoryMenu);
QMenu *primaryCategoryMenu = menu.addMenu(tr("Primary Category"));
QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category"));
connect(primaryCategoryMenu, SIGNAL(aboutToShow()), this, SLOT(addPrimaryCategoryCandidates()));
connect(primaryCategoryMenu, SIGNAL(aboutToHide()), this, SLOT(savePrimaryCategory()));
addMenuAsPushButton(&menu, primaryCategoryMenu);
menu.addAction(tr("Rename Mod..."), this, SLOT(renameMod_clicked()));
menu.addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked()));
@@ -3592,12 +3704,19 @@ void MainWindow::linkMenu()
}
}
void MainWindow::downloadSpeed(const QString &serverName, int bytesPerSecond)
{
m_Settings.setDownloadSpeed(serverName, bytesPerSecond);
}
void MainWindow::on_actionSettings_triggered()
{
QString oldModDirectory(m_Settings.getModDirectory());
QString oldCacheDirectory(m_Settings.getCacheDirectory());
bool proxy = m_Settings.useProxy();
m_Settings.query(this);
m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
fixCategories();
refreshFilters();
if (QDir::fromNativeSeparators(m_DownloadManager.getOutputDirectory()) != QDir::fromNativeSeparators(m_Settings.getDownloadDirectory())) {
@@ -4098,7 +4217,6 @@ void MainWindow::updateDownloadListDelegate()
ui->downloadView->setModel(sortProxy);
ui->downloadView->sortByColumn(1, Qt::AscendingOrder);
ui->downloadView->header()->resizeSections(QHeaderView::Fixed);
// ui->downloadView->setFirstColumnSpanned(0, QModelIndex(), true);
connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), this, SLOT(installDownload(int)));
connect(ui->downloadView->itemDelegate(), SIGNAL(queryInfo(int)), &m_DownloadManager, SLOT(queryInfo(int)));
@@ -4136,7 +4254,6 @@ void MainWindow::modDetailsUpdated(bool)
void MainWindow::nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant userData, QVariant resultData, int)
{
m_ModsToUpdate -= modIDs.size();
QVariantList resultList = resultData.toList();
for (auto iter = resultList.begin(); iter != resultList.end(); ++iter) {
QVariantMap result = iter->toMap();
@@ -4149,8 +4266,9 @@ void MainWindow::nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant us
for (auto iter = info.begin(); iter != info.end(); ++iter) {
(*iter)->setNewestVersion(VersionInfo(result["version"].toString()));
(*iter)->setNexusDescription(result["description"].toString());
if (NexusInterface::instance()->getAccessManager()->loggedIn()) {
// don't use endorsement info if we're not logged in
if (NexusInterface::instance()->getAccessManager()->loggedIn() &&
result.contains("voted_by_user")) {
// don't use endorsement info if we're not logged in or if the response doesn't contain it
(*iter)->setIsEndorsed(result["voted_by_user"].toBool());
}
}
@@ -4468,6 +4586,25 @@ void MainWindow::unlockESPIndex()
}
void MainWindow::removeFromToolbar()
{
Executable &exe = m_ExecutablesList.find(m_ContextAction->text());
exe.m_Toolbar = false;
updateToolBar();
}
void MainWindow::toolBar_customContextMenuRequested(const QPoint &point)
{
QAction *action = ui->toolBar->actionAt(point);
if (action->objectName().startsWith("custom_")) {
m_ContextAction = action;
QMenu menu;
menu.addAction(tr("Remove"), this, SLOT(removeFromToolbar()));
menu.exec(ui->toolBar->mapToGlobal(point));
}
}
void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos)
{
m_ContextRow = m_PluginListSortProxy->mapToSource(ui->espList->indexAt(pos)).row();
@@ -4539,3 +4676,18 @@ void MainWindow::on_groupCombo_currentIndexChanged(int index)
m_ModListSortProxy->setSourceModel(&m_ModList);
}
}
void MainWindow::on_linkButton_pressed()
{
const Executable &selectedExecutable = ui->executablesListBox->itemData(ui->executablesListBox->currentIndex()).value<Executable>();
QIcon addIcon(":/MO/gui/link");
QIcon removeIcon(":/MO/gui/remove");
QFileInfo linkDesktopFile(QDir::fromNativeSeparators(getDesktopDirectory()) + "/" + selectedExecutable.m_Title + ".lnk");
QFileInfo linkMenuFile(QDir::fromNativeSeparators(getStartMenuDirectory()) + "/" + selectedExecutable.m_Title + ".lnk");
ui->linkButton->menu()->actions().at(0)->setIcon(selectedExecutable.m_Toolbar ? removeIcon : addIcon);
ui->linkButton->menu()->actions().at(1)->setIcon(linkDesktopFile.exists() ? removeIcon : addIcon);
ui->linkButton->menu()->actions().at(2)->setIcon(linkMenuFile.exists() ? removeIcon : addIcon);
}

Some files were not shown because too many files have changed in this diff Show More