summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTannin <devnull@localhost>2013-03-27 20:48:33 +0100
committerTannin <devnull@localhost>2013-03-27 20:48:33 +0100
commite0eb97922d5ef4a1448e76f13374ebfda7fda403 (patch)
tree00f32ad3f680e51a210bee8155624d71f608bb15 /src
parent74c75e60d67b66a63225239c1f6b1403662857aa (diff)
- added hooks for getFileVersion* functions
- automatic donwload retry - support for storing multiple download urls - improved "query info" functionality - some cleanup to download manager code - external fomod installer dialog are now brought to front - added shell... functions to have windows handle problematic situations - added visual clue when filters are active - esps are now automatically activated when installing a mod - added option to never endorse a mod - added "previous" and "next" buttons to mod info dialog - improved the way messagedialog text is shortened - coloring in mod info dialog now visible in other color schemes - plugin list is now saved automatically - vanilla bsas are now enabled even if they are not listed in the ini file - bugfix: setting mod to maximum now doesn't try to place the mod below overwrite
Diffstat (limited to 'src')
-rw-r--r--src/downloadlistwidget.cpp10
-rw-r--r--src/downloadlistwidgetcompact.cpp8
-rw-r--r--src/downloadmanager.cpp291
-rw-r--r--src/downloadmanager.h36
-rw-r--r--src/installationmanager.cpp58
-rw-r--r--src/logbuffer.cpp276
-rw-r--r--src/main.cpp54
-rw-r--r--src/mainwindow.cpp221
-rw-r--r--src/mainwindow.h21
-rw-r--r--src/mainwindow.ui661
-rw-r--r--src/messagedialog.cpp121
-rw-r--r--src/messagedialog.ui8
-rw-r--r--src/modinfo.cpp31
-rw-r--r--src/modinfo.h16
-rw-r--r--src/modinfodialog.cpp27
-rw-r--r--src/modinfodialog.h6
-rw-r--r--src/modinfodialog.ui14
-rw-r--r--src/modlist.cpp12
-rw-r--r--src/modlist.h423
-rw-r--r--src/modlistsortproxy.cpp6
-rw-r--r--src/modlistsortproxy.h6
-rw-r--r--src/nexusinterface.cpp2
-rw-r--r--src/organizer.pro1
-rw-r--r--src/pluginlist.cpp44
-rw-r--r--src/pluginlist.h11
-rw-r--r--src/profile.cpp35
-rw-r--r--src/profilesdialog.cpp2
-rw-r--r--src/selfupdater.cpp18
-rw-r--r--src/settingsdialog.ui22
-rw-r--r--src/shared/fallout3info.cpp10
-rw-r--r--src/shared/fallout3info.h2
-rw-r--r--src/shared/falloutnvinfo.cpp10
-rw-r--r--src/shared/falloutnvinfo.h2
-rw-r--r--src/shared/gameinfo.h2
-rw-r--r--src/shared/oblivioninfo.cpp11
-rw-r--r--src/shared/oblivioninfo.h2
-rw-r--r--src/shared/skyriminfo.cpp16
-rw-r--r--src/shared/skyriminfo.h2
-rw-r--r--src/stylesheets/dark.qss9
-rw-r--r--src/version.rc4
40 files changed, 1461 insertions, 1050 deletions
diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp
index 8c6a8a4e..18276b0a 100644
--- a/src/downloadlistwidget.cpp
+++ b/src/downloadlistwidget.cpp
@@ -71,7 +71,7 @@ void DownloadListWidgetDelegate::paint(QPainter *painter, const QStyleOptionView
}
m_NameLabel->setText(name);
DownloadManager::DownloadState state = m_Manager->getState(downloadIndex);
- if (state == DownloadManager::STATE_PAUSED) {
+ if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) {
QPalette labelPalette;
m_InstallLabel->setVisible(true);
m_Progress->setVisible(false);
@@ -82,6 +82,12 @@ void DownloadListWidgetDelegate::paint(QPainter *painter, const QStyleOptionView
#endif
labelPalette.setColor(QPalette::WindowText, Qt::darkRed);
m_InstallLabel->setPalette(labelPalette);
+ } else if (state == DownloadManager::STATE_FETCHINGMODINFO) {
+ m_InstallLabel->setText(tr("Fetching Info 1"));
+ m_Progress->setVisible(false);
+ } else if (state == DownloadManager::STATE_FETCHINGFILEINFO) {
+ m_InstallLabel->setText(tr("Fetching Info 2"));
+ m_Progress->setVisible(false);
} else if (state >= DownloadManager::STATE_READY) {
QPalette labelPalette;
m_InstallLabel->setVisible(true);
@@ -231,7 +237,7 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel *
} else if (state == DownloadManager::STATE_DOWNLOADING){
menu.addAction(tr("Cancel"), this, SLOT(issueCancel()));
menu.addAction(tr("Pause"), this, SLOT(issuePause()));
- } else if (state == DownloadManager::STATE_PAUSED) {
+ } else if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) {
menu.addAction(tr("Remove"), this, SLOT(issueDelete()));
menu.addAction(tr("Resume"), this, SLOT(issueResume()));
}
diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp
index 6e56d8c6..d4491dfb 100644
--- a/src/downloadlistwidgetcompact.cpp
+++ b/src/downloadlistwidgetcompact.cpp
@@ -77,11 +77,15 @@ void DownloadListWidgetCompactDelegate::paint(QPainter *painter, const QStyleOpt
}
m_NameLabel->setText(name);
DownloadManager::DownloadState state = m_Manager->getState(downloadIndex);
- if (state == DownloadManager::STATE_PAUSED) {
+ if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) {
m_DoneLabel->setVisible(true);
m_Progress->setVisible(false);
m_DoneLabel->setText(tr("Paused"));
m_DoneLabel->setForegroundRole(QPalette::Link);
+ } else if (state == DownloadManager::STATE_FETCHINGMODINFO) {
+ m_DoneLabel->setText(tr("Fetching Info 1"));
+ } else if (state == DownloadManager::STATE_FETCHINGFILEINFO) {
+ m_DoneLabel->setText(tr("Fetching Info 2"));
} else if (state >= DownloadManager::STATE_READY) {
m_DoneLabel->setVisible(true);
m_Progress->setVisible(false);
@@ -218,7 +222,7 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem
} else if (state == DownloadManager::STATE_DOWNLOADING){
menu.addAction(tr("Cancel"), this, SLOT(issueCancel()));
menu.addAction(tr("Pause"), this, SLOT(issuePause()));
- } else if (state == DownloadManager::STATE_PAUSED) {
+ } else if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) {
menu.addAction(tr("Remove"), this, SLOT(issueDelete()));
menu.addAction(tr("Resume"), this, SLOT(issueResume()));
}
diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp
index 24e20bc6..69edc625 100644
--- a/src/downloadmanager.cpp
+++ b/src/downloadmanager.cpp
@@ -28,7 +28,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QFileInfo>
#include <QRegExp>
#include <QDirIterator>
-#include <QSettings>
#include <QInputDialog>
#include <regex>
#include <QMessageBox>
@@ -44,6 +43,66 @@ using namespace MOBase;
static const char UNFINISHED[] = ".unfinished";
+DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const NexusInfo &nexusInfo, int modID, int fileID, const QStringList &URLs)
+{
+ DownloadInfo *info = new DownloadInfo;
+
+ info->m_StartTime.start();
+ info->m_Progress = 0;
+ info->m_ResumePos = 0;
+ info->m_ModID = modID;
+ info->m_FileID = fileID;
+ info->m_NexusInfo = nexusInfo;
+ info->m_Urls = URLs;
+ info->m_CurrentUrl = 0;
+ info->m_Tries = AUTOMATIC_RETRIES;
+ info->m_State = STATE_STARTED;
+
+ return info;
+}
+
+DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(const QString &filePath)
+{
+ DownloadInfo *info = new DownloadInfo;
+
+ QString metaFileName = filePath + ".meta";
+ QSettings metaFile(metaFileName, QSettings::IniFormat);
+ if (metaFile.value("removed", false).toBool()) {
+ return NULL;
+ }
+
+ QString fileName = QFileInfo(filePath).fileName();
+
+ if (fileName.endsWith(UNFINISHED)) {
+ info->m_FileName = fileName.mid(0, fileName.length() - strlen(UNFINISHED));
+ info->m_State = STATE_PAUSED;
+ } else {
+ info->m_FileName = fileName;
+
+ if (metaFile.value("paused", false).toBool()) {
+ info->m_State = STATE_PAUSED;
+ } else if (metaFile.value("installed", false).toBool()) {
+ info->m_State = STATE_INSTALLED;
+ } else {
+ info->m_State = STATE_READY;
+ }
+ }
+
+ info->m_Output.setFileName(filePath);
+ info->m_ModID = metaFile.value("modID", 0).toInt();
+ info->m_FileID = metaFile.value("fileID", 0).toInt();
+ info->m_CurrentUrl = 0;
+ info->m_Urls = metaFile.value("url", "").toString().split(";");
+ info->m_Tries = 0;
+ info->m_NexusInfo.m_Name = metaFile.value("name", 0).toString();
+ info->m_NexusInfo.m_ModName = metaFile.value("modName", "").toString();
+ info->m_NexusInfo.m_Version = metaFile.value("version", 0).toString();
+ info->m_NexusInfo.m_NewestVersion = metaFile.value("newestVersion", "").toString();
+ info->m_NexusInfo.m_Category = metaFile.value("category", 0).toInt();
+
+ return info;
+}
+
void DownloadManager::DownloadInfo::setName(QString newName, bool renameFile)
{
QString oldMetaFileName = QString("%1.meta").arg(m_FileName);
@@ -66,6 +125,16 @@ void DownloadManager::DownloadInfo::setName(QString newName, bool renameFile)
m_Output.setFileName(newName);
}
+bool DownloadManager::DownloadInfo::isPausedState()
+{
+ return m_State == STATE_PAUSED || m_State == STATE_ERROR;
+}
+
+QString DownloadManager::DownloadInfo::currentURL()
+{
+ return m_Urls[m_CurrentUrl];
+}
+
DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent)
: QObject(parent), m_NexusInterface(nexusInterface), m_DirWatcher()
@@ -86,7 +155,7 @@ bool DownloadManager::downloadsInProgress()
{
for (QVector<DownloadInfo*>::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end(); ++iter) {
if ((*iter)->m_State < STATE_READY) {
- return true;
+// return true;
}
}
return false;
@@ -112,8 +181,6 @@ void DownloadManager::refreshList()
delete *Iter;
Iter = m_ActiveDownloads.erase(Iter);
} else {
-//qDebug("leaving %s / %s in the download list since it's still being processed",
-// qPrintable((*Iter)->m_FileName), qPrintable((*Iter)->m_Output.fileName()));
++Iter;
}
}
@@ -134,72 +201,42 @@ void DownloadManager::refreshList()
if (QString::compare((*Iter)->m_FileName, file, Qt::CaseInsensitive) == 0) {
Exists = true;
} else if (QString::compare(QFileInfo((*Iter)->m_Output.fileName()).fileName(), file, Qt::CaseInsensitive) == 0) {
-//qDebug("%s found in m_Output: %s", qPrintable(file), qPrintable((*Iter)->m_Output.fileName()));
Exists = true;
}
}
if (Exists) {
continue;
}
- DownloadInfo *info = new DownloadInfo;
- info->m_State = STATE_READY;
- if (file.endsWith(UNFINISHED)) {
- info->m_FileName = file.mid(0, file.length() - strlen(UNFINISHED));
- info->m_State = STATE_PAUSED;
- } else {
- info->m_FileName = file;
- }
- info->m_Output.setFileName(QString("%1/%2").arg(QDir::fromNativeSeparators(m_OutputDirectory)).arg(file));
- QString metaFileName = QString("%1.meta").arg(info->m_Output.fileName());
- QSettings metaFile(metaFileName, QSettings::IniFormat);
- if (metaFile.value("removed", false).toBool()) {
- continue;
- }
- info->m_ModID = metaFile.value("modID", 0).toInt();
- info->m_FileID = metaFile.value("fileID", 0).toInt();
- info->m_Url = metaFile.value("url", "").toString();
- info->m_NexusInfo.m_Name = metaFile.value("name", 0).toString();
- info->m_NexusInfo.m_ModName = metaFile.value("modName", "").toString();
- info->m_NexusInfo.m_Version = metaFile.value("version", 0).toString();
- info->m_NexusInfo.m_NewestVersion = metaFile.value("newestVersion", "").toString();
- info->m_NexusInfo.m_Category = metaFile.value("category", 0).toInt();
- if (metaFile.value("installed", false).toBool()) {
- info->m_State = STATE_INSTALLED;
- }
- if (metaFile.value("paused", false).toBool()) {
- info->m_State = STATE_PAUSED;
+ QString fileName = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + file;
+
+ DownloadInfo *info = DownloadInfo::createFromMeta(fileName);
+ if (info != NULL) {
+ m_ActiveDownloads.push_front(info);
}
- m_ActiveDownloads.push_front(info);
}
qDebug("downloads after refresh: %d", m_ActiveDownloads.size());
emit update(-1);
}
-bool DownloadManager::addDownload(const QUrl &url, int modID, int fileID, const NexusInfo &nexusInfo)
+bool DownloadManager::addDownload(const QStringList &URLs,
+ int modID, int fileID, const NexusInfo &nexusInfo)
{
- QString fileName = QFileInfo(url.path()).fileName();
+ QString fileName = QFileInfo(URLs.first()).fileName();
if (fileName.isEmpty()) {
fileName = "unknown";
}
- QNetworkRequest request(url);
- return addDownload(m_NexusInterface->getAccessManager()->get(request), fileName, modID, fileID, nexusInfo);
+ QNetworkRequest request(URLs.first());
+ return addDownload(m_NexusInterface->getAccessManager()->get(request), URLs, fileName, modID, fileID, nexusInfo);
}
-bool DownloadManager::addDownload(QNetworkReply *reply, const QString &fileName, int modID,
- int fileID, const NexusInfo &nexusInfo)
+bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName,
+ int modID, int fileID, const NexusInfo &nexusInfo)
{
// download invoked from an already open network reply (i.e. download link in the browser)
- DownloadInfo *newDownload = new DownloadInfo;
- newDownload->m_StartTime.start();
- newDownload->m_Progress = 0;
- newDownload->m_ResumePos = 0;
- newDownload->m_ModID = modID;
- newDownload->m_FileID = fileID;
- newDownload->m_NexusInfo = nexusInfo;
- newDownload->m_State = STATE_STARTED;
+ DownloadInfo *newDownload = DownloadInfo::createNew(nexusInfo, modID, fileID, URLs);
QString baseName = fileName;
@@ -223,18 +260,20 @@ bool DownloadManager::addDownload(QNetworkReply *reply, const QString &fileName,
newDownload->setName(getDownloadFileName(baseName), false);
- addDownload(reply, newDownload, false);
+ startDownload(reply, newDownload, false);
emit update(-1);
return true;
}
-void DownloadManager::addDownload(QNetworkReply *reply, DownloadInfo *newDownload, bool resume)
+void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownload, bool resume)
{
newDownload->m_Reply = reply;
- newDownload->m_State = STATE_DOWNLOADING;
- newDownload->m_Url = reply->url().toString();
+ setState(newDownload, STATE_DOWNLOADING);
+ if (newDownload->m_Urls.count() == 0) {
+ newDownload->m_Urls = QStringList(reply->url().toString());
+ }
QIODevice::OpenMode mode = QIODevice::WriteOnly;
if (resume) {
@@ -256,7 +295,6 @@ void DownloadManager::addDownload(QNetworkReply *reply, DownloadInfo *newDownloa
emit aboutToUpdate();
m_ActiveDownloads.append(newDownload);
-// qDebug("downloads after add: %d", m_ActiveDownloads.size());
emit update(-1);
}
@@ -285,7 +323,7 @@ void DownloadManager::removeFile(int index, bool deleteFile)
return;
}
- if (download->m_State == STATE_PAUSED) {
+ if ((download->m_State == STATE_PAUSED) || (download->m_State == STATE_ERROR)) {
filePath = download->m_Output.fileName();
}
@@ -362,7 +400,7 @@ void DownloadManager::removeDownload(int index, bool deleteFile)
}
removeFile(index, deleteFile);
- delete m_ActiveDownloads[index];
+ delete m_ActiveDownloads.at(index);
m_ActiveDownloads.erase(m_ActiveDownloads.begin() + index);
}
emit update(-1);
@@ -379,8 +417,8 @@ void DownloadManager::cancelDownload(int index)
return;
}
- if (m_ActiveDownloads[index]->m_State == STATE_DOWNLOADING) {
- m_ActiveDownloads[index]->m_State = STATE_CANCELING;
+ if (m_ActiveDownloads.at(index)->m_State == STATE_DOWNLOADING) {
+ setState(m_ActiveDownloads.at(index), STATE_CANCELING);
qDebug("canceled %d - %s", index, m_ActiveDownloads[index]->m_FileName.toUtf8().constData());
}
}
@@ -393,8 +431,8 @@ void DownloadManager::pauseDownload(int index)
return;
}
- if (m_ActiveDownloads[index]->m_State == STATE_DOWNLOADING) {
- m_ActiveDownloads[index]->m_State = STATE_PAUSING;
+ if (m_ActiveDownloads.at(index)->m_State == STATE_DOWNLOADING) {
+ setState(m_ActiveDownloads.at(index), STATE_PAUSING);
qDebug("pausing %d - %s", index, m_ActiveDownloads[index]->m_FileName.toUtf8().constData());
}
}
@@ -406,14 +444,18 @@ void DownloadManager::resumeDownload(int index)
reportError(tr("invalid index %1").arg(index));
return;
}
- if (m_ActiveDownloads[index]->m_State == STATE_PAUSED) {
- DownloadInfo *info = m_ActiveDownloads[index];
- qDebug("request resume from url %s", info->m_Url.toUtf8().constData());
- QNetworkRequest request(info->m_Url);
+ DownloadInfo *info = m_ActiveDownloads[index];
+ if (info->isPausedState()) {
+ if (info->m_State == STATE_ERROR) {
+ info->m_CurrentUrl = (info->m_CurrentUrl + 1) % info->m_Urls.count();
+ }
+ qDebug("request resume from url %s", qPrintable(info->currentURL()));
+ QNetworkRequest request(info->currentURL());
info->m_ResumePos = info->m_Output.size();
+ qDebug("resume at %lld bytes", info->m_ResumePos);
QByteArray rangeHeader = "bytes=" + QByteArray::number(info->m_ResumePos) + "-";
request.setRawHeader("Range", rangeHeader);
- addDownload(m_NexusInterface->getAccessManager()->get(request), info, true);
+ startDownload(m_NexusInterface->getAccessManager()->get(request), info, true);
}
emit update(index);
}
@@ -427,6 +469,11 @@ void DownloadManager::queryInfo(int index)
}
DownloadInfo *info = m_ActiveDownloads[index];
+ if (info->m_State < DownloadManager::STATE_READY) {
+ // UI shouldn't allow this
+ return;
+ }
+
if (info->m_ModID == 0UL) {
QString fileName = getFileName(index);
QString ignore;
@@ -447,7 +494,9 @@ void DownloadManager::queryInfo(int index)
info->m_ModID = modIDString.toInt(NULL, 10);
}
}
- m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_ModID, this, qVariantFromValue(static_cast<void*>(info))));
+ info->m_ReQueried = true;
+ setState(info, STATE_FETCHINGMODINFO);
+// m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_ModID, this, qVariantFromValue(static_cast<void*>(info))));
}
@@ -532,10 +581,12 @@ void DownloadManager::markInstalled(int index)
if ((index < 0) || (index >= m_ActiveDownloads.size())) {
throw MyException(tr("invalid index"));
}
- QSettings metaFile(QString("%1.meta").arg(m_ActiveDownloads.at(index)->m_Output.fileName()), QSettings::IniFormat);
+
+ DownloadInfo *info = m_ActiveDownloads.at(index);
+ QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat);
metaFile.setValue("installed", true);
- m_ActiveDownloads.at(index)->m_State = STATE_INSTALLED;
+ setState(m_ActiveDownloads.at(index), STATE_INSTALLED);
}
@@ -569,6 +620,32 @@ QString DownloadManager::getFileNameFromNetworkReply(QNetworkReply *reply)
}
+void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadManager::DownloadState state)
+{
+ qDebug("change state of %s %d -> %d", qPrintable(info->m_FileName), info->m_State, state);
+ info->m_State = state;
+ switch (state) {
+ case STATE_PAUSED:
+ case STATE_ERROR: {
+ info->m_Reply->abort();
+ } break;
+ case STATE_CANCELED: {
+ info->m_Reply->abort();
+ } break;
+ case STATE_FETCHINGMODINFO: {
+ m_RequestIDs.insert(m_NexusInterface->requestDescription(info->m_ModID, this, qVariantFromValue(static_cast<void*>(info))));
+ } break;
+ case STATE_FETCHINGFILEINFO: {
+ m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_ModID, this, qVariantFromValue(static_cast<void*>(info))));
+ } break;
+ case STATE_READY: {
+ createMetaFile(info);
+ } break;
+ default: /* NOP */ break;
+ }
+}
+
+
DownloadManager::DownloadInfo *DownloadManager::findDownload(QObject *reply, int *index) const
{
for (int i = 0; i < m_ActiveDownloads.size(); ++i) {
@@ -592,12 +669,14 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
DownloadInfo *info = findDownload(this->sender(), &index);
if (info != NULL) {
if (info->m_State == STATE_CANCELING) {
- info->m_Reply->abort();
- info->m_State = STATE_CANCELED;
+ setState(info, STATE_CANCELED);
} else if (info->m_State == STATE_PAUSING) {
- info->m_Reply->abort();
- info->m_State = STATE_PAUSED;
+ setState(info, STATE_PAUSED);
} else {
+ if (bytesTotal > info->m_TotalSize) {
+ qDebug("file size %s: %lld", qPrintable(info->m_FileName), bytesTotal);
+ info->m_TotalSize = bytesTotal;
+ }
info->m_Progress = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal);
emit update(index);
}
@@ -619,7 +698,7 @@ void DownloadManager::createMetaFile(DownloadInfo *info)
QSettings metaFile(QString("%1.meta").arg(info->m_Output.fileName()), QSettings::IniFormat);
metaFile.setValue("modID", info->m_ModID);
metaFile.setValue("fileID", info->m_FileID);
- metaFile.setValue("url", info->m_Url);
+ metaFile.setValue("url", info->m_Urls.join(";"));
metaFile.setValue("name", info->m_NexusInfo.m_Name);
metaFile.setValue("modName", info->m_NexusInfo.m_ModName);
metaFile.setValue("version", info->m_NexusInfo.m_Version);
@@ -627,7 +706,8 @@ void DownloadManager::createMetaFile(DownloadInfo *info)
metaFile.setValue("newestVersion", info->m_NexusInfo.m_NewestVersion);
metaFile.setValue("category", info->m_NexusInfo.m_Category);
metaFile.setValue("installed", info->m_State == DownloadManager::STATE_INSTALLED);
- metaFile.setValue("paused", info->m_State == DownloadManager::STATE_PAUSED);
+ metaFile.setValue("paused", (info->m_State == DownloadManager::STATE_PAUSED) ||
+ (info->m_State == DownloadManager::STATE_ERROR));
// slightly hackish...
for (int i = 0; i < m_ActiveDownloads.size(); ++i) {
@@ -655,11 +735,9 @@ void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant r
info->m_NexusInfo.m_NewestVersion = result["version"].toString();
if (info->m_FileID != 0) {
- createMetaFile(info);
- info->m_State = STATE_READY;
+ setState(info, STATE_READY);
} else {
- info->m_State = STATE_FETCHINGFILEINFO;
- m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_ModID, this, qVariantFromValue(static_cast<void*>(info))));
+ setState(info, STATE_FETCHINGFILEINFO);
}
}
@@ -703,8 +781,7 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD
}
}
- if ((info->m_State == STATE_READY) ||
- (info->m_State == STATE_INSTALLED)) {
+ if (info->m_ReQueried) {
if (found) {
emit showMessage(tr("Information updated"));
} else if (result.count() == 0) {
@@ -727,12 +804,12 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD
}
} else {
if (info->m_FileID == 0) {
- qWarning("could not determine file id for %s", info->m_FileName.toUtf8().constData());
+ qWarning("could not determine file id for %s (state %d)",
+ info->m_FileName.toUtf8().constData(), info->m_State);
}
-
- info->m_State = STATE_READY;
}
- createMetaFile(info);
+
+ setState(info, STATE_READY);
}
@@ -802,11 +879,14 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u
}
qSort(resultList.begin(), resultList.end(), ServerByPreference);
- QVariantMap dlServer = resultList.first().toMap();
- qDebug("picked url: %s", dlServer["URI"].toString().toUtf8().constData());
+ QStringList URLs;
+ foreach (const QVariant &server, resultList) {
+ URLs.append(server.toMap()["URI"].toString());
+ }
+ qDebug("urls: %s", qPrintable(URLs.join(";")));
- addDownload(dlServer["URI"].toString(), modID, fileID, info);
+ addDownload(URLs, modID, fileID, info);
}
@@ -824,12 +904,11 @@ void DownloadManager::nxmRequestFailed(int modID, QVariant, int requestID, const
for (QVector<DownloadInfo*>::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end(); ++iter, ++index) {
DownloadInfo *info = *iter;
if (info->m_ModID == modID) {
- if (info->m_State == STATE_STARTED) {
+ if (info->m_State < STATE_FETCHINGMODINFO) {
m_ActiveDownloads.erase(iter);
delete info;
- } else if ((info->m_State == STATE_FETCHINGFILEINFO) ||
- (info->m_State == STATE_FETCHINGMODINFO)) {
- info->m_State = STATE_READY;
+ } else {
+ setState(info, STATE_READY);
}
emit update(index);
break;
@@ -851,24 +930,31 @@ void DownloadManager::downloadFinished()
info->m_Output.write(data);
info->m_Output.close();
+ bool error = false;
+
if ((info->m_State != STATE_CANCELING) &&
(info->m_State != STATE_PAUSING)) {
if ((info->m_Output.size() == 0) ||
(reply->error() != QNetworkReply::NoError) ||
reply->header(QNetworkRequest::ContentTypeHeader).toString().startsWith("text", Qt::CaseInsensitive)) {
- emit showMessage(tr("Download failed: %1 (%2)").arg(reply->errorString()).arg(reply->error()));
- info->m_State = STATE_PAUSING;
+ if (info->m_Tries == 0) {
+ emit showMessage(tr("Download failed: %1 (%2)").arg(reply->errorString()).arg(reply->error()));
+ }
+ error = true;
+ setState(info, STATE_PAUSING);
}
}
if (info->m_State == STATE_CANCELING) {
- info->m_Reply->abort();
- info->m_State = STATE_CANCELED;
+ setState(info, STATE_CANCELED);
} else if (info->m_State == STATE_PAUSING) {
info->m_Output.write(info->m_Reply->readAll());
- info->m_Reply->abort();
- info->m_State = STATE_PAUSED;
+ if (error) {
+ setState(info, STATE_ERROR);
+ } else {
+ setState(info, STATE_PAUSED);
+ }
}
if (info->m_State == STATE_CANCELED) {
@@ -877,12 +963,13 @@ void DownloadManager::downloadFinished()
delete info;
m_ActiveDownloads.erase(m_ActiveDownloads.begin() + index);
emit update(-1);
- } else if (info->m_State == STATE_PAUSED) {
+ } else if (info->isPausedState()) {
info->m_Output.close();
createMetaFile(info);
emit update(index);
} else {
- info->m_State = STATE_FETCHINGMODINFO;
+ setState(info, STATE_FETCHINGMODINFO); // need to set this state before changing the file name, otherwise .unfinished is appended
+
QString newName = getFileNameFromNetworkReply(reply);
QString oldName = QFileInfo(info->m_Output).fileName();
if (!newName.isEmpty() && (newName != oldName)) {
@@ -891,12 +978,15 @@ void DownloadManager::downloadFinished()
info->setName(m_OutputDirectory + "/" + info->m_FileName, true); // don't rename but remove the ".unfinished" extension
}
- m_RequestIDs.insert(m_NexusInterface->requestDescription(info->m_ModID, this, qVariantFromValue(static_cast<void*>(info))));
-
emit update(index);
}
reply->close();
reply->deleteLater();
+
+ if (info->m_Tries > 0) {
+ --info->m_Tries;
+ resumeDownload(index);
+ }
} else {
qWarning("no download index %d", index);
}
@@ -915,8 +1005,7 @@ void DownloadManager::metaDataChanged()
refreshAlphabeticalTranslation();
if (!info->m_Output.open(QIODevice::WriteOnly | QIODevice::Append)) {
reportError(tr("failed to re-open %1").arg(info->m_FileName));
- info->m_State = STATE_CANCELING;
- info->m_Reply->abort();
+ setState(info, STATE_CANCELING);
}
}
} else {
diff --git a/src/downloadmanager.h b/src/downloadmanager.h
index da57f167..407acd3c 100644
--- a/src/downloadmanager.h
+++ b/src/downloadmanager.h
@@ -30,7 +30,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QTime>
#include <QVector>
#include <QMap>
+#include <QStringList>
#include <QFileSystemWatcher>
+#include <QSettings>
struct NexusInfo {
@@ -63,6 +65,7 @@ public:
STATE_PAUSING,
STATE_CANCELED,
STATE_PAUSED,
+ STATE_ERROR,
STATE_FETCHINGMODINFO,
STATE_FETCHINGFILEINFO,
STATE_READY,
@@ -79,10 +82,18 @@ private:
int m_Progress;
int m_ModID;
int m_FileID;
- NexusInfo m_NexusInfo;
DownloadState m_State;
- QString m_Url;
+ int m_CurrentUrl;
+ QStringList m_Urls;
qint64 m_ResumePos;
+ qint64 m_TotalSize;
+ int m_Tries;
+ bool m_ReQueried;
+
+ NexusInfo m_NexusInfo;
+
+ static DownloadInfo *createNew(const NexusInfo &nexusInfo, int modID, int fileID, const QStringList &URLs);
+ static DownloadInfo *createFromMeta(const QString &filePath);
/**
* @brief rename the file
@@ -93,6 +104,13 @@ private:
* yet exist, set this to false
**/
void setName(QString newName, bool renameFile);
+
+ bool isPausedState();
+
+ QString currentURL();
+
+ private:
+ DownloadInfo() : m_TotalSize(0), m_ReQueried(false) {}
};
public:
@@ -135,7 +153,7 @@ public:
* @param nexusInfo information previously retrieved from the nexus network
* @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again
**/
- bool addDownload(const QUrl &url, int modID, int fileID = 0, const NexusInfo &nexusInfo = NexusInfo());
+ bool addDownload(const QStringList &URLs, int modID, int fileID = 0, const NexusInfo &nexusInfo = NexusInfo());
/**
* @brief download from an already open network connection
@@ -147,7 +165,7 @@ public:
* @param nexusInfo information previously retrieved from the nexus network
* @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again
**/
- bool addDownload(QNetworkReply *reply, const QString &fileName, int modID, int fileID = 0, const NexusInfo &nexusInfo = NexusInfo());
+ bool addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName, int modID, int fileID = 0, const NexusInfo &nexusInfo = NexusInfo());
/**
* @brief start a download using a nxm-link
@@ -310,7 +328,7 @@ private:
// QString getOutputPath(const QUrl &url, const QString &fileName) const;
QString getDownloadFileName(const QString &baseName) const;
- void addDownload(QNetworkReply *reply, DownloadInfo *newDownload, bool resume);
+ void startDownload(QNetworkReply *reply, DownloadInfo *newDownload, bool resume);
// important: the caller has to lock the list-mutex, otherwise the DownloadInfo-pointer might get invalidated at any time
DownloadInfo *findDownload(QObject *reply, int *index = NULL) const;
@@ -323,6 +341,12 @@ private:
QString getFileNameFromNetworkReply(QNetworkReply *reply);
+ void setState(DownloadInfo *info, DownloadManager::DownloadState state);
+
+private:
+
+ static const int AUTOMATIC_RETRIES = 3;
+
private:
NexusInterface *m_NexusInterface;
@@ -334,6 +358,8 @@ private:
QFileSystemWatcher m_DirWatcher;
+ std::map<QString, int> m_DownloadFails;
+
};
#endif // DOWNLOADMANAGER_H
diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp
index 27dfea6e..d92cae15 100644
--- a/src/installationmanager.cpp
+++ b/src/installationmanager.cpp
@@ -520,7 +520,7 @@ bool InstallationManager::testOverwrite(const QString &modsDirectory, QString &m
}
// remove the directory with all content, then recreate it empty
- removeDir(targetDirectory);
+ shellDelete(QStringList(targetDirectory), NULL);
if (!QDir().mkdir(targetDirectory)) {
// windows may keep the directory around for a moment, preventing its re-creation
Sleep(100);
@@ -781,6 +781,22 @@ bool InstallationManager::installFomodInternal(DirectoryTree *&baseNode, const Q
}
+static BOOL CALLBACK BringToFront(HWND hwnd, LPARAM lParam)
+{
+ DWORD procid;
+
+ GetWindowThreadProcessId(hwnd, &procid);
+
+ if (procid == static_cast<DWORD>(lParam)) {
+ ::SetForegroundWindow(hwnd);
+ ::SetLastError(NOERROR);
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
+
bool InstallationManager::installFomodExternal(const QString &fileName, const QString &pluginsFileName, const QString &modDirectory)
{
wchar_t binary[MAX_PATH];
@@ -797,13 +813,24 @@ bool InstallationManager::installFomodExternal(const QString &fileName, const QS
GameInfo &gameInfo = GameInfo::instance();
- QString binaryDestination = modDirectory.mid(0).append("/").append(ToQString(gameInfo.getBinaryName()));
-
- // NCC assumes the installation directory is the game directory and may try to access the binary to determine version information
- QFile::copy(QDir::fromNativeSeparators(ToQString(gameInfo.getGameDirectory().append(L"\\").append(gameInfo.getBinaryName()))),
- binaryDestination);
-
- ON_BLOCK_EXIT([&binaryDestination] { if (!QFile::remove(binaryDestination)) qCritical("failed to remove %s", qPrintable(binaryDestination)); } );
+ QStringList copiedFiles;
+ QStringList patterns;
+ patterns.append(QDir::fromNativeSeparators(ToQString(gameInfo.getBinaryName())));
+ patterns.append("*se_loader.exe");
+ QDirIterator iter(QDir::fromNativeSeparators(ToQString(gameInfo.getGameDirectory())), patterns);
+ QDir modDir(modDirectory);
+ while (iter.hasNext()) {
+ iter.next();
+ QString destination = modDir.absoluteFilePath(iter.fileInfo().fileName());
+ if (QFile::copy(iter.fileInfo().absoluteFilePath(), destination)) {
+ copiedFiles.append(destination);
+ }
+ }
+ ON_BLOCK_EXIT([&copiedFiles] {
+ foreach (const QString &fileName, copiedFiles) {
+ if (!QFile::remove(fileName)) qCritical("failed to remove %s", qPrintable(fileName));
+ }
+ });
SHELLEXECUTEINFOW execInfo = {0};
execInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
@@ -820,13 +847,20 @@ bool InstallationManager::installFomodExternal(const QString &fileName, const QS
return false;
}
- QProgressDialog busyDialog(tr("Running external installer.\nNote: This installer will not be aware of other installed mods!"), tr("Force Close"), 0, 0, m_ParentWidget);
+ QProgressDialog busyDialog(tr("Preparing external installer, this can take a few minutes.\nNote: This installer will not be aware of other installed mods (including skse)!"), tr("Force Close"), 0, 0, m_ParentWidget);
busyDialog.setWindowModality(Qt::WindowModal);
bool confirmCancel = false;
busyDialog.show();
bool finished = false;
+ DWORD procid = ::GetProcessId(execInfo.hProcess);
+ bool inFront = false;
while (true) {
QCoreApplication::processEvents();
+ if (!inFront) {
+ if (!::EnumWindows(BringToFront, procid) && (::GetLastError() == NOERROR)) {
+ inFront = true;
+ }
+ }
DWORD res = ::WaitForSingleObject(execInfo.hProcess, 100);
if (res == WAIT_OBJECT_0) {
finished = true;
@@ -872,7 +906,7 @@ bool InstallationManager::installFomodExternal(const QString &fileName, const QS
errorOccured = true;
}
} // if it's a directory and the target exists that isn't really a problem
-
+ // TODO: use shellRename?
if (!QFile::rename(fileInfo.absoluteFilePath(), newName)) {
// moving doesn't work when merging
if (!copyDir(fileInfo.absoluteFilePath(), newName, true)) {
@@ -890,7 +924,7 @@ bool InstallationManager::installFomodExternal(const QString &fileName, const QS
}
QString dataDir = modDirectory.mid(0).append("/Data");
- if (!removeDir(dataDir)) {
+ if (!shellDelete(QStringList(dataDir), NULL)) {
qCritical("failed to remove data directory from %s", dataDir.toUtf8().constData());
errorOccured = true;
}
@@ -905,7 +939,7 @@ bool InstallationManager::installFomodExternal(const QString &fileName, const QS
return true;
} else {
// after cancelation or error the installer may leave the empty mod directory
- if (!removeDir(modDirectory)) {
+ if (!shellDelete(QStringList(modDirectory), NULL)) {
qCritical ("failed to remove empty mod directory %s", modDirectory.toUtf8().constData());
}
return false;
diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp
index 6a1857fc..c891f4c5 100644
--- a/src/logbuffer.cpp
+++ b/src/logbuffer.cpp
@@ -17,141 +17,141 @@ 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 "logbuffer.h"
-#include "report.h"
-#include <QMutexLocker>
-#include <QFile>
-#include <Windows.h>
-
-QScopedPointer<LogBuffer> LogBuffer::s_Instance;
-QMutex LogBuffer::s_Mutex;
-
-
-LogBuffer::LogBuffer(int messageCount, QtMsgType minMsgType, const QString &outputFileName)
- : QObject(NULL), m_OutFileName(outputFileName), m_ShutDown(false),
- m_MinMsgType(minMsgType), m_NumMessages(0)
-{
- m_Messages.resize(messageCount);
-}
-
-LogBuffer::~LogBuffer()
-{
-#if QT_VERSION >= 0x050000
- qInstallMessageHandler(0);
-#else
- qInstallMsgHandler(0);
-#endif
- if (!m_ShutDown) {
- write();
- }
-}
-
-
-void LogBuffer::logMessage(QtMsgType type, const QString &message)
-{
- if (type >= m_MinMsgType) {
- m_Messages.at(m_NumMessages % m_Messages.size()) = message;
- ++m_NumMessages;
- if (type >= QtCriticalMsg) {
- write();
- }
- }
-}
-
-
-void LogBuffer::write() const
-{
- if (m_NumMessages == 0) {
- return;
- }
-
- QFile file(m_OutFileName);
- if (!file.open(QIODevice::WriteOnly)) {
- reportError(tr("failed to write log to %1: %2").arg(m_OutFileName).arg(file.errorString()));
- return;
- }
-
- unsigned int i = (m_NumMessages > m_Messages.size()) ? m_NumMessages - m_Messages.size()
- : 0U;
- for (; i < m_NumMessages; ++i) {
- file.write(m_Messages.at(i % m_Messages.size()).toUtf8());
- file.write("\r\n");
- }
-}
-
-
-void LogBuffer::init(int messageCount, QtMsgType minMsgType, const QString &outputFileName)
-{
- QMutexLocker guard(&s_Mutex);
-
- if (!s_Instance.isNull()) {
- s_Instance.reset();
- }
- s_Instance.reset(new LogBuffer(messageCount, minMsgType, outputFileName));
-#if QT_VERSION >= 0x050000
- qInstallMessageHandler(LogBuffer::log);
-#else
- qInstallMsgHandler(LogBuffer::log);
-#endif
-}
-
-#if QT_VERSION >= 0x050000
-
-void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, const QString &message)
-{
- QMutexLocker guard(&s_Mutex);
- if (!s_Instance.isNull()) {
- s_Instance->logMessage(type, message);
- }
- fprintf(stdout, "(%s:%u, %s) %s\n", context.file, context.line, context.function, message.toLocal8Bit());
- fflush(stdout);
-}
-
-#else
-
-void LogBuffer::log(QtMsgType type, const char *message)
-{
- QMutexLocker guard(&s_Mutex);
- if (!s_Instance.isNull()) {
- s_Instance->logMessage(type, message);
- }
- fprintf(stdout, "%s\n", message);
- fflush(stdout);
-}
-
-#endif
-
-void LogBuffer::writeNow()
-{
- QMutexLocker guard(&s_Mutex);
- if (!s_Instance.isNull()) {
- s_Instance->write();
- }
-}
-
-
-void LogBuffer::cleanQuit()
-{
- QMutexLocker guard(&s_Mutex);
- if (!s_Instance.isNull()) {
- s_Instance->m_ShutDown = true;
- }
-}
-
-void log(const char *format, ...)
-{
- va_list argList;
- va_start(argList, format);
-
- static const int BUFFERSIZE = 1000;
-
- char buffer[BUFFERSIZE + 1];
- buffer[BUFFERSIZE] = '\0';
-
- vsnprintf(buffer, BUFFERSIZE, format, argList);
-
- qCritical("%s", buffer);
-
- va_end(argList);
-}
-
+#include "logbuffer.h"
+#include "report.h"
+#include <QMutexLocker>
+#include <QFile>
+#include <Windows.h>
+
+QScopedPointer<LogBuffer> LogBuffer::s_Instance;
+QMutex LogBuffer::s_Mutex;
+
+
+LogBuffer::LogBuffer(int messageCount, QtMsgType minMsgType, const QString &outputFileName)
+ : QObject(NULL), m_OutFileName(outputFileName), m_ShutDown(false),
+ m_MinMsgType(minMsgType), m_NumMessages(0)
+{
+ m_Messages.resize(messageCount);
+}
+
+LogBuffer::~LogBuffer()
+{
+#if QT_VERSION >= 0x050000
+ qInstallMessageHandler(0);
+#else
+ qInstallMsgHandler(0);
+#endif
+// if (!m_ShutDown) {
+ write();
+// }
+}
+
+
+void LogBuffer::logMessage(QtMsgType type, const QString &message)
+{
+ if (type >= m_MinMsgType) {
+ m_Messages.at(m_NumMessages % m_Messages.size()) = message;
+ ++m_NumMessages;
+ if (type >= QtCriticalMsg) {
+ write();
+ }
+ }
+}
+
+
+void LogBuffer::write() const
+{
+ if (m_NumMessages == 0) {
+ return;
+ }
+
+ QFile file(m_OutFileName);
+ if (!file.open(QIODevice::WriteOnly)) {
+ reportError(tr("failed to write log to %1: %2").arg(m_OutFileName).arg(file.errorString()));
+ return;
+ }
+
+ unsigned int i = (m_NumMessages > m_Messages.size()) ? m_NumMessages - m_Messages.size()
+ : 0U;
+ for (; i < m_NumMessages; ++i) {
+ file.write(m_Messages.at(i % m_Messages.size()).toUtf8());
+ file.write("\r\n");
+ }
+}
+
+
+void LogBuffer::init(int messageCount, QtMsgType minMsgType, const QString &outputFileName)
+{
+ QMutexLocker guard(&s_Mutex);
+
+ if (!s_Instance.isNull()) {
+ s_Instance.reset();
+ }
+ s_Instance.reset(new LogBuffer(messageCount, minMsgType, outputFileName));
+#if QT_VERSION >= 0x050000
+ qInstallMessageHandler(LogBuffer::log);
+#else
+ qInstallMsgHandler(LogBuffer::log);
+#endif
+}
+
+#if QT_VERSION >= 0x050000
+
+void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, const QString &message)
+{
+ QMutexLocker guard(&s_Mutex);
+ if (!s_Instance.isNull()) {
+ s_Instance->logMessage(type, message);
+ }
+ fprintf(stdout, "(%s:%u, %s) %s\n", context.file, context.line, context.function, message.toLocal8Bit());
+ fflush(stdout);
+}
+
+#else
+
+void LogBuffer::log(QtMsgType type, const char *message)
+{
+ QMutexLocker guard(&s_Mutex);
+ if (!s_Instance.isNull()) {
+ s_Instance->logMessage(type, message);
+ }
+ fprintf(stdout, "%s\n", message);
+ fflush(stdout);
+}
+
+#endif
+
+void LogBuffer::writeNow()
+{
+ QMutexLocker guard(&s_Mutex);
+ if (!s_Instance.isNull()) {
+ s_Instance->write();
+ }
+}
+
+
+void LogBuffer::cleanQuit()
+{
+ QMutexLocker guard(&s_Mutex);
+ if (!s_Instance.isNull()) {
+ s_Instance->m_ShutDown = true;
+ }
+}
+
+void log(const char *format, ...)
+{
+ va_list argList;
+ va_start(argList, format);
+
+ static const int BUFFERSIZE = 1000;
+
+ char buffer[BUFFERSIZE + 1];
+ buffer[BUFFERSIZE] = '\0';
+
+ vsnprintf(buffer, BUFFERSIZE, format, argList);
+
+ qCritical("%s", buffer);
+
+ va_end(argList);
+}
+
diff --git a/src/main.cpp b/src/main.cpp
index af6649b2..268bb98f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -34,6 +34,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <cstdarg>
#include <inject.h>
#include <appconfig.h>
+#include <utility.h>
#include <stdexcept>
#include "mainwindow.h"
#include "report.h"
@@ -60,6 +61,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QBuffer>
#include <QSplashScreen>
#include <QDirIterator>
+#include <QDesktopServices>
#include <eh.h>
#include <windows_error.h>
@@ -76,8 +78,13 @@ void removeOldLogfiles()
QDir::Files, QDir::Name);
if (files.count() > 5) {
+ QStringList deleteFiles;
for (int i = 0; i < files.count() - 5; ++i) {
- QFile::remove(files.at(i).absoluteFilePath());
+ deleteFiles.append(files.at(i).absoluteFilePath());
+ }
+
+ if (!shellDelete(deleteFiles, NULL)) {
+ qWarning("failed to remove log files: %s", qPrintable(windowsErrorString(::GetLastError())));
}
}
}
@@ -86,13 +93,15 @@ void removeOldLogfiles()
// set up required folders (for a first install or after an update or to fix a broken installation)
bool bootstrap()
{
+ qDebug("bootstapping");
+
GameInfo &gameInfo = GameInfo::instance();
// remove the temporary backup directory in case we're restarting after an update
QString moDirectory = QDir::fromNativeSeparators(ToQString(gameInfo.getOrganizerDirectory()));
QString backupDirectory = moDirectory.mid(0).append("/update_backup");
if (QDir(backupDirectory).exists()) {
- removeDir(backupDirectory);
+ shellDelete(QStringList(backupDirectory), NULL);
}
// cycle logfile
@@ -140,6 +149,7 @@ bool bootstrap()
}
// verify the hook-dll exists
+ qDebug("checking dll");
QString dllName = qApp->applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName());
HMODULE dllMod = ::LoadLibraryW(ToWString(dllName).c_str());
if (dllMod == NULL) {
@@ -176,6 +186,8 @@ void cleanupDir()
static const int NUM_FILES = sizeof(fileNames) / sizeof(QString);
+ qDebug("cleaning up unused files");
+
for (int i = 0; i < NUM_FILES; ++i) {
if (QFile::remove(QApplication::applicationDirPath().append("/").append(fileNames[i]))) {
qDebug("%s removed in cleanup",
@@ -211,8 +223,8 @@ LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs
if (QMessageBox::question(NULL, QObject::tr("Woops"),
QObject::tr("ModOrganizer has crashed! Should a diagnostic file be created? If you send me this file "
- "by email (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"),
+ "(%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"));
@@ -258,13 +270,16 @@ void registerMetaTypes()
registerExecutable();
}
-
int main(int argc, char *argv[])
{
MOApplication application(argc, argv);
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
LogBuffer::init(20, QtDebugMsg, application.applicationDirPath().append("/logs/mo_interface.log"));
+
+ qDebug("Working directory: %s", qPrintable(QDir::currentPath()));
+ qDebug("MO at: %s", qPrintable(application.applicationDirPath()));
+ qDebug("user name: %s", getenv("USERNAME"));
QPixmap pixmap(":/MO/gui/splash");
QSplashScreen splash(pixmap);
splash.show();
@@ -282,8 +297,8 @@ int main(int argc, char *argv[])
try {
SingleInstance instance(update);
if (!instance.primaryInstance()) {
- if ((arguments.size() == 2) &&
- isNxmLink(arguments.at(1))) {
+ if ((arguments.size() == 2) && isNxmLink(arguments.at(1))) {
+ qDebug("not primary instance, sending download message");
instance.sendMessage(arguments.at(1));
return 0;
} else if (arguments.size() == 1) {
@@ -294,20 +309,20 @@ int main(int argc, char *argv[])
// TODO: this should be MAX_PATH_UNICODE!
- wchar_t omoPath[MAX_PATH];
- memset(omoPath, 0, sizeof(TCHAR) * MAX_PATH);
- ::GetModuleFileNameW(NULL, omoPath, MAX_PATH);
- wchar_t *lastBSlash = wcsrchr(omoPath, TEXT('\\'));
+ wchar_t moPath[MAX_PATH];
+ memset(moPath, 0, sizeof(TCHAR) * MAX_PATH);
+ ::GetModuleFileNameW(NULL, moPath, MAX_PATH);
+ wchar_t *lastBSlash = wcsrchr(moPath, TEXT('\\'));
if (lastBSlash != NULL) {
*lastBSlash = TEXT('\0');
}
- QSettings settings(ToQString(std::wstring(omoPath).append(L"\\ModOrganizer.ini")), QSettings::IniFormat);
+ QSettings settings(ToQString(std::wstring(moPath).append(L"\\ModOrganizer.ini")), QSettings::IniFormat);
QString gamePath = QString::fromUtf8(settings.value("gamePath", "").toByteArray());
bool done = false;
while (!done) {
- if (!GameInfo::init(omoPath, ToWString(gamePath))) {
+ if (!GameInfo::init(moPath, ToWString(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));
@@ -358,6 +373,9 @@ int main(int argc, char *argv[])
// user selected a folder and game was initialised with it
settings.setValue("gamePath", gamePath.toUtf8().constData());
}
+
+ qDebug("managing game at %s", qPrintable(gamePath));
+
ExecutablesList executablesList;
executablesList.init();
@@ -368,6 +386,8 @@ int main(int argc, char *argv[])
cleanupDir();
+ qDebug("setting up configured executables");
+
int numCustomExecutables = settings.beginReadArray("customExecutables");
for (int i = 0; i < numCustomExecutables; ++i) {
settings.setArrayIndex(i);
@@ -384,14 +404,15 @@ int main(int argc, char *argv[])
settings.endArray();
+ qDebug("initializing tutorials");
TutorialManager::init(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getTutorialDir())).append("/"));
application.setStyleFile(settings.value("Settings/style", "").toString());
+ qDebug("setting up main window");
// set up main window and its data structures
MainWindow mainWindow(argv[0], settings);
QObject::connect(&mainWindow, SIGNAL(styleChanged(QString)), &application, SLOT(setStyleFile(QString)));
-
QObject::connect(&instance, SIGNAL(messageSent(QString)), &mainWindow, SLOT(externalMessage(QString)));
mainWindow.setExecutablesList(executablesList);
@@ -402,16 +423,19 @@ int main(int argc, char *argv[])
{ // see if there is a profile on the command line
int profileIndex = arguments.indexOf("-p", 1);
if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) {
+ qDebug("profile overwritten on command line");
selectedProfileName = arguments.at(profileIndex + 1);
}
arguments.removeAt(profileIndex);
arguments.removeAt(profileIndex);
}
+ qDebug("configured profile: %s", qPrintable(selectedProfileName));
// if we have a command line parameter, it is either a nxm link or
// a binary to start
if ((arguments.size() > 1) && (!isNxmLink(arguments.at(1)))) {
QString exeName = arguments.at(1);
+ qDebug("starting %s from command line", qPrintable(exeName));
arguments.removeFirst(); // remove application name (ModOrganizer.exe)
arguments.removeFirst(); // remove binary name
// pass the remaining parameters to the binary
@@ -431,10 +455,12 @@ int main(int argc, char *argv[])
mainWindow.setCurrentProfile(1);
}
+ qDebug("displaying main window");
mainWindow.show();
if ((arguments.size() > 1) &&
(isNxmLink(arguments.at(1)))) {
+ qDebug("starting download from command line: %s", qPrintable(arguments.at(1)));
mainWindow.externalMessage(arguments.at(1));
}
splash.finish(&mainWindow);
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 5bc339d3..f2f3af65 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -196,14 +196,17 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(ui->savegameList, SIGNAL(itemEntered(QListWidgetItem*)),
this, SLOT(saveSelectionChanged(QListWidgetItem*)));
- connect(&m_PluginList, SIGNAL(esplist_changed()), this, SLOT(esplist_changed()));
+ connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList()));
connect(&m_ModList, SIGNAL(modorder_changed()), this, SLOT(modorder_changed()));
connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, SLOT(removeOrigin(QString)));
connect(&m_ModList, SIGNAL(showMessage(QString)), this, SLOT(showMessage(QString)));
connect(&m_ModList, SIGNAL(modRenamed(QString,QString)), this, SLOT(modRenamed(QString,QString)));
+ connect(m_ModListSortProxy, SIGNAL(filterActive(bool)), this, SLOT(modFilterActive(bool)));
connect(ui->modFilterEdit, SIGNAL(textChanged(QString)), m_ModListSortProxy, SLOT(updateFilter(QString)));
connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), m_PluginListSortProxy, SLOT(updateFilter(QString)));
- connect(&m_ModList, SIGNAL(modlist_changed(int)), m_ModListSortProxy, SLOT(invalidate()));
+ connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString)));
+ connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), m_ModListSortProxy, SLOT(invalidate()));
+ connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), this, SLOT(modlistChanged(QModelIndex, int)));
connect(&m_ModList, SIGNAL(removeSelectedMods()), this, SLOT(removeMod_clicked()));
connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, SLOT(directory_refreshed()));
@@ -212,6 +215,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(&m_Settings, SIGNAL(languageChanged(QString)), this, SLOT(languageChange(QString)));
connect(&m_Settings, SIGNAL(styleChanged(QString)), this, SIGNAL(styleChanged(QString)));
+ connect(this, SIGNAL(styleChanged(QString)), this, SLOT(updateStyle(QString)));
connect(&m_Updater, SIGNAL(restart()), this, SLOT(close()));
connect(&m_Updater, SIGNAL(updateAvailable()), this, SLOT(updateAvailable()));
@@ -252,6 +256,11 @@ MainWindow::~MainWindow()
delete m_GameInfo;
}
+void MainWindow::updateStyle(const QString&)
+{
+ // no effect?
+ ensurePolished();
+}
void MainWindow::resizeEvent(QResizeEvent *event)
{
@@ -503,6 +512,43 @@ void MainWindow::saveArchiveList()
}
}
+void MainWindow::savePluginList()
+{
+ m_PluginList.saveTo(m_CurrentProfile->getPluginsFileName(),
+ m_CurrentProfile->getLoadOrderFileName(),
+ m_CurrentProfile->getLockedOrderFileName(),
+ m_CurrentProfile->getDeleterFileName(),
+ m_Settings.hideUncheckedPlugins());
+ m_PluginList.saveLoadOrder(*m_DirectoryStructure);
+}
+
+void MainWindow::modFilterActive(bool active)
+{
+ if (active) {
+ ui->modList->setStyleSheet("QTreeView { border: 2px ridge #f00; }");
+ } else {
+ ui->modList->setStyleSheet("");
+ }
+}
+
+void MainWindow::espFilterChanged(const QString &filter)
+{
+ if (!filter.isEmpty()) {
+ ui->espList->setStyleSheet("QTreeView { border: 2px ridge #f00; }");
+ } else {
+ ui->espList->setStyleSheet("");
+ }
+}
+
+void MainWindow::downloadFilterChanged(const QString &filter)
+{
+ if (!filter.isEmpty()) {
+ ui->downloadView->setStyleSheet("QTreeView { border: 2px ridge #f00; }");
+ } else {
+ ui->downloadView->setStyleSheet("");
+ }
+}
+
bool MainWindow::saveCurrentLists()
{
if (m_DirectoryUpdate) {
@@ -511,19 +557,8 @@ bool MainWindow::saveCurrentLists()
}
// save plugin list
-
try {
- m_PluginList.saveTo(m_CurrentProfile->getPluginsFileName(),
- m_CurrentProfile->getLoadOrderFileName(),
- m_CurrentProfile->getLockedOrderFileName(),
- m_CurrentProfile->getDeleterFileName(),
- m_Settings.hideUncheckedPlugins());
-
- if (!m_PluginList.saveLoadOrder(*m_DirectoryStructure)) {
- MessageDialog::showMessage(tr("load order could not be saved"), this);
- } else {
- ui->btnSave->setEnabled(false);
- }
+ savePluginList();
} catch (const std::exception &e) {
reportError(tr("failed to save load order: %1").arg(e.what()));
}
@@ -644,6 +679,7 @@ void MainWindow::closeEvent(QCloseEvent* event)
void MainWindow::createFirstProfile()
{
if (!refreshProfiles(false)) {
+ qDebug("creating default profile");
Profile newProf("Default", false);
refreshProfiles(false);
}
@@ -895,6 +931,11 @@ QString MainWindow::profilePath() const
return m_CurrentProfile->getPath();
}
+QString MainWindow::downloadsPath() const
+{
+ return QDir::fromNativeSeparators(m_Settings.getDownloadDirectory());
+}
+
VersionInfo MainWindow::appVersion() const
{
return m_Updater.getVersion();
@@ -1404,8 +1445,6 @@ void MainWindow::refreshESPList()
m_CurrentProfile->getLoadOrderFileName(),
m_CurrentProfile->getLockedOrderFileName());
//m_PluginList.readFrom(m_CurrentProfile->getPluginsFileName());
-
- findChild<QPushButton*>("btnSave")->setEnabled(false);
}
@@ -1432,6 +1471,11 @@ void MainWindow::refreshBSAList()
if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
L"", buffer, 256, iniFileName.c_str()) != 0) {
m_DefaultArchives = ToQString(buffer).split(',');
+ } else {
+ std::vector<std::wstring> vanillaBSAs = GameInfo::instance().getVanillaBSAs();
+ for (auto iter = vanillaBSAs.begin(); iter != vanillaBSAs.end(); ++iter) {
+ m_DefaultArchives.append(ToQString(*iter));
+ }
}
if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().append(L"2").c_str(),
@@ -1651,7 +1695,6 @@ void MainWindow::on_btnRefreshData_clicked()
void MainWindow::on_tabWidget_currentChanged(int index)
{
- saveCurrentLists();
if (index == 0) {
refreshESPList();
} else if (index == 1) {
@@ -1721,12 +1764,6 @@ void MainWindow::installMod()
}
-void MainWindow::on_btnSave_clicked()
-{
- saveCurrentLists();
-}
-
-
void MainWindow::on_startButton_clicked()
{
QComboBox* executablesList = findChild<QComboBox*>("executablesListBox");
@@ -1982,12 +2019,6 @@ bool MainWindow::setCurrentProfile(const QString &name)
}
-void MainWindow::esplist_changed()
-{
- findChild<QPushButton*>("btnSave")->setEnabled(true);
-}
-
-
void MainWindow::refresher_progress(int percent)
{
m_RefreshProgress->setValue(percent);
@@ -2220,6 +2251,8 @@ void MainWindow::addCategoryFilters(QTreeWidgetItem *root, const std::set<int> &
void MainWindow::refreshFilters()
{
+ QItemSelection currentSelection = ui->modList->selectionModel()->selection();
+
ui->modList->setCurrentIndex(QModelIndex());
// save previous filter text so we can restore it later, in case the filter still exists then
@@ -2258,6 +2291,8 @@ void MainWindow::refreshFilters()
currentItem = currentItem->parent();
}
}
+
+ ui->modList->selectionModel()->select(currentSelection, QItemSelectionModel::Select);
}
@@ -2284,7 +2319,7 @@ void MainWindow::restoreBackup_clicked()
(QMessageBox::question(this, tr("Overwrite?"),
tr("This will replace the existing mod \"%1\". Continue?").arg(regName),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) {
- if (modDir.exists(regName) && !removeDir(modDir.absoluteFilePath(regName))) {
+ if (modDir.exists(regName) && !shellDelete(QStringList(modDir.absoluteFilePath(regName)), NULL)) {
reportError(tr("failed to remove mod \"%1\"").arg(regName));
} else {
QString destinationPath = QDir::fromNativeSeparators(m_Settings.getModDirectory()) + "/" + regName;
@@ -2298,6 +2333,29 @@ void MainWindow::restoreBackup_clicked()
}
+void MainWindow::modlistChanged(const QModelIndex &index, int role)
+{
+ if (role == Qt::CheckStateRole) {
+ if (index.data(Qt::CheckStateRole).toBool()) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(index.row());
+
+ QDir dir(modInfo->absolutePath());
+ foreach (const QString &esm, dir.entryList(QStringList("*.esm"), QDir::Files)) {
+ m_PluginList.enableESP(esm);
+ }
+
+ QStringList esps = dir.entryList(QStringList("*.esp"), QDir::Files);
+ foreach (const QString &esp, esps) {
+ m_PluginList.enableESP(esp);
+ }
+ if (esps.count() > 1) {
+ MessageDialog::showMessage(tr("Multiple esps activated, please check that they don't conflict."), this);
+ }
+ }
+ }
+}
+
+
void MainWindow::removeMod_clicked()
{
try {
@@ -2370,6 +2428,11 @@ void MainWindow::endorse_clicked()
endorseMod(ModInfo::getByIndex(m_ContextRow));
}
+void MainWindow::dontendorse_clicked()
+{
+ ModInfo::getByIndex(m_ContextRow)->setNeverEndorse();
+}
+
void MainWindow::unendorse_clicked()
{
@@ -2397,13 +2460,14 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index,
ModInfoDialog dialog(modInfo, m_DirectoryStructure, this);
connect(&dialog, SIGNAL(nexusLinkActivated(QString)), this, SLOT(nexusLinkActivated(QString)));
connect(&dialog, SIGNAL(downloadRequest(QString)), this, SLOT(downloadRequestedNXM(QString)));
- connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)));
+ connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection);
+ connect(&dialog, SIGNAL(modOpenNext()), this, SLOT(modOpenNext()), Qt::QueuedConnection);
+ connect(&dialog, SIGNAL(modOpenPrev()), this, SLOT(modOpenPrev()), Qt::QueuedConnection);
connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int)));
connect(&dialog, SIGNAL(endorseMod(ModInfo::Ptr)), this, SLOT(endorseMod(ModInfo::Ptr)));
dialog.openTab(tab);
dialog.exec();
-
modInfo->saveMeta();
emit modInfoDisplayed();
}
@@ -2429,6 +2493,43 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index,
}
+void MainWindow::modOpenNext()
+{
+ QModelIndex index = m_ModListSortProxy->mapFromSource(m_ModList.index(m_ContextRow, 0));
+ index = m_ModListSortProxy->index((index.row() + 1) % m_ModListSortProxy->rowCount(), 0);
+
+ m_ContextRow = m_ModListSortProxy->mapToSource(index).row();
+ ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow);
+ std::vector<ModInfo::EFlag> flags = mod->getFlags();
+ if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) ||
+ (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end())) {
+ // skip overwrite and backups
+ modOpenNext();
+ } else {
+ displayModInformation(m_ContextRow);
+ }
+}
+
+void MainWindow::modOpenPrev()
+{
+ QModelIndex index = m_ModListSortProxy->mapFromSource(m_ModList.index(m_ContextRow, 0));
+ int row = index.row() - 1;
+ if (row == -1) {
+ row = m_ModListSortProxy->rowCount() - 1;
+ }
+
+ m_ContextRow = m_ModListSortProxy->mapToSource(m_ModListSortProxy->index(row, 0)).row();
+ ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow);
+ std::vector<ModInfo::EFlag> flags = mod->getFlags();
+ if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) ||
+ (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end())) {
+ // skip overwrite and backups
+ modOpenPrev();
+ } else {
+ displayModInformation(m_ContextRow);
+ }
+}
+
void MainWindow::displayModInformation(const QString &modName, int tab)
{
unsigned int index = ModInfo::getIndex(modName);
@@ -2537,31 +2638,6 @@ void MainWindow::syncOverwrite()
}
-void MainWindow::setPriorityMax()
-{
- int newPriority = m_CurrentProfile->numMods() - 1;
- m_CurrentProfile->setModPriority(m_ContextRow, newPriority);
-}
-
-
-void MainWindow::setPriorityManually()
-{
-// m_CurrentProfile->setModPriority(m_ContextRow, m_CurrentProfile->numMods() - 1);
-
- int current = m_CurrentProfile->getModPriority(m_ContextRow);
- int newPriority = QInputDialog::getInt(this, tr("Priority"), tr("Choose Priority"), current, 0, m_ModList.rowCount() - 1);
- m_ModList.changeModPriority(m_ContextRow, newPriority);
-// m_CurrentProfile->setModPriority(m_ContextRow, newPriority);
-}
-
-
-void MainWindow::setPriorityMin()
-{
- int newPriority = 0;
- m_CurrentProfile->setModPriority(m_ContextRow, newPriority);
-}
-
-
void MainWindow::cancelModListEditor()
{
ui->modList->setEnabled(false);
@@ -2572,6 +2648,7 @@ void MainWindow::cancelModListEditor()
void MainWindow::on_modList_doubleClicked(const QModelIndex &index)
{
try {
+ m_ContextRow = m_ModListSortProxy->mapToSource(index).row();
displayModInformation(m_ModListSortProxy->mapToSource(index).row());
// workaround to cancel the editor that might have opened because of
// selection-click
@@ -2648,12 +2725,21 @@ void MainWindow::saveCategories()
return;
}
// m_ModList.resetCategories(m_ContextRow);
- saveCategoriesFromMenu(menu, m_ContextRow);
+
+ QModelIndexList selected = ui->modList->selectionModel()->selectedRows();
+ if (selected.size() > 0) {
+ for (int i = 0; i < selected.size(); ++i) {
+ saveCategoriesFromMenu(menu, m_ModListSortProxy->mapToSource(selected.at(i)).row());
+ }
+ } else {
+ saveCategoriesFromMenu(menu, m_ContextRow);
+ }
refreshFilters();
}
+
void MainWindow::savePrimaryCategory()
{
QMenu *menu = qobject_cast<QMenu*>(sender());
@@ -2662,13 +2748,16 @@ void MainWindow::savePrimaryCategory()
return;
}
- ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow);
foreach (QAction* action, menu->actions()) {
QWidgetAction *widgetAction = qobject_cast<QWidgetAction*>(action);
if (widgetAction != NULL) {
QRadioButton *btn = qobject_cast<QRadioButton*>(widgetAction->defaultWidget());
if (btn->isChecked()) {
- modInfo->setPrimaryCategory(widgetAction->data().toInt());
+ QModelIndexList selected = ui->modList->selectionModel()->selectedRows();
+ for (int i = 0; i < selected.size(); ++i) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ModListSortProxy->mapToSource(selected.at(i)).row());
+ modInfo->setPrimaryCategory(widgetAction->data().toInt());
+ }
break;
}
}
@@ -2847,6 +2936,10 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
} break;
case ModInfo::ENDORSED_FALSE: {
menu.addAction(tr("Endorse"), this, SLOT(endorse_clicked()));
+ menu.addAction(tr("Don't endorse"), this, SLOT(dontendorse_clicked()));
+ } break;
+ case ModInfo::ENDORSED_NEVER: {
+ menu.addAction(tr("Endorse"), this, SLOT(endorse_clicked()));
} break;
default: {
QAction *action = new QAction(tr("Endorsement state unknown"), &menu);
@@ -2875,7 +2968,8 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
void MainWindow::on_categoriesList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *)
{
if (current != NULL) {
- m_ModListSortProxy->setCategoryFilter(current->data(0, Qt::UserRole).toInt());
+ int filter = current->data(0, Qt::UserRole).toInt();
+ m_ModListSortProxy->setCategoryFilter(filter);
ui->currentCategoryLabel->setText(QString("(%1)").arg(current->text(0)));
}
}
@@ -2986,7 +3080,6 @@ void MainWindow::linkToolbar()
void MainWindow::linkDesktop()
{
QComboBox* executablesList = findChild<QComboBox*>("executablesListBox");
-// QPushButton *linkButton = findChild<QPushButton*>("linkDesktopButton");
const Executable &selectedExecutable = executablesList->itemData(executablesList->currentIndex()).value<Executable>();
QString linkName = getDesktopDirectory() + "\\" + selectedExecutable.m_Title + ".lnk";
@@ -3131,7 +3224,7 @@ void MainWindow::downloadRequestedNXM(const QString &url)
void MainWindow::downloadRequested(QNetworkReply *reply, int modID, const QString &fileName)
{
try {
- if (m_DownloadManager.addDownload(reply, fileName, modID)) {
+ if (m_DownloadManager.addDownload(reply, QStringList(), fileName, modID)) {
MessageDialog::showMessage(tr("Download started"), this);
}
} catch (const std::exception &e) {
@@ -3228,7 +3321,6 @@ void MainWindow::installDownload(int index)
} else {
reportError(tr("mod \"%1\" not found").arg(modName));
}
-
m_DownloadManager.markInstalled(index);
emit modInstalled();
@@ -3558,6 +3650,7 @@ void MainWindow::updateDownloadListDelegate()
DownloadListSortProxy *sortProxy = new DownloadListSortProxy(&m_DownloadManager, ui->downloadView);
sortProxy->setSourceModel(new DownloadList(&m_DownloadManager, ui->downloadView));
connect(ui->downloadFilterEdit, SIGNAL(textChanged(QString)), sortProxy, SLOT(updateFilter(QString)));
+ connect(ui->downloadFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(downloadFilterChanged(QString)));
ui->downloadView->setModel(sortProxy);
ui->downloadView->sortByColumn(1, Qt::AscendingOrder);
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 89e29685..1e6eef44 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -94,6 +94,7 @@ public:
virtual MOBase::IGameInfo &gameInfo() const;
virtual QString profileName() const;
virtual QString profilePath() const;
+ virtual QString downloadsPath() const;
virtual MOBase::VersionInfo appVersion() const;
virtual MOBase::IModInterface *getMod(const QString &name);
virtual MOBase::IModInterface *createMod(const QString &name);
@@ -104,13 +105,13 @@ public:
void addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info);
void saveArchiveList();
+
public slots:
void displayColumnSelection(const QPoint &pos);
void externalMessage(const QString &message);
void modorder_changed();
- void esplist_changed();
void refresher_progress(int percent);
void directory_refreshed();
@@ -171,7 +172,6 @@ private:
void installMod(const QString &fileName);
void installMod();
bool modifyExecutablesDialog();
- void modOpenNext();
void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab);
void displayModInformation(int row, int tab = 0);
void testExtractBSA(int modIndex);
@@ -305,6 +305,7 @@ private slots:
void removeMod_clicked();
void reinstallMod_clicked();
void endorse_clicked();
+ void dontendorse_clicked();
void unendorse_clicked();
void visitOnNexus_clicked();
void openExplorer_clicked();
@@ -336,10 +337,6 @@ private slots:
void removeOrigin(const QString &name);
- void setPriorityMax();
- void setPriorityManually();
- void setPriorityMin();
-
void procError(QProcess::ProcessError error);
void procFinished(int exitCode, QProcess::ExitStatus exitStatus);
@@ -376,6 +373,8 @@ private slots:
void editCategories();
void displayModInformation(const QString &modName, int tab);
+ void modOpenNext();
+ void modOpenPrev();
void modRenamed(const QString &oldName, const QString &newName);
@@ -396,6 +395,15 @@ private slots:
void startExeAction();
void checkBSAList();
+ void updateStyle(const QString &style);
+
+ void modlistChanged(const QModelIndex &index, int role);
+
+ void savePluginList();
+
+ void modFilterActive(bool active);
+ void espFilterChanged(const QString &filter);
+ void downloadFilterChanged(const QString &filter);
private slots: // ui slots
// actions
@@ -411,7 +419,6 @@ private slots: // ui slots
void on_bsaList_customContextMenuRequested(const QPoint &pos);
void on_bsaList_itemChanged(QTreeWidgetItem *item, int column);
void on_btnRefreshData_clicked();
- void on_btnSave_clicked();
void on_categoriesList_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_categoriesList_customContextMenuRequested(const QPoint &pos);
void on_compactBox_toggled(bool checked);
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
index 71f0e3c6..74f8c686 100644
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -99,6 +99,9 @@
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>2</number>
+ </property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
@@ -158,173 +161,164 @@ p, li { white-space: pre-wrap; }
</layout>
</item>
<item>
- <widget class="QGroupBox" name="groupBox">
- <property name="title">
- <string>Installed Mods</string>
+ <widget class="QTreeView" name="modList">
+ <property name="minimumSize">
+ <size>
+ <width>330</width>
+ <height>400</height>
+ </size>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_11">
- <property name="margin">
- <number>3</number>
- </property>
- <item>
- <widget class="QTreeView" name="modList">
- <property name="minimumSize">
- <size>
- <width>330</width>
- <height>400</height>
- </size>
- </property>
- <property name="palette">
- <palette>
- <active>
- <colorrole role="ButtonText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>64</red>
- <green>64</green>
- <blue>64</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Link">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>255</red>
- <green>0</green>
- <blue>0</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="LinkVisited">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>0</red>
- <green>170</green>
- <blue>0</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="ButtonText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>64</red>
- <green>64</green>
- <blue>64</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Link">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>255</red>
- <green>0</green>
- <blue>0</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="LinkVisited">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>0</red>
- <green>170</green>
- <blue>0</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="ButtonText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>120</red>
- <green>120</green>
- <blue>120</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="Link">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>255</red>
- <green>0</green>
- <blue>0</blue>
- </color>
- </brush>
- </colorrole>
- <colorrole role="LinkVisited">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>0</red>
- <green>170</green>
- <blue>0</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="contextMenuPolicy">
- <enum>Qt::CustomContextMenu</enum>
- </property>
- <property name="toolTip">
- <string>List of available mods.</string>
- </property>
- <property name="whatsThis">
- <string>This is a list of installed mods. Use the checkboxes to activate/deactivate mods and drag &amp; drop mods to change their &quot;installation&quot; orders.</string>
- </property>
- <property name="editTriggers">
- <set>QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
- </property>
- <property name="showDropIndicator" stdset="0">
- <bool>true</bool>
- </property>
- <property name="dragEnabled">
- <bool>true</bool>
- </property>
- <property name="dragDropOverwriteMode">
- <bool>false</bool>
- </property>
- <property name="dragDropMode">
- <enum>QAbstractItemView::InternalMove</enum>
- </property>
- <property name="defaultDropAction">
- <enum>Qt::MoveAction</enum>
- </property>
- <property name="alternatingRowColors">
- <bool>true</bool>
- </property>
- <property name="selectionMode">
- <enum>QAbstractItemView::ExtendedSelection</enum>
- </property>
- <property name="selectionBehavior">
- <enum>QAbstractItemView::SelectRows</enum>
- </property>
- <property name="indentation">
- <number>0</number>
- </property>
- <property name="itemsExpandable">
- <bool>false</bool>
- </property>
- <property name="sortingEnabled">
- <bool>true</bool>
- </property>
- <property name="expandsOnDoubleClick">
- <bool>false</bool>
- </property>
- <attribute name="headerDefaultSectionSize">
- <number>10</number>
- </attribute>
- <attribute name="headerShowSortIndicator" stdset="0">
- <bool>true</bool>
- </attribute>
- <attribute name="headerStretchLastSection">
- <bool>false</bool>
- </attribute>
- </widget>
- </item>
- </layout>
+ <property name="palette">
+ <palette>
+ <active>
+ <colorrole role="ButtonText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>64</red>
+ <green>64</green>
+ <blue>64</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Link">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="LinkVisited">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>170</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ </active>
+ <inactive>
+ <colorrole role="ButtonText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>64</red>
+ <green>64</green>
+ <blue>64</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Link">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="LinkVisited">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>170</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ </inactive>
+ <disabled>
+ <colorrole role="ButtonText">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>120</red>
+ <green>120</green>
+ <blue>120</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="Link">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ <colorrole role="LinkVisited">
+ <brush brushstyle="SolidPattern">
+ <color alpha="255">
+ <red>0</red>
+ <green>170</green>
+ <blue>0</blue>
+ </color>
+ </brush>
+ </colorrole>
+ </disabled>
+ </palette>
+ </property>
+ <property name="contextMenuPolicy">
+ <enum>Qt::CustomContextMenu</enum>
+ </property>
+ <property name="toolTip">
+ <string>List of available mods.</string>
+ </property>
+ <property name="whatsThis">
+ <string>This is a list of installed mods. Use the checkboxes to activate/deactivate mods and drag &amp; drop mods to change their &quot;installation&quot; orders.</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="editTriggers">
+ <set>QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked</set>
+ </property>
+ <property name="showDropIndicator" stdset="0">
+ <bool>true</bool>
+ </property>
+ <property name="dragEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="dragDropOverwriteMode">
+ <bool>false</bool>
+ </property>
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::InternalMove</enum>
+ </property>
+ <property name="defaultDropAction">
+ <enum>Qt::MoveAction</enum>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::ExtendedSelection</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="indentation">
+ <number>0</number>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="expandsOnDoubleClick">
+ <bool>false</bool>
+ </property>
+ <attribute name="headerDefaultSectionSize">
+ <number>10</number>
+ </attribute>
+ <attribute name="headerShowSortIndicator" stdset="0">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="headerStretchLastSection">
+ <bool>false</bool>
+ </attribute>
</widget>
</item>
<item>
@@ -391,170 +385,155 @@ p, li { white-space: pre-wrap; }
<widget class="QWidget" name="layoutWidget_2">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
- <widget class="QGroupBox" name="startGroup">
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="title">
- <string>Start</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_6">
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_5" stretch="1,0">
- <item>
- <widget class="QComboBox" name="executablesListBox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>40</height>
- </size>
- </property>
- <property name="font">
- <font>
- <pointsize>9</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="toolTip">
- <string>Pick a program to run.</string>
- </property>
- <property name="whatsThis">
- <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+ <layout class="QHBoxLayout" name="horizontalLayout_5" stretch="1,0">
+ <item>
+ <widget class="QComboBox" name="executablesListBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>40</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>9</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="toolTip">
+ <string>Pick a program to run.</string>
+ </property>
+ <property name="whatsThis">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Choose the program to run. Once you start using ModOrganizer, you should always run your game and tools from here or through shortcuts created here, otherwise mods installed through MO will not be visible.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You can add new Tools to this list, but I can't promise tools I haven't tested will work.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="iconSize">
- <size>
- <width>32</width>
- <height>32</height>
- </size>
- </property>
- <property name="frame">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QVBoxLayout" name="verticalLayout_12" stretch="0,0">
- <item>
- <widget class="QPushButton" name="startButton">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>120</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="font">
- <font>
- <pointsize>10</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="toolTip">
- <string>Run program</string>
- </property>
- <property name="whatsThis">
- <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>32</width>
+ <height>32</height>
+ </size>
+ </property>
+ <property name="frame">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_12" stretch="0,0">
+ <item>
+ <widget class="QPushButton" name="startButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>120</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="toolTip">
+ <string>Run program</string>
+ </property>
+ <property name="whatsThis">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Run the selected program with ModOrganizer enabled.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="locale">
- <locale language="English" country="UnitedStates"/>
- </property>
- <property name="text">
- <string>Run</string>
- </property>
- <property name="icon">
- <iconset resource="resources.qrc">
- <normaloff>:/MO/gui/run</normaloff>:/MO/gui/run</iconset>
- </property>
- <property name="iconSize">
- <size>
- <width>36</width>
- <height>36</height>
- </size>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="linkButton">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>140</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="toolTip">
- <string>Create a shortcut in your start menu or on the desktop to the specified program</string>
- </property>
- <property name="whatsThis">
- <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+ </property>
+ <property name="locale">
+ <locale language="English" country="UnitedStates"/>
+ </property>
+ <property name="text">
+ <string>Run</string>
+ </property>
+ <property name="icon">
+ <iconset resource="resources.qrc">
+ <normaloff>:/MO/gui/run</normaloff>:/MO/gui/run</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>36</width>
+ <height>36</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="linkButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>140</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Create a shortcut in your start menu or on the desktop to the specified program</string>
+ </property>
+ <property name="whatsThis">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;This creates a start menu shortcut that directly starts the selected program with the MO active.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="text">
- <string>Shortcut</string>
- </property>
- <property name="icon">
- <iconset resource="resources.qrc">
- <normaloff>:/MO/gui/link</normaloff>:/MO/gui/link</iconset>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
+ </property>
+ <property name="text">
+ <string>Shortcut</string>
+ </property>
+ <property name="icon">
+ <iconset resource="resources.qrc">
+ <normaloff>:/MO/gui/link</normaloff>:/MO/gui/link</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
@@ -593,7 +572,7 @@ p, li { white-space: pre-wrap; }
</size>
</property>
<attribute name="title">
- <string notr="true">ESPs</string>
+ <string notr="true">Plugins</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
@@ -609,34 +588,6 @@ p, li { white-space: pre-wrap; }
<number>0</number>
</property>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <widget class="QPushButton" name="btnSave">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="toolTip">
- <string>save esp list and load order.</string>
- </property>
- <property name="whatsThis">
- <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
-&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
-p, li { white-space: pre-wrap; }
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Save the list of active mods and load order. This automatically happens if you close MO or start a program.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="text">
- <string>Save</string>
- </property>
- <property name="icon">
- <iconset resource="resources.qrc">
- <normaloff>:/MO/gui/resources/document-save.png</normaloff>:/MO/gui/resources/document-save.png</iconset>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
<widget class="QTreeView" name="espList">
<property name="minimumSize">
<size>
@@ -743,7 +694,7 @@ p, li { white-space: pre-wrap; }
</widget>
<widget class="QWidget" name="bsaTab">
<attribute name="title">
- <string notr="true">BSAs</string>
+ <string notr="true">Archives</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_9">
<property name="margin">
@@ -927,13 +878,6 @@ p, li { white-space: pre-wrap; }
<item>
<layout class="QVBoxLayout" name="downloadLayout">
<item>
- <widget class="MOBase::LineEditClear" name="downloadFilterEdit">
- <property name="placeholderText">
- <string>Filter</string>
- </property>
- </widget>
- </item>
- <item>
<widget class="QTreeView" name="downloadView">
<property name="minimumSize">
<size>
@@ -991,7 +935,7 @@ p, li { white-space: pre-wrap; }
</layout>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout" stretch="1">
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,2">
<item>
<widget class="QCheckBox" name="compactBox">
<property name="text">
@@ -999,6 +943,13 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
+ <item>
+ <widget class="MOBase::LineEditClear" name="downloadFilterEdit">
+ <property name="placeholderText">
+ <string>Namefilter</string>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
</layout>
diff --git a/src/messagedialog.cpp b/src/messagedialog.cpp
index af89ee8d..4a1ef0d6 100644
--- a/src/messagedialog.cpp
+++ b/src/messagedialog.cpp
@@ -17,54 +17,73 @@ 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 "messagedialog.h"
-#include "ui_messagedialog.h"
-#include <QTimer>
-#include <QResizeEvent>
-#include <Windows.h>
-
-MessageDialog::MessageDialog(const QString &text, QWidget *reference) :
- QDialog(reference),
- ui(new Ui::MessageDialog)
-{
- ui->setupUi(this);
- findChild<QLabel*>("message")->setText(text);
- this->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
- this->setFocusPolicy(Qt::NoFocus);
- this->setAttribute(Qt::WA_ShowWithoutActivating);
- QTimer::singleShot(1000 + (text.length() * 40), this, SLOT(hide()));
- if (reference != NULL) {
- QPoint position = reference->mapToGlobal(QPoint(reference->width() / 2, reference->height()));
- position.rx() -= this->width() / 2;
- position.ry() -= this->height() + 5;
- move(position);
- }
-}
-
-
-MessageDialog::~MessageDialog()
-{
- delete ui;
-}
-
-
-void MessageDialog::resizeEvent(QResizeEvent *event)
-{
- QWidget *par = parentWidget();
- if (par != NULL) {
- QPoint position = par->mapToGlobal(QPoint(par->width() / 2, par->height()));
- position.rx() -= event->size().width() / 2;
- position.ry() -= event->size().height() + 5;
- move(position);
- }
-}
-
-
-void MessageDialog::showMessage(const QString &text, QWidget *reference)
-{
- if (reference != NULL) {
- MessageDialog *dialog = new MessageDialog(text, reference);
- dialog->show();
- reference->activateWindow();
- }
-}
+#include "messagedialog.h"
+#include "ui_messagedialog.h"
+#include <QTimer>
+#include <QResizeEvent>
+#include <Windows.h>
+
+MessageDialog::MessageDialog(const QString &text, QWidget *reference) :
+ QDialog(reference),
+ ui(new Ui::MessageDialog)
+{
+ ui->setupUi(this);
+
+ // very crude way to ensure no single word in the test is wider than the message window. ellide in the center if necessary
+ QFontMetrics metrics(ui->message->font());
+ QString restrictedText;
+ QStringList lines = text.split("\n");
+ foreach (const QString &line, lines) {
+ QString newLine;
+ QStringList words = line.split(" ");
+ foreach (const QString &word, words) {
+ if (word.length() > 10) {
+ newLine += "<span style=\"nobreak\">" + metrics.elidedText(word, Qt::ElideMiddle, ui->message->maximumWidth()) + "</span>";
+ } else {
+ newLine += word;
+ }
+ newLine += " ";
+ }
+ restrictedText += newLine + "\n";
+ }
+
+ ui->message->setText(restrictedText);
+ this->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
+ this->setFocusPolicy(Qt::NoFocus);
+ this->setAttribute(Qt::WA_ShowWithoutActivating);
+ QTimer::singleShot(1000 + (text.length() * 40), this, SLOT(hide()));
+ if (reference != NULL) {
+ QPoint position = reference->mapToGlobal(QPoint(reference->width() / 2, reference->height()));
+ position.rx() -= this->width() / 2;
+ position.ry() -= this->height() + 5;
+ move(position);
+ }
+}
+
+
+MessageDialog::~MessageDialog()
+{
+ delete ui;
+}
+
+
+void MessageDialog::resizeEvent(QResizeEvent *event)
+{
+ QWidget *par = parentWidget();
+ if (par != NULL) {
+ QPoint position = par->mapToGlobal(QPoint(par->width() / 2, par->height()));
+ position.rx() -= event->size().width() / 2;
+ position.ry() -= event->size().height() + 5;
+ move(position);
+ }
+}
+
+
+void MessageDialog::showMessage(const QString &text, QWidget *reference)
+{
+ if (reference != NULL) {
+ MessageDialog *dialog = new MessageDialog(text, reference);
+ dialog->show();
+ reference->activateWindow();
+ }
+}
diff --git a/src/messagedialog.ui b/src/messagedialog.ui
index 9e0f145e..469888ff 100644
--- a/src/messagedialog.ui
+++ b/src/messagedialog.ui
@@ -179,6 +179,9 @@
<property name="text">
<string>Placeholder</string>
</property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
@@ -186,7 +189,10 @@
<bool>true</bool>
</property>
<property name="margin">
- <number>0</number>
+ <number>2</number>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
</property>
</widget>
</item>
diff --git a/src/modinfo.cpp b/src/modinfo.cpp
index 80f27ccf..aebab059 100644
--- a/src/modinfo.cpp
+++ b/src/modinfo.cpp
@@ -345,7 +345,16 @@ ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStruc
m_NexusDescription = metaFile.value("nexusDescription", "").toString();
m_LastNexusQuery = QDateTime::fromString(metaFile.value("lastNexusQuery", "").toString(), Qt::ISODate);
if (metaFile.contains("endorsed")) {
- m_EndorsedState = metaFile.value("endorsed", false).toBool() ? ENDORSED_TRUE : ENDORSED_FALSE;
+ if (metaFile.value("endorsed").canConvert<int>()) {
+ switch (metaFile.value("endorsed").toInt()) {
+ case ENDORSED_FALSE: m_EndorsedState = ENDORSED_FALSE; break;
+ case ENDORSED_TRUE: m_EndorsedState = ENDORSED_TRUE; break;
+ case ENDORSED_NEVER: m_EndorsedState = ENDORSED_NEVER; break;
+ default: m_EndorsedState = ENDORSED_UNKNOWN; break;
+ }
+ } else {
+ m_EndorsedState = metaFile.value("endorsed", false).toBool() ? ENDORSED_TRUE : ENDORSED_FALSE;
+ }
}
int numNexusFiles = metaFile.beginReadArray("nexusFiles");
@@ -411,9 +420,10 @@ void ModInfoRegular::saveMeta()
metaFile.setValue("nexusDescription", m_NexusDescription);
metaFile.setValue("lastNexusQuery", m_LastNexusQuery.toString(Qt::ISODate));
if (m_EndorsedState != ENDORSED_UNKNOWN) {
- metaFile.setValue("endorsed", m_EndorsedState == ENDORSED_TRUE ? true : false);
+ metaFile.setValue("endorsed", m_EndorsedState);
}
+
metaFile.beginWriteArray("nexusFiles");
for (int i = 0; i < m_NexusFileInfos.size(); ++i) {
metaFile.setArrayIndex(i);
@@ -442,7 +452,9 @@ void ModInfoRegular::nxmDescriptionAvailable(int, QVariant, QVariant resultData)
QVariantMap result = resultData.toMap();
m_NewestVersion.parse(result["version"].toString());
m_NexusDescription = result["description"].toString();
- m_EndorsedState = result["voted_by_user"].toBool() ? ENDORSED_TRUE : ENDORSED_FALSE;
+ if (m_EndorsedState != ENDORSED_NEVER) {
+ m_EndorsedState = result["voted_by_user"].toBool() ? ENDORSED_TRUE : ENDORSED_FALSE;
+ }
m_NexusBridge.requestFiles(m_NexusID, QVariant());
}
@@ -593,7 +605,16 @@ void ModInfoRegular::setNexusDescription(const QString &description)
void ModInfoRegular::setIsEndorsed(bool endorsed)
{
- m_EndorsedState = endorsed ? ENDORSED_TRUE : ENDORSED_FALSE;
+ if (m_EndorsedState != ENDORSED_NEVER) {
+ m_EndorsedState = endorsed ? ENDORSED_TRUE : ENDORSED_FALSE;
+ m_MetaInfoChanged = true;
+ }
+}
+
+
+void ModInfoRegular::setNeverEndorse()
+{
+ m_EndorsedState = ENDORSED_NEVER;
m_MetaInfoChanged = true;
}
@@ -601,7 +622,7 @@ void ModInfoRegular::setIsEndorsed(bool endorsed)
bool ModInfoRegular::remove()
{
m_MetaInfoChanged = false;
- return removeDir(absolutePath());
+ return shellDelete(QStringList(absolutePath()), NULL);
}
void ModInfoRegular::endorse(bool doEndorse)
diff --git a/src/modinfo.h b/src/modinfo.h
index a944a6f6..4c5cfd50 100644
--- a/src/modinfo.h
+++ b/src/modinfo.h
@@ -76,7 +76,8 @@ public:
enum EEndorsedState {
ENDORSED_FALSE,
ENDORSED_TRUE,
- ENDORSED_UNKNOWN
+ ENDORSED_UNKNOWN,
+ ENDORSED_NEVER
};
struct NexusFileInfo {
@@ -268,6 +269,11 @@ public:
virtual void setIsEndorsed(bool endorsed) = 0;
/**
+ * set the mod to "i don't intend to endorse". The mod will not show as unendorsed but can still be endorsed
+ */
+ virtual void setNeverEndorse() = 0;
+
+ /**
* @brief delete the mod from the disc. This does not update the global ModInfo structure or indices
* @return true if the mod was successfully removed
**/
@@ -415,7 +421,7 @@ public:
/**
* @return true if the file has been endorsed on nexus
*/
- virtual EEndorsedState endorsedState() const { return ENDORSED_UNKNOWN; }
+ virtual EEndorsedState endorsedState() const { return ENDORSED_NEVER; }
/**
* @brief updates the valid-flag for this mod
@@ -581,6 +587,11 @@ public:
virtual void setIsEndorsed(bool endorsed);
/**
+ * set the mod to "i don't intend to endorse". The mod will not show as unendorsed but can still be endorsed
+ */
+ virtual void setNeverEndorse();
+
+ /**
* @brief delete the mod from the disc. This does not update the global ModInfo structure or indices
* @return true if the mod was successfully removed
**/
@@ -800,6 +811,7 @@ public:
virtual void setNewestVersion(const MOBase::VersionInfo&) {}
virtual void setNexusDescription(const QString&) {}
virtual void setIsEndorsed(bool) {}
+ virtual void setNeverEndorse() {}
virtual bool remove() { return false; }
virtual void endorse(bool) {}
virtual QString name() const { return tr("Overwrite"); }
diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp
index df42cc11..5d6f4a2b 100644
--- a/src/modinfodialog.cpp
+++ b/src/modinfodialog.cpp
@@ -154,7 +154,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo
activateNexusTab();
}
- ui->endorseBtn->setEnabled(m_ModInfo->endorsedState() == ModInfo::ENDORSED_FALSE);
+ ui->endorseBtn->setEnabled((m_ModInfo->endorsedState() == ModInfo::ENDORSED_FALSE) ||
+ (m_ModInfo->endorsedState() == ModInfo::ENDORSED_NEVER));
}
@@ -704,15 +705,17 @@ QString ModInfoDialog::getFileCategory(int categoryID)
void ModInfoDialog::updateVersionColor()
{
- QPalette versionColor;
+// QPalette versionColor;
if (m_ModInfo->getVersion() < m_ModInfo->getNewestVersion()) {
- versionColor.setColor(QPalette::Text, Qt::red);
+ ui->versionEdit->setStyleSheet("color: red");
+// versionColor.setColor(QPalette::Text, Qt::red);
ui->versionEdit->setToolTip(tr("Current Version: %1").arg(m_ModInfo->getNewestVersion().canonicalString()));
} else {
- versionColor.setColor(QPalette::Text, Qt::green);
+ ui->versionEdit->setStyleSheet("color: green");
+// versionColor.setColor(QPalette::Text, Qt::green);
ui->versionEdit->setToolTip(tr("No update available"));
}
- ui->versionEdit->setPalette(versionColor);
+// ui->versionEdit->setPalette(versionColor);
}
@@ -1203,8 +1206,8 @@ void ModInfoDialog::on_overwriteTree_customContextMenuRequested(const QPoint &po
void ModInfoDialog::on_overwrittenTree_itemDoubleClicked(QTreeWidgetItem *item, int)
{
- this->close();
emit modOpen(item->data(1, Qt::UserRole).toString(), TAB_CONFLICTS);
+ this->accept();
}
void ModInfoDialog::on_refreshButton_clicked()
@@ -1218,3 +1221,15 @@ void ModInfoDialog::on_endorseBtn_clicked()
{
emit endorseMod(m_ModInfo);
}
+
+void ModInfoDialog::on_nextButton_clicked()
+{
+ emit modOpenNext();
+ this->accept();
+}
+
+void ModInfoDialog::on_prevButton_clicked()
+{
+ emit modOpenPrev();
+ this->accept();
+}
diff --git a/src/modinfodialog.h b/src/modinfodialog.h
index 03cf44ba..7d4ddf52 100644
--- a/src/modinfodialog.h
+++ b/src/modinfodialog.h
@@ -106,6 +106,8 @@ signals:
void nexusLinkActivated(const QString &link);
void downloadRequest(const QString &link);
void modOpen(const QString &modName, int tab);
+ void modOpenNext();
+ void modOpenPrev();
void originModified(int originID);
void endorseMod(ModInfo::Ptr nexusID);
@@ -181,6 +183,10 @@ private slots:
void on_endorseBtn_clicked();
+ void on_nextButton_clicked();
+
+ void on_prevButton_clicked();
+
private:
Ui::ModInfoDialog *ui;
diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui
index faf08213..5c58c87a 100644
--- a/src/modinfodialog.ui
+++ b/src/modinfodialog.ui
@@ -804,6 +804,20 @@ p, li { white-space: pre-wrap; }
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
+ <widget class="QPushButton" name="prevButton">
+ <property name="text">
+ <string>Previous</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="nextButton">
+ <property name="text">
+ <string>Next</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
diff --git a/src/modlist.cpp b/src/modlist.cpp
index 1f318e0d..1d5db5a0 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -51,7 +51,6 @@ ModList::ModList(QObject *parent)
: QAbstractItemModel(parent), m_Profile(NULL), m_Modified(false),
m_FontMetrics(QFont())
{
-// setSupportedDragActions(Qt::MoveAction);
}
@@ -294,8 +293,7 @@ bool ModList::renameMod(int index, const QString &newName)
emit modRenamed(oldName, newName);
// invalidate the currently displayed state of this list
-#pragma message("why is this necessary? It unselects the current selection")
- notifyChange(-1);
+// notifyChange(-1);
return true;
}
@@ -314,7 +312,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
m_Profile->setModEnabled(index.row(), enabled);
m_Modified = true;
- emit modlist_changed(index.row());
+ emit modlist_changed(index, role);
}
return true;
} else if (role == Qt::EditRole) {
@@ -328,7 +326,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
if (ok) {
m_Profile->setModPriority(index.row(), newPriority);
- emit modlist_changed(index.row());
+ emit modlist_changed(index, role);
return true;
} else {
return false;
@@ -340,7 +338,7 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
int newID = value.toInt(&ok);
if (ok) {
info->setNexusID(newID);
- emit modlist_changed(index.row());
+ emit modlist_changed(index, role);
return true;
} else {
return false;
@@ -592,7 +590,7 @@ QString ModList::getColumnName(int column)
{
switch (column) {
case COL_FLAGS: return tr("Flags");
- case COL_NAME: return tr("Name");
+ case COL_NAME: return tr("Mod Name");
case COL_VERSION: return tr("Version");
case COL_PRIORITY: return tr("Priority");
case COL_CATEGORY: return tr("Category");
diff --git a/src/modlist.h b/src/modlist.h
index 7c37f4d5..a520ba75 100644
--- a/src/modlist.h
+++ b/src/modlist.h
@@ -17,214 +17,215 @@ 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 MODLIST_H
-#define MODLIST_H
-
-
-#include "categories.h"
-#include "nexusinterface.h"
-#include "modinfo.h"
-#include "profile.h"
-
-#include <QFile>
-#include <QListWidget>
-#include <QNetworkReply>
-#include <QNetworkAccessManager>
-
-#include <set>
-#include <vector>
-#include <QVector>
-
-
-/**
- * Model presenting an overview of the installed mod
- * This is used in a view in the main window of MO. It combines general information about
- * the mods from ModInfo with status information from the Profile
- **/
-class ModList : public QAbstractItemModel
-{
- Q_OBJECT
-
-public:
-
- enum EColumn {
- COL_NAME,
- COL_FLAGS,
- COL_CATEGORY,
- COL_MODID,
- COL_VERSION,
- COL_PRIORITY,
-
- COL_LASTCOLUMN = COL_PRIORITY
- };
-
-public:
-
- /**
- * @brief constructor
- * @todo ensure this view works without a profile set, otherwise there are intransparent dependencies on the initialisation order
- **/
- ModList(QObject *parent = NULL);
-
- /**
- * @brief set the profile used for status information
- *
- * @param profile the profile to use
- **/
- void setProfile(Profile *profile);
-
- /**
- * @brief retrieve the current sorting mode
- * @note this is used to store the sorting mode between sessions
- * @return current sorting mode, encoded to be compatible to previous versions
- **/
- int getCurrentSortingMode() const;
-
- /**
- * @brief force a refresh of the mod list
- **/
- void refresh();
-
- /**
- * @brief remove the specified mod without asking for confirmation
- * @param row the row to remove
- */
- void removeRowForce(int row);
-
- void notifyChange(int row);
-
- static QString getColumnName(int column);
-
- void changeModPriority(int sourceIndex, int newPriority);
-
-public: // implementation of virtual functions of QAbstractItemModel
-
- virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
- virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
- virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
- virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const;
- virtual Qt::DropActions supportedDropActions() const { return Qt::MoveAction; }
- virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
- virtual void removeRow(int row, const QModelIndex &parent);
-
-
- virtual QModelIndex index(int row, int column,
- const QModelIndex &parent = QModelIndex()) const;
- virtual QModelIndex parent(const QModelIndex &child) const;
-
-public slots:
-
-signals:
-
- /**
- * @brief emitted whenever the sorting in the list was changed by the user
- *
- * the sorting of the list can only be manually changed if the list is sorted by priority
- * in which case the move is intended to change the priority of a mod
- **/
- void modorder_changed();
-
- /**
- * @brief emitted when the model wants a text to be displayed by the UI
- *
- * @param message the message to display
- **/
- void showMessage(const QString &message);
-
- /**
- * @brief signals change to the count of headers
- */
- void resizeHeaders();
-
- /**
- * @brief requestColumnSelect is emitted whenever the user requested a menu to select visible columns
- * @param pos the position to display the menu at
- */
- void requestColumnSelect(QPoint pos);
-
- /**
- * @brief emitted to remove a file origin
- * @param name name of the orign to remove
- */
- void removeOrigin(const QString &name);
-
- /**
- * @brief emitted after a mod has been renamed
- * This signal MUST be used to fix the mod names in profiles (except the active one) and to invalidate/refresh other
- * structures that may have become invalid with the rename
- *
- * @param oldName the old name of the mod
- * @param newName new name of the mod
- */
- void modRenamed(const QString &oldName, const QString &newName);
-
- /**
- * @brief emitted whenever a row in the list has changed
- *
- * @param row the row that changed
- * @note this signal must only be emitted if the row really did change.
- * Slots handling this signal therefore do not have to verify that a change has happened
- * @note this signal is currently only used in tutorials
- **/
- void modlist_changed(int row);
-
- /**
- * @brief emitted to have all selected mods deleted
- */
- void removeSelectedMods();
-
-protected:
-
- // event filter, handles event from the header and the tree view itself
- bool eventFilter(QObject *obj, QEvent *event);
-
-private:
-
- bool testValid(const QString &modDir);
-
- void changeModPriority(std::vector<int> sourceIndices, int newPriority);
-
- QVariant getOverwriteData(int column, int role) const;
-
- QString getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const;
-
- static QString getColumnToolTip(int column);
-
- ModList::EColumn getEnabledColumn(int index) const;
-
- QVariant categoryData(int categoryID, int column, int role) const;
- QVariant modData(int modID, int modelColumn, int role) const;
-
- bool renameMod(int index, const QString &newName);
-
-private slots:
-
-private:
-
- struct TModInfo {
- TModInfo(unsigned int index, ModInfo::Ptr modInfo)
- : modInfo(modInfo), nameOrder(index) {}
- ModInfo::Ptr modInfo;
- unsigned int nameOrder;
- unsigned int priorityOrder;
- unsigned int modIDOrder;
- unsigned int categoryOrder;
- };
-
-private:
-
- Profile *m_Profile;
-
- NexusInterface *m_NexusInterface;
- std::set<int> m_RequestIDs;
-
- mutable bool m_Modified;
-
- QFontMetrics m_FontMetrics;
-
-};
-
-#endif // MODLIST_H
-
+#ifndef MODLIST_H
+#define MODLIST_H
+
+
+#include "categories.h"
+#include "nexusinterface.h"
+#include "modinfo.h"
+#include "profile.h"
+
+#include <QFile>
+#include <QListWidget>
+#include <QNetworkReply>
+#include <QNetworkAccessManager>
+
+#include <set>
+#include <vector>
+#include <QVector>
+
+
+/**
+ * Model presenting an overview of the installed mod
+ * This is used in a view in the main window of MO. It combines general information about
+ * the mods from ModInfo with status information from the Profile
+ **/
+class ModList : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+
+ enum EColumn {
+ COL_NAME,
+ COL_FLAGS,
+ COL_CATEGORY,
+ COL_MODID,
+ COL_VERSION,
+ COL_PRIORITY,
+
+ COL_LASTCOLUMN = COL_PRIORITY
+ };
+
+public:
+
+ /**
+ * @brief constructor
+ * @todo ensure this view works without a profile set, otherwise there are intransparent dependencies on the initialisation order
+ **/
+ ModList(QObject *parent = NULL);
+
+ /**
+ * @brief set the profile used for status information
+ *
+ * @param profile the profile to use
+ **/
+ void setProfile(Profile *profile);
+
+ /**
+ * @brief retrieve the current sorting mode
+ * @note this is used to store the sorting mode between sessions
+ * @return current sorting mode, encoded to be compatible to previous versions
+ **/
+ int getCurrentSortingMode() const;
+
+ /**
+ * @brief force a refresh of the mod list
+ **/
+ void refresh();
+
+ /**
+ * @brief remove the specified mod without asking for confirmation
+ * @param row the row to remove
+ */
+ void removeRowForce(int row);
+
+ void notifyChange(int row);
+
+ static QString getColumnName(int column);
+
+ void changeModPriority(int sourceIndex, int newPriority);
+
+public: // implementation of virtual functions of QAbstractItemModel
+
+ virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const;
+ virtual Qt::DropActions supportedDropActions() const { return Qt::MoveAction; }
+ virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
+ virtual void removeRow(int row, const QModelIndex &parent);
+
+
+ virtual QModelIndex index(int row, int column,
+ const QModelIndex &parent = QModelIndex()) const;
+ virtual QModelIndex parent(const QModelIndex &child) const;
+
+public slots:
+
+signals:
+
+ /**
+ * @brief emitted whenever the sorting in the list was changed by the user
+ *
+ * the sorting of the list can only be manually changed if the list is sorted by priority
+ * in which case the move is intended to change the priority of a mod
+ **/
+ void modorder_changed();
+
+ /**
+ * @brief emitted when the model wants a text to be displayed by the UI
+ *
+ * @param message the message to display
+ **/
+ void showMessage(const QString &message);
+
+ /**
+ * @brief signals change to the count of headers
+ */
+ void resizeHeaders();
+
+ /**
+ * @brief requestColumnSelect is emitted whenever the user requested a menu to select visible columns
+ * @param pos the position to display the menu at
+ */
+ void requestColumnSelect(QPoint pos);
+
+ /**
+ * @brief emitted to remove a file origin
+ * @param name name of the orign to remove
+ */
+ void removeOrigin(const QString &name);
+
+ /**
+ * @brief emitted after a mod has been renamed
+ * This signal MUST be used to fix the mod names in profiles (except the active one) and to invalidate/refresh other
+ * structures that may have become invalid with the rename
+ *
+ * @param oldName the old name of the mod
+ * @param newName new name of the mod
+ */
+ void modRenamed(const QString &oldName, const QString &newName);
+
+ /**
+ * @brief emitted whenever a row in the list has changed
+ *
+ * @param index the index of the changed field
+ * @param role role of the field that changed
+ * @note this signal must only be emitted if the row really did change.
+ * Slots handling this signal therefore do not have to verify that a change has happened
+ * @note this signal is currently only used in tutorials
+ **/
+ void modlist_changed(const QModelIndex &index, int role);
+
+ /**
+ * @brief emitted to have all selected mods deleted
+ */
+ void removeSelectedMods();
+
+protected:
+
+ // event filter, handles event from the header and the tree view itself
+ bool eventFilter(QObject *obj, QEvent *event);
+
+private:
+
+ bool testValid(const QString &modDir);
+
+ void changeModPriority(std::vector<int> sourceIndices, int newPriority);
+
+ QVariant getOverwriteData(int column, int role) const;
+
+ QString getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const;
+
+ static QString getColumnToolTip(int column);
+
+ ModList::EColumn getEnabledColumn(int index) const;
+
+ QVariant categoryData(int categoryID, int column, int role) const;
+ QVariant modData(int modID, int modelColumn, int role) const;
+
+ bool renameMod(int index, const QString &newName);
+
+private slots:
+
+private:
+
+ struct TModInfo {
+ TModInfo(unsigned int index, ModInfo::Ptr modInfo)
+ : modInfo(modInfo), nameOrder(index) {}
+ ModInfo::Ptr modInfo;
+ unsigned int nameOrder;
+ unsigned int priorityOrder;
+ unsigned int modIDOrder;
+ unsigned int categoryOrder;
+ };
+
+private:
+
+ Profile *m_Profile;
+
+ NexusInterface *m_NexusInterface;
+ std::set<int> m_RequestIDs;
+
+ mutable bool m_Modified;
+
+ QFontMetrics m_FontMetrics;
+
+};
+
+#endif // MODLIST_H
+
diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp
index bf70f238..a18174c6 100644
--- a/src/modlistsortproxy.cpp
+++ b/src/modlistsortproxy.cpp
@@ -45,10 +45,15 @@ void ModListSortProxy::setProfile(Profile *profile)
m_Profile = profile;
}
+void ModListSortProxy::updateFilterActive()
+{
+ emit filterActive((m_CategoryFilter != CategoryFactory::CATEGORY_NONE) || !m_CurrentFilter.isEmpty());
+}
void ModListSortProxy::setCategoryFilter(int category)
{
m_CategoryFilter = category;
+ updateFilterActive();
this->invalidate();
}
@@ -164,6 +169,7 @@ bool ModListSortProxy::lessThan(const QModelIndex &left,
void ModListSortProxy::updateFilter(const QString &filter)
{
m_CurrentFilter = filter;
+ updateFilterActive();
invalidateFilter();
}
diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h
index f7a9b587..d1e3e238 100644
--- a/src/modlistsortproxy.h
+++ b/src/modlistsortproxy.h
@@ -59,15 +59,19 @@ public slots:
void displayColumnSelection(const QPoint &pos);
void updateFilter(const QString &filter);
+signals:
+
+ void filterActive(bool active);
+
protected:
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
virtual bool filterAcceptsRow(int row, const QModelIndex &parent) const;
-// virtual bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const;
private:
bool hasConflictFlag(const std::vector<ModInfo::EFlag> &flags) const;
+ void updateFilterActive();
private:
diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp
index 2a53235d..49cf595e 100644
--- a/src/nexusinterface.cpp
+++ b/src/nexusinterface.cpp
@@ -366,7 +366,7 @@ void NexusInterface::nextRequest()
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml");
#pragma message("automatically insert the correct version number")
- request.setRawHeader("User-Agent", QString("Mod Organizer v0.12.8 (compatible to Nexus Client v%1)").arg(m_NMMVersion).toUtf8());
+ request.setRawHeader("User-Agent", QString("Mod Organizer v0.13.0 (compatible to Nexus Client v%1)").arg(m_NMMVersion).toUtf8());
info.m_Reply = m_AccessManager->get(request);
diff --git a/src/organizer.pro b/src/organizer.pro
index 86e9c616..076bcde9 100644
--- a/src/organizer.pro
+++ b/src/organizer.pro
@@ -203,7 +203,6 @@ QMAKE_CXXFLAGS_WARN_ON -= -W3
QMAKE_CXXFLAGS_WARN_ON += -W4
QMAKE_CXXFLAGS += -wd4127 -wd4512 -wd4189
-
CONFIG += embed_manifest_exe
# QMAKE_CXXFLAGS += /analyze
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp
index 25dc69bd..93f511a4 100644
--- a/src/pluginlist.cpp
+++ b/src/pluginlist.cpp
@@ -73,9 +73,11 @@ bool ByDate(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) {
}
PluginList::PluginList(QObject *parent)
- : QAbstractTableModel(parent), m_Modified(false),
- m_FontMetrics(QFont())
+ : QAbstractTableModel(parent),
+ m_FontMetrics(QFont()), m_SaveTimer(this)
{
+ m_SaveTimer.setSingleShot(true);
+ connect(&m_SaveTimer, SIGNAL(timeout()), this, SIGNAL(saveTimer()));
}
@@ -192,7 +194,7 @@ void PluginList::enableESP(const QString &name)
if (iter != m_ESPsByName.end()) {
m_ESPs[iter->second].m_Enabled = true;
- m_Modified = true;
+ startSaveTime();
} else {
reportError(tr("esp not found: %1").arg(name));
}
@@ -204,8 +206,7 @@ void PluginList::enableAll()
for (std::vector<ESPInfo>::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) {
iter->m_Enabled = true;
}
- m_Modified = true;
- emit esplist_changed();
+ startSaveTime();
}
@@ -216,8 +217,7 @@ void PluginList::disableAll()
iter->m_Enabled = false;
}
}
- m_Modified = true;
- emit esplist_changed();
+ startSaveTime();
}
@@ -308,7 +308,7 @@ void PluginList::readEnabledFrom(const QString &fileName)
m_ESPs[iter->second].m_Enabled = true;
} else {
qWarning("plugin %s not found", modName.toUtf8().constData());
- m_Modified = true;
+ startSaveTime();
}
}
}
@@ -417,10 +417,6 @@ void PluginList::writeLockedOrder(const QString &fileName) const
void PluginList::saveTo(const QString &pluginFileName, const QString &loadOrderFileName, const QString &lockedOrderFileName, const QString& deleterFileName, bool hideUnchecked) const
{
- if (!m_Modified) {
- return;
- }
-
writePlugins(pluginFileName, false);
writePlugins(loadOrderFileName, true);
writeLockedOrder(lockedOrderFileName);
@@ -442,7 +438,7 @@ void PluginList::saveTo(const QString &pluginFileName, const QString &loadOrderF
qDebug("%s saved", QDir::toNativeSeparators(deleterFileName).toUtf8().constData());
}
- m_Modified = false;
+ m_SaveTimer.stop();
}
@@ -508,8 +504,7 @@ void PluginList::lockESPIndex(int index, bool lock)
m_LockedOrder.erase(iter);
}
}
- m_Modified = true;
- emit esplist_changed();
+ startSaveTime();
}
@@ -544,7 +539,7 @@ void PluginList::refreshLoadOrder()
// locked esp exists
// find the location to insert at
- while ((targetPrio < static_cast<int>(m_ESPs.size())) &&
+ while ((targetPrio < static_cast<int>(m_ESPs.size() - 1)) &&
(m_ESPs[m_ESPsByPriority[targetPrio]].m_LoadOrder < iter->first)) {
if (QString::compare(m_ESPs[m_ESPsByPriority[targetPrio]].m_Name, iter->second) != 0) {
++targetPrio;
@@ -555,7 +550,7 @@ void PluginList::refreshLoadOrder()
setPluginPriority(nameIter->second, temp);
m_ESPs[nameIter->second].m_LoadOrder = iter->first;
syncLoadOrder();
- m_Modified = true;
+ startSaveTime();
}
}
}
@@ -653,8 +648,7 @@ bool PluginList::setData(const QModelIndex &index, const QVariant &value, int ro
emit layoutAboutToBeChanged();
refreshLoadOrder();
- m_Modified = true;
- emit esplist_changed();
+ startSaveTime();
emit layoutChanged();
return true;
@@ -768,14 +762,20 @@ void PluginList::changePluginPriority(std::vector<int> rows, int newPriority)
}
refreshLoadOrder();
- emit esplist_changed();
-
- m_Modified = true;
+ startSaveTime();
emit layoutChanged();
}
+void PluginList::startSaveTime()
+{
+ if (!m_SaveTimer.isActive()) {
+ m_SaveTimer.start(2000);
+ }
+}
+
+
bool PluginList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, int row, int, const QModelIndex &parent)
{
if (action == Qt::IgnoreAction) {
diff --git a/src/pluginlist.h b/src/pluginlist.h
index a46fd4d5..d2d699d5 100644
--- a/src/pluginlist.h
+++ b/src/pluginlist.h
@@ -22,6 +22,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QString>
#include <QListWidget>
+#include <QTimer>
#include <vector>
#include <directoryentry.h>
@@ -162,6 +163,11 @@ signals:
**/
void esplist_changed();
+ /**
+ * @brief emitted when the plugin list should be saved
+ */
+ void saveTimer();
+
private:
void refreshLoadOrder();
@@ -175,6 +181,8 @@ private:
void setPluginPriority(int row, int &newPriority);
void changePluginPriority(std::vector<int> rows, int newPriority);
+ void startSaveTime();
+
private:
struct ESPInfo {
@@ -212,9 +220,10 @@ private:
std::map<QString, int> m_LockedOrder;
QString m_CurrentProfile;
- mutable bool m_Modified;
QFontMetrics m_FontMetrics;
+ mutable QTimer m_SaveTimer;
+
};
#endif // PLUGINLIST_H
diff --git a/src/profile.cpp b/src/profile.cpp
index 67fd1e13..d67c6b8f 100644
--- a/src/profile.cpp
+++ b/src/profile.cpp
@@ -38,12 +38,15 @@ using namespace MOBase;
using namespace MOShared;
Profile::Profile()
+ : m_SaveTimer(NULL)
{
initTimer();
}
Profile::Profile(const QString &name, bool useDefaultSettings)
+ : m_SaveTimer(NULL)
{
+ initTimer();
QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()));
QDir profileBase(profilesDir);
@@ -63,17 +66,17 @@ Profile::Profile(const QString &name, bool useDefaultSettings)
GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings);
} catch (...) {
// clean up in case of an error
- removeDir(profileBase.absoluteFilePath(name));
+ shellDelete(QStringList(profileBase.absoluteFilePath(name)), NULL);
throw;
}
- initTimer();
refreshModStatus();
}
Profile::Profile(const QDir& directory)
- : m_Directory(directory)
+ : m_Directory(directory), m_SaveTimer(NULL)
{
+ initTimer();
if (!QFile::exists(m_Directory.filePath("modlist.txt"))) {
throw std::runtime_error(QObject::tr("modlist.txt missing").toUtf8().constData());
}
@@ -83,13 +86,12 @@ Profile::Profile(const QDir& directory)
if (!QFile::exists(getIniFileName())) {
reportError(QObject::tr("\"%1\" is missing").arg(getIniFileName()));
}
- initTimer();
refreshModStatus();
}
Profile::Profile(const Profile& reference)
- : m_Directory(reference.m_Directory)
+ : m_Directory(reference.m_Directory), m_SaveTimer(NULL)
{
initTimer();
refreshModStatus();
@@ -173,17 +175,11 @@ void Profile::createTweakedIniFile()
QFileInfo iniInfo(getIniFileName());
QString tweakedIni = iniInfo.absolutePath() + "/initweaks.ini";
-// QFile iniFile(tweakedIni);
- // workaround: the fallout nv launcher seems to mark the file read-only. crazy...
-/* ::SetFileAttributesW(ToWString(tweakedIni).c_str(), FILE_ATTRIBUTE_NORMAL);
- QFile(tweakedIni).remove();
- if (!iniFile.copy(tweakedIni)) {
- reportError(tr("failed to apply ini tweaks").append(": ").append(iniFile.errorString()));
+ if (!shellDelete(QStringList(tweakedIni), NULL)) {
+ reportError(tr("failed to update tweaked ini file, wrong settings may be used: %1").arg(windowsErrorString(::GetLastError())));
return;
- }*/
-
- QFile::remove(tweakedIni); // remove the old ini tweaks
+ }
for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
if (m_ModStatus[i].m_Enabled) {
@@ -391,11 +387,18 @@ int Profile::getModPriority(unsigned int index) const
void Profile::setModPriority(unsigned int index, int &newPriority)
{
- int newPriorityTemp = (std::max)(0, (std::min<int>)(m_ModStatus.size() - 1, newPriority));
if (m_ModStatus[index].m_Overwrite) {
// can't change priority of the overwrite
return;
}
+
+ int newPriorityTemp = (std::max)(0, (std::min<int>)(m_ModStatus.size() - 1, newPriority));
+
+ // don't try to place below overwrite
+ while (m_ModStatus[m_ModIndexByPriority[newPriorityTemp]].m_Overwrite) {
+ --newPriorityTemp;
+ }
+
int oldPriority = m_ModStatus[index].m_Priority;
if (newPriorityTemp > oldPriority) {
// priority is higher than the old, so the gap we left is in lower priorities
@@ -641,7 +644,7 @@ bool Profile::enableLocalSaves(bool enable)
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
QMessageBox::Cancel);
if (res == QMessageBox::Yes) {
- removeDir(m_Directory.absoluteFilePath("_saves"));
+ shellDelete(QStringList(m_Directory.absoluteFilePath("_saves")), NULL);
} else if (res == QMessageBox::No) {
m_Directory.rename("saves", "_saves");
} else {
diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp
index ae3d6660..ac2c534f 100644
--- a/src/profilesdialog.cpp
+++ b/src/profilesdialog.cpp
@@ -185,7 +185,7 @@ void ProfilesDialog::on_removeProfileButton_clicked()
if (item != NULL) {
delete item;
}
- removeDir(profilePath);
+ shellDelete(QStringList(profilePath), NULL);
}
}
diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp
index 503c2596..78c7dea7 100644
--- a/src/selfupdater.cpp
+++ b/src/selfupdater.cpp
@@ -90,10 +90,10 @@ SelfUpdater::~SelfUpdater()
void SelfUpdater::testForUpdate()
{
-/* if (QFile::exists(QCoreApplication::applicationDirPath() + "/mo_test_update.7z")) {
+ if (QFile::exists(QCoreApplication::applicationDirPath() + "/mo_test_update.7z")) {
emit updateAvailable();
return;
- }*/
+ }
if (m_UpdateRequestID == -1) {
m_UpdateRequestID = m_Interface->requestDescription(
@@ -104,11 +104,11 @@ void SelfUpdater::testForUpdate()
void SelfUpdater::startUpdate()
{
-/* if (QFile::exists(QCoreApplication::applicationDirPath() + "/mo_test_update.7z")) {
+ if (QFile::exists(QCoreApplication::applicationDirPath() + "/mo_test_update.7z")) {
m_UpdateFile.setFileName(QCoreApplication::applicationDirPath() + "/mo_test_update.7z");
installUpdate();
return;
- }*/
+ }
if ((m_UpdateRequestID == -1) &&
(!m_NewestVersion.isEmpty())) {
@@ -243,9 +243,13 @@ void SelfUpdater::installUpdate()
} else if (outputName == "ModOrganizer") {
data[i]->setSkip(true);
}
- QFile file(mopath.mid(0).append("/").append(outputName));
- if (file.exists()) {
- file.rename(backupPath.mid(0).append("/").append(outputName));
+ QFileInfo file(mopath.mid(0).append("/").append(outputName));
+ if (file.exists() && file.isFile()) {
+ if (!shellMove(QStringList(mopath.mid(0).append("/").append(outputName)),
+ QStringList(backupPath.mid(0).append("/").append(outputName)))) {
+ reportError(tr("failed to move outdated files: %1. Please update manually.").arg(windowsErrorString(::GetLastError())));
+ return;
+ }
}
}
diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui
index cd8a5ffd..6c54bc62 100644
--- a/src/settingsdialog.ui
+++ b/src/settingsdialog.ui
@@ -595,15 +595,13 @@ p, li { white-space: pre-wrap; }
<string>Select loading mechanism. See help for details.</string>
</property>
<property name="whatsThis">
- <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
-&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
-p, li { white-space: pre-wrap; }
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Mod Organizer needs a dll to be injected into the game so all mods are visible to it.&lt;/span&gt;&lt;/p&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;There are several means to do this:&lt;/span&gt;&lt;/p&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt; font-weight:600;&quot;&gt;Mod Organizer&lt;/span&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; (default) In this mode the Mod Organizer itself injects the dll. The disadvantage is that you always have to start the game through MO or a link created by it. This does not work for the Steam version of Oblivion!&lt;/span&gt;&lt;/p&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt; font-weight:600;&quot;&gt;Script Extender&lt;/span&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; In this mode, MO is installed as a Script Extender (obse, fose, nvse, skse) plugin. (recommended if you have a script extender installed)&lt;/span&gt;&lt;/p&gt;
-&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt; font-weight:600;&quot;&gt;Proxy DLL&lt;/span&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; In this mode, MO replaces one of the game's dlls with one that loads MO (and the original dll of course). This will ONLY work with Steam games and it has only been tested with Skyrim. Please use this only if the other mechanisms don't work.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>Mod Organizer needs a dll to be injected into the game so all mods are visible to it.
+There are several means to do this:
+*Mod Organizer* (default) In this mode the Mod Organizer itself injects the dll. The disadvantage is that you always have to start the game through MO or a link created by it.
+*Script Extender* In this mode, MO is installed as a Script Extender (obse, fose, nvse, skse) plugin.
+*Proxy DLL* In this mode, MO replaces one of the game's dlls with one that loads MO (and the original dll of course). This will ONLY work with Steam games and it has only been tested with Skyrim. Please use this only if the other mechanisms don't work.
+
+If you use the Steam version of Oblivion the default will NOT work. In this case, please install obse and use &quot;Script Extender&quot; as the load mechanism. Also you can then not start Oblivion from MO. Instead, use MO only to set up your mods, then quit and start Oblivion through Steam.</string>
</property>
</widget>
</item>
@@ -701,7 +699,7 @@ For the other games this is not a sufficient replacement for AI!</string>
<string>Back-date BSAs</string>
</property>
<property name="icon">
- <iconset resource="resources.qrc">
+ <iconset>
<normaloff>:/MO/gui/resources/emblem-readonly.png</normaloff>:/MO/gui/resources/emblem-readonly.png</iconset>
</property>
</widget>
@@ -745,9 +743,7 @@ For the other games this is not a sufficient replacement for AI!</string>
</item>
</layout>
</widget>
- <resources>
- <include location="resources.qrc"/>
- </resources>
+ <resources/>
<connections>
<connection>
<sender>buttonBox</sender>
diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp
index a37e1119..79dd90c9 100644
--- a/src/shared/fallout3info.cpp
+++ b/src/shared/fallout3info.cpp
@@ -102,6 +102,16 @@ std::vector<std::wstring> Fallout3Info::getPrimaryPlugins()
return boost::assign::list_of(L"fallout3.esm");
}
+std::vector<std::wstring> Fallout3Info::getVanillaBSAs()
+{
+ return boost::assign::list_of (L"Fallout - Textures.bsa")
+ (L"Fallout - Meshes.bsa")
+ (L"Fallout - Voices.bsa")
+ (L"Fallout - Sound.bsa")
+ (L"Fallout - MenuVoices.bsa")
+ (L"Fallout - Misc.bsa");
+}
+
std::vector<std::wstring> Fallout3Info::getIniFileNames()
{
return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini");
diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h
index 9ec32027..95a76471 100644
--- a/src/shared/fallout3info.h
+++ b/src/shared/fallout3info.h
@@ -57,6 +57,8 @@ public:
virtual std::vector<std::wstring> getPrimaryPlugins();
+ virtual std::vector<std::wstring> getVanillaBSAs();
+
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames();
diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp
index cd5586f3..6da454ee 100644
--- a/src/shared/falloutnvinfo.cpp
+++ b/src/shared/falloutnvinfo.cpp
@@ -103,6 +103,16 @@ std::vector<std::wstring> FalloutNVInfo::getPrimaryPlugins()
return boost::assign::list_of(L"falloutnv.esm");
}
+std::vector<std::wstring> FalloutNVInfo::getVanillaBSAs()
+{
+ return boost::assign::list_of (L"Fallout - Textures.bsa")
+ (L"Fallout - Textures2.bsa")
+ (L"Fallout - Meshes.bsa")
+ (L"Fallout - Voices1.bsa")
+ (L"Fallout - Sound.bsa")
+ (L"Fallout - Misc.bsa");
+}
+
std::vector<std::wstring> FalloutNVInfo::getIniFileNames()
{
return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini");
diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h
index 53784177..2f6cba7b 100644
--- a/src/shared/falloutnvinfo.h
+++ b/src/shared/falloutnvinfo.h
@@ -59,6 +59,8 @@ public:
virtual std::vector<std::wstring> getPrimaryPlugins();
+ virtual std::vector<std::wstring> getVanillaBSAs();
+
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames();
diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h
index fd8ba998..3e022ef4 100644
--- a/src/shared/gameinfo.h
+++ b/src/shared/gameinfo.h
@@ -127,6 +127,8 @@ public:
virtual std::vector<std::wstring> getPrimaryPlugins() = 0;
+ virtual std::vector<std::wstring> getVanillaBSAs() = 0;
+
// file name of this games ini file(s)
virtual std::vector<std::wstring> getIniFileNames() = 0;
diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp
index 73e841a7..7c6d5cbb 100644
--- a/src/shared/oblivioninfo.cpp
+++ b/src/shared/oblivioninfo.cpp
@@ -106,6 +106,17 @@ std::vector<std::wstring> OblivionInfo::getPrimaryPlugins()
}
+std::vector<std::wstring> OblivionInfo::getVanillaBSAs()
+{
+ return boost::assign::list_of(L"Oblivion - Meshes.bsa")
+ (L"Oblivion - Textures - Compressed.bsa")
+ (L"Oblivion - Sounds.bsa")
+ (L"Oblivion - Voices1.bsa")
+ (L"Oblivion - Voices2.bsa")
+ (L"Oblivion - Misc.bsa");
+}
+
+
std::vector<std::wstring> OblivionInfo::getIniFileNames()
{
return boost::assign::list_of(L"oblivion.ini")(L"oblivionprefs.ini");
diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h
index 51fd46e4..13374aa3 100644
--- a/src/shared/oblivioninfo.h
+++ b/src/shared/oblivioninfo.h
@@ -55,6 +55,8 @@ public:
virtual std::vector<std::wstring> getPrimaryPlugins();
+ virtual std::vector<std::wstring> getVanillaBSAs();
+
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames();
diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp
index 1c13effb..80cf4c80 100644
--- a/src/shared/skyriminfo.cpp
+++ b/src/shared/skyriminfo.cpp
@@ -131,6 +131,22 @@ std::vector<std::wstring> SkyrimInfo::getPrimaryPlugins()
return boost::assign::list_of(L"skyrim.esm")(L"update.esm");
}
+std::vector<std::wstring> SkyrimInfo::getVanillaBSAs()
+{
+ return boost::assign::list_of(L"Skyrim - Misc.bsa")
+ (L"Skyrim - Shaders.bsa")
+ (L"Skyrim - Textures.bsa")
+ (L"HighResTexturePack01.bsa")
+ (L"HighResTexturePack02.bsa")
+ (L"HighResTexturePack03.bsa")
+ (L"Skyrim - Interface.bsa")
+ (L"Skyrim - Animations.bsa")
+ (L"Skyrim - Meshes.bsa")
+ (L"Skyrim - Sounds.bsa")
+ (L"Skyrim - Voices.bsa")
+ (L"Skyrim - VoicesExtra.bsa");
+}
+
std::vector<std::wstring> SkyrimInfo::getIniFileNames()
{
return boost::assign::list_of(L"skyrim.ini")(L"skyrimprefs.ini");
diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h
index 024d0395..4760c38b 100644
--- a/src/shared/skyriminfo.h
+++ b/src/shared/skyriminfo.h
@@ -62,6 +62,8 @@ public:
virtual std::vector<std::wstring> getPrimaryPlugins();
+ virtual std::vector<std::wstring> getVanillaBSAs();
+
// file name of this games ini (no path)
virtual std::vector<std::wstring> getIniFileNames();
diff --git a/src/stylesheets/dark.qss b/src/stylesheets/dark.qss
index 80e58379..a3819fad 100644
--- a/src/stylesheets/dark.qss
+++ b/src/stylesheets/dark.qss
@@ -2,7 +2,7 @@ QToolTip
{
border: 1px solid black;
color: #D9E6EA;
- background-color: #484F53;
+ background-color: #2F3031;
padding: 1px;
border-radius: 3px;
opacity: 100;
@@ -28,7 +28,7 @@ QAbstractItemView
QLineEdit
{
- background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #484F53, stop: 0.9 #757676, stop: 1 #484F53);
+ background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #2D3330, stop: 0.9 #484F53, stop: 1 #2D3330);
padding: 1px;
border-style: solid;
border: 1px solid #1e1e1e;
@@ -205,6 +205,11 @@ QPlainTextEdit
background-color: #484F53;
}
+QWebView
+{
+ background-color: #484F53;
+}
+
QHeaderView::section
{
background-color: QLinearGradient(x1:0, y1:0, x2:0, y2:1, stop:0 #484F53, stop:0.5 #757676, stop:1 #484F53);
diff --git a/src/version.rc b/src/version.rc
index 9f083ed4..3b2c4804 100644
--- a/src/version.rc
+++ b/src/version.rc
@@ -1,7 +1,7 @@
#include "Winver.h"
-#define VER_FILEVERSION 0,12,9,0
-#define VER_FILEVERSION_STR 0,12,9,0
+#define VER_FILEVERSION 0,13,0,0
+#define VER_FILEVERSION_STR 0,13,0,0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION