From 38391b10e968d22f0e5c731642407412178f29b5 Mon Sep 17 00:00:00 2001 From: Silarn Date: Thu, 10 May 2018 09:21:02 -0500 Subject: Fix plugin lock slot typo --- src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2830c776..5b6525bd 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4637,7 +4637,7 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Unlock load order"), this, SLOT(unlockESPIndex())); } if (hasUnlocked) { - menu.addAction(tr("Lock load order"), this, SLOT(f())); + menu.addAction(tr("Lock load order"), this, SLOT(lockESPIndex())); } try { -- cgit v1.3.1 From aeb0b42cde85c8075918d564e8ca9e5652b2c39d Mon Sep 17 00:00:00 2001 From: Silarn Date: Thu, 10 May 2018 11:21:27 -0500 Subject: Refresh the sort proxy when the data is changed --- src/modlist.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/modlist.cpp b/src/modlist.cpp index 2d58081d..bf57ef6e 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -699,7 +699,9 @@ void ModList::modInfoChanged(ModInfo::Ptr info) int row = ModInfo::getIndex(info->name()); info->testValid(); + emit aboutToChangeData(); emit dataChanged(index(row, 0), index(row, columnCount())); + emit postDataChanged(); } else { qCritical("modInfoChanged not called after modInfoAboutToChange"); } -- cgit v1.3.1 From decbbb611edf002bc1458a651c331d1832ed2a6a Mon Sep 17 00:00:00 2001 From: Silarn Date: Sun, 13 May 2018 18:32:59 -0500 Subject: Download resume fixes for restarting MO, hung downloads --- src/downloadmanager.cpp | 19 ++++++++++++++++++- src/downloadmanager.h | 4 ++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index bfc4de3e..55801e81 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -70,6 +70,7 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const Mo info->m_Tries = AUTOMATIC_RETRIES; info->m_State = STATE_STARTED; info->m_TaskProgressId = TaskProgressManager::instance().getId(); + info->m_Reply = nullptr; return info; } @@ -136,6 +137,7 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(con info->m_FileInfo->fileCategory = metaFile.value("fileCategory", 0).toInt(); info->m_FileInfo->repository = metaFile.value("repository", "Nexus").toString(); info->m_FileInfo->userData = metaFile.value("userData").toMap(); + info->m_Reply = nullptr; return info; } @@ -182,6 +184,9 @@ DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent m_DateExpression("/Date\\((\\d+)\\)/") { connect(&m_DirWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); + m_TimeoutTimer.setSingleShot(false); + connect(&m_TimeoutTimer, SIGNAL(timeout()), this, SLOT(checkDownloadTimeout())); + m_TimeoutTimer.start(5 * 1000); } @@ -695,7 +700,7 @@ void DownloadManager::resumeDownloadInt(int index) DownloadInfo *info = m_ActiveDownloads[index]; // Check for finished download; - if (info->m_TotalSize <= info->m_Output.size()) { + if (info->m_TotalSize <= info->m_Output.size() && info->m_Reply != nullptr) { setState(info, STATE_DOWNLOADING); downloadFinished(index); return; @@ -1632,3 +1637,15 @@ void DownloadManager::managedGameChanged(MOBase::IPluginGame const *managedGame) { m_ManagedGame = managedGame; } + +void DownloadManager::checkDownloadTimeout() +{ + for (int i = 0; i < m_ActiveDownloads.size(); ++i) { + if (m_ActiveDownloads[i]->m_StartTime.elapsed() - std::get<3>(m_ActiveDownloads[i]->m_SpeedDiff) > 5 * 1000 && + m_ActiveDownloads[i]->m_State == STATE_DOWNLOADING) { + pauseDownload(i); + downloadFinished(i); + resumeDownload(i); + } + } +} \ No newline at end of file diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 98f5e468..17ae8a35 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -29,6 +29,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -443,6 +444,7 @@ private slots: void downloadError(QNetworkReply::NetworkError error); void metaDataChanged(); void directoryChanged(const QString &dirctory); + void checkDownloadTimeout(); private: @@ -524,6 +526,8 @@ private: QRegExp m_DateExpression; MOBase::IPluginGame const *m_ManagedGame; + + QTimer m_TimeoutTimer; }; -- cgit v1.3.1 From 0d220b6b41afdc06d2f7c2c6dce99453323ee4f4 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 15 May 2018 11:13:16 -0500 Subject: Fixes for download errors and retrying with alternate mirrors --- src/downloadmanager.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 55801e81..31671529 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -700,7 +700,8 @@ void DownloadManager::resumeDownloadInt(int index) DownloadInfo *info = m_ActiveDownloads[index]; // Check for finished download; - if (info->m_TotalSize <= info->m_Output.size() && info->m_Reply != nullptr) { + if (info->m_TotalSize <= info->m_Output.size() && info->m_Reply != nullptr + && info->m_Reply->isOpen() && info->m_Reply->isFinished() && info->m_State != STATE_ERROR) { setState(info, STATE_DOWNLOADING); downloadFinished(index); return; @@ -717,15 +718,17 @@ void DownloadManager::resumeDownloadInt(int index) } qDebug("request resume from url %s", qPrintable(info->currentURL())); QNetworkRequest request(QUrl::fromEncoded(info->currentURL().toLocal8Bit())); - info->m_ResumePos = info->m_Output.size(); + if (info->m_State != STATE_ERROR) { + info->m_ResumePos = info->m_Output.size(); + QByteArray rangeHeader = "bytes=" + QByteArray::number(info->m_ResumePos) + "-"; + request.setRawHeader("Range", rangeHeader); + } std::get<0>(info->m_SpeedDiff) = 0; std::get<1>(info->m_SpeedDiff) = 0; std::get<2>(info->m_SpeedDiff) = 0; std::get<3>(info->m_SpeedDiff) = 0; std::get<4>(info->m_SpeedDiff) = 0; qDebug("resume at %lld bytes", info->m_ResumePos); - QByteArray rangeHeader = "bytes=" + QByteArray::number(info->m_ResumePos) + "-"; - request.setRawHeader("Range", rangeHeader); startDownload(m_NexusInterface->getAccessManager()->get(request), info, true); } emit update(index); @@ -1511,15 +1514,14 @@ void DownloadManager::downloadFinished(int index) emit showMessage(tr("Warning: Content type is: %1").arg(reply->header(QNetworkRequest::ContentTypeHeader).toString())); if ((info->m_Output.size() == 0) || ((reply->error() != QNetworkReply::NoError) - && (reply->error() != QNetworkReply::OperationCanceledError) - && (reply->error() == QNetworkReply::UnknownContentError && (info->m_Output.size() != reply->header(QNetworkRequest::ContentLengthHeader).toLongLong())))) { + && (reply->error() != QNetworkReply::OperationCanceledError))) { if (reply->error() == QNetworkReply::UnknownContentError) emit showMessage(tr("Download header content length: %1 downloaded file size: %2").arg(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong()).arg(info->m_Output.size())); if (info->m_Tries == 0) { emit showMessage(tr("Download failed: %1 (%2)").arg(reply->errorString()).arg(reply->error())); } error = true; - setState(info, STATE_PAUSING); + setState(info, STATE_ERROR); } } @@ -1529,19 +1531,15 @@ void DownloadManager::downloadFinished(int index) if (info->m_Output.isOpen()) { info->m_Output.write(info->m_Reply->readAll()); } - - if (error) { - setState(info, STATE_ERROR); - } else { - setState(info, STATE_PAUSED); - } } - if (info->m_State == STATE_CANCELED) { + if (info->m_State == STATE_CANCELED || (info->m_Tries == 0 && error)) { emit aboutToUpdate(); info->m_Output.remove(); delete info; m_ActiveDownloads.erase(m_ActiveDownloads.begin() + index); + if (error) + emit showMessage(tr("We were unable to download the file due to errors after four retries. There may be an issue with the Nexus servers.")); emit update(-1); } else if (info->isPausedState()) { info->m_Output.close(); -- cgit v1.3.1 From 2ee5f0e96dd6928c271e1b37cd1f77ddc456ef9c Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 15 May 2018 12:46:49 -0500 Subject: Push version update for 2.1.4 dev --- src/version.rc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/version.rc b/src/version.rc index 083cbfb0..700e2488 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,13 +1,13 @@ #include "Winver.h" -#define VER_FILEVERSION 2,1,3 -#define VER_FILEVERSION_STR "2.1.3\0" +#define VER_FILEVERSION 2,1,4 +#define VER_FILEVERSION_STR "2.1.4-dev\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION PRODUCTVERSION VER_FILEVERSION FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -FILEFLAGS (0) +FILEFLAGS VS_FF_PRERELEASE FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE (0) -- cgit v1.3.1 From 8f905e1d498f0c264529737f4b8f7a47c0de59ee Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 15 May 2018 23:43:35 +0100 Subject: Change how the VersionInfo object is created from version.rc such that non-release builds are possible and get labelled as such --- src/selfupdater.cpp | 44 ++++++++++++++++++++++++++++++++++++++++---- src/shared/util.cpp | 28 ++++++++++++++++++++++++++++ src/shared/util.h | 1 + src/version.rc | 5 ++++- 4 files changed, 73 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index d671bafc..8027e804 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -104,10 +104,46 @@ SelfUpdater::SelfUpdater(NexusInterface *nexusInterface) VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath())); - m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16, - version.dwFileVersionMS & 0xFFFF, - version.dwFileVersionLS >> 16, - version.dwFileVersionLS & 0xFFFF); + if (version.dwFileFlags | VS_FF_PRERELEASE) + { + // Pre-release builds need annotating + QString versionString = QString::fromStdWString(GetFileVersionString(ToWString(QApplication::applicationFilePath()))); + + // The pre-release flag can be set without the string specifying what type of pre-release + bool noLetters = true; + for (QChar character : versionString) + { + if (character.isLetter()) + { + noLetters = false; + break; + } + } + + if (noLetters) + { + // Default to pre-alpha when release type is unspecified + m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16, + version.dwFileVersionMS & 0xFFFF, + version.dwFileVersionLS >> 16, + version.dwFileVersionLS & 0xFFFF, + VersionInfo::RELEASE_PREALPHA); + } + else + { + // Trust the string to make sense + m_MOVersion = VersionInfo(versionString); + qDebug() << "<: " << (m_MOVersion < VersionInfo(2, 1, 4)) << ", >: " << (m_MOVersion > VersionInfo(2, 1, 4)); + } + } + else + { + // Non-pre-release builds just need their version numbers reading + m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16, + version.dwFileVersionMS & 0xFFFF, + version.dwFileVersionLS >> 16, + version.dwFileVersionLS & 0xFFFF); + } } diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 5491a9e6..008c5050 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -174,5 +174,33 @@ VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName) } } +std::wstring GetFileVersionString(const std::wstring &fileName) +{ + DWORD handle = 0UL; + DWORD size = ::GetFileVersionInfoSizeW(fileName.c_str(), &handle); + if (size == 0) { + throw windows_error("failed to determine file version info size"); + } + + boost::scoped_array buffer(new char[size]); + try { + handle = 0UL; + if (!::GetFileVersionInfoW(fileName.c_str(), handle, size, buffer.get())) { + throw windows_error("failed to determine file version info"); + } + + LPVOID strBuffer = nullptr; + UINT strLength = 0; + if (!::VerQueryValue(buffer.get(), L"\\StringFileInfo\\040904B0\\ProductVersion", &strBuffer, &strLength)) { + throw windows_error("failed to determine file version"); + } + + return std::wstring((LPCTSTR)strBuffer); + } + catch (...) { + throw; + } +} + } // namespace MOShared diff --git a/src/shared/util.h b/src/shared/util.h index 1e498059..75c382a7 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -45,6 +45,7 @@ std::wstring ToLower(const std::wstring &text); bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs); VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName); +std::wstring GetFileVersionString(const std::wstring &fileName); } // namespace MOShared diff --git a/src/version.rc b/src/version.rc index 700e2488..39565d70 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,10 @@ #include "Winver.h" +// If VS_FF_PRERELEASE is not set, MO labels the build as a release and uses VER_FILEVERSION to determine version number. +// Otherwise, if letters are used in VER_FILEVERSION_STR, uses the full MOBase::VersionInfo parser +// Otherwise, uses the numbers from VER_FILEVERSION and sets the release type as pre-alpha #define VER_FILEVERSION 2,1,4 -#define VER_FILEVERSION_STR "2.1.4-dev\0" +#define VER_FILEVERSION_STR "2.1.4\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION -- cgit v1.3.1 From 9c1806cc74a1600f1c28610532d9a06434adf30d Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 15 May 2018 23:45:15 +0100 Subject: Remove sneaky test qDebug instance. --- src/selfupdater.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 8027e804..a57b7962 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -133,7 +133,6 @@ SelfUpdater::SelfUpdater(NexusInterface *nexusInterface) { // Trust the string to make sense m_MOVersion = VersionInfo(versionString); - qDebug() << "<: " << (m_MOVersion < VersionInfo(2, 1, 4)) << ", >: " << (m_MOVersion > VersionInfo(2, 1, 4)); } } else -- cgit v1.3.1 From 15db1b76f72a7dd88dbb84df42951c57dfe9917e Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 16 May 2018 00:42:31 +0100 Subject: Move improved version parsing to util.h and use it everywhere. --- src/main.cpp | 6 +----- src/nexusinterface.cpp | 5 +---- src/selfupdater.cpp | 42 +----------------------------------------- src/shared/util.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/shared/util.h | 3 +++ 5 files changed, 52 insertions(+), 50 deletions(-) (limited to 'src') diff --git a/src/main.cpp b/src/main.cpp index 9c30a1c6..bfc3b926 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -446,11 +446,7 @@ static void preloadSsl() static QString getVersionDisplayString() { - VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath())); - return VersionInfo(version.dwFileVersionMS >> 16, - version.dwFileVersionMS & 0xFFFF, - version.dwFileVersionLS >> 16, - version.dwFileVersionLS & 0xFFFF).displayString(); + return createVersionInfo().displayString(); } int runApplication(MOApplication &application, SingleInstance &instance, diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index d2a52c7b..e97e9800 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -147,10 +147,7 @@ QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0); NexusInterface::NexusInterface(PluginContainer *pluginContainer) : m_NMMVersion(), m_PluginContainer(pluginContainer) { - VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath())); - m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16, - version.dwFileVersionMS & 0xFFFF, - version.dwFileVersionLS >> 16); + m_MOVersion = createVersionInfo(); m_AccessManager = new NXMAccessManager(this, m_MOVersion.displayString()); m_DiskCache = new QNetworkDiskCache(this); diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index a57b7962..2b051b09 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -102,47 +102,7 @@ SelfUpdater::SelfUpdater(NexusInterface *nexusInterface) throw MyException(InstallationManager::getErrorString(m_ArchiveHandler->getLastError())); } - VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath())); - - if (version.dwFileFlags | VS_FF_PRERELEASE) - { - // Pre-release builds need annotating - QString versionString = QString::fromStdWString(GetFileVersionString(ToWString(QApplication::applicationFilePath()))); - - // The pre-release flag can be set without the string specifying what type of pre-release - bool noLetters = true; - for (QChar character : versionString) - { - if (character.isLetter()) - { - noLetters = false; - break; - } - } - - if (noLetters) - { - // Default to pre-alpha when release type is unspecified - m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16, - version.dwFileVersionMS & 0xFFFF, - version.dwFileVersionLS >> 16, - version.dwFileVersionLS & 0xFFFF, - VersionInfo::RELEASE_PREALPHA); - } - else - { - // Trust the string to make sense - m_MOVersion = VersionInfo(versionString); - } - } - else - { - // Non-pre-release builds just need their version numbers reading - m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16, - version.dwFileVersionMS & 0xFFFF, - version.dwFileVersionLS >> 16, - version.dwFileVersionLS & 0xFFFF); - } + m_MOVersion = createVersionInfo(); } diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 008c5050..102565f5 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -27,6 +27,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include namespace MOShared { @@ -202,5 +203,50 @@ std::wstring GetFileVersionString(const std::wstring &fileName) } } +MOBase::VersionInfo createVersionInfo() +{ + VS_FIXEDFILEINFO version = GetFileVersion(QApplication::applicationFilePath().toStdWString()); + + if (version.dwFileFlags | VS_FF_PRERELEASE) + { + // Pre-release builds need annotating + QString versionString = QString::fromStdWString(GetFileVersionString(QApplication::applicationFilePath().toStdWString())); + + // The pre-release flag can be set without the string specifying what type of pre-release + bool noLetters = true; + for (QChar character : versionString) + { + if (character.isLetter()) + { + noLetters = false; + break; + } + } + + if (noLetters) + { + // Default to pre-alpha when release type is unspecified + return MOBase::VersionInfo(version.dwFileVersionMS >> 16, + version.dwFileVersionMS & 0xFFFF, + version.dwFileVersionLS >> 16, + version.dwFileVersionLS & 0xFFFF, + MOBase::VersionInfo::RELEASE_PREALPHA); + } + else + { + // Trust the string to make sense + return MOBase::VersionInfo(versionString); + } + } + else + { + // Non-pre-release builds just need their version numbers reading + return MOBase::VersionInfo(version.dwFileVersionMS >> 16, + version.dwFileVersionMS & 0xFFFF, + version.dwFileVersionLS >> 16, + version.dwFileVersionLS & 0xFFFF); + } +} + } // namespace MOShared diff --git a/src/shared/util.h b/src/shared/util.h index 75c382a7..1fdfb089 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -25,6 +25,8 @@ along with Mod Organizer. If not, see . #define WIN32_LEAN_AND_MEAN #include +#include + namespace MOShared { /// Test if a file (or directory) by the specified name exists @@ -46,6 +48,7 @@ bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs); VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName); std::wstring GetFileVersionString(const std::wstring &fileName); +MOBase::VersionInfo createVersionInfo(); } // namespace MOShared -- cgit v1.3.1 From 1e6e055f398cda4ba417015fa85d6eb3eb8345fe Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 16 May 2018 11:12:15 -0500 Subject: Add the MO user agent string to the download requests (fixes EU#1) --- src/downloadmanager.cpp | 1 + src/nxmaccessmanager.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 31671529..1ffd29f0 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -356,6 +356,7 @@ bool DownloadManager::addDownload(const QStringList &URLs, QString gameName, QUrl preferredUrl = QUrl::fromEncoded(URLs.first().toLocal8Bit()); qDebug("selected download url: %s", qPrintable(preferredUrl.toString())); QNetworkRequest request(preferredUrl); + request.setHeader(QNetworkRequest::UserAgentHeader, m_NexusInterface->getAccessManager()->userAgent()); return addDownload(m_NexusInterface->getAccessManager()->get(request), URLs, fileName, gameName, modID, fileID, fileInfo); } diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 05016e8f..426c8b9c 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -224,12 +224,12 @@ void NXMAccessManager::login(const QString &username, const QString &password) QString NXMAccessManager::userAgent(const QString &subModule) const { QStringList comments; - comments << "compatible to Nexus Client v" + m_NMMVersion; + comments << "Nexus Client v" + m_NMMVersion; if (!subModule.isEmpty()) { comments << "module: " + subModule; } - return QString("Mod Organizer v%1 (%2)").arg(m_MOVersion, comments.join("; ")); + return QString("Mod Organizer/%1 (%2)").arg(m_MOVersion, comments.join("; ")); } -- cgit v1.3.1 From 941c50bff903bdd241764240167d21013d9e8f5c Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 16 May 2018 14:58:43 -0500 Subject: Add UA to resume and fix edge case for hung download check --- src/downloadmanager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 1ffd29f0..8aba42aa 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -719,6 +719,7 @@ void DownloadManager::resumeDownloadInt(int index) } qDebug("request resume from url %s", qPrintable(info->currentURL())); QNetworkRequest request(QUrl::fromEncoded(info->currentURL().toLocal8Bit())); + request.setHeader(QNetworkRequest::UserAgentHeader, m_NexusInterface->getAccessManager()->userAgent()); if (info->m_State != STATE_ERROR) { info->m_ResumePos = info->m_Output.size(); QByteArray rangeHeader = "bytes=" + QByteArray::number(info->m_ResumePos) + "-"; @@ -1641,7 +1642,8 @@ void DownloadManager::checkDownloadTimeout() { for (int i = 0; i < m_ActiveDownloads.size(); ++i) { if (m_ActiveDownloads[i]->m_StartTime.elapsed() - std::get<3>(m_ActiveDownloads[i]->m_SpeedDiff) > 5 * 1000 && - m_ActiveDownloads[i]->m_State == STATE_DOWNLOADING) { + m_ActiveDownloads[i]->m_State == STATE_DOWNLOADING && m_ActiveDownloads[i]->m_Reply != nullptr && + m_ActiveDownloads[i]->m_Reply->isOpen()) { pauseDownload(i); downloadFinished(i); resumeDownload(i); -- cgit v1.3.1 From 1c6543454ef4d3b84e2ce1f6928b6c99005eee29 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Fri, 11 May 2018 20:16:48 +0200 Subject: Initial work on avoiding the spamming of the Downlods tab refresh caused by the fileSystemWatcher picking up changes made by MO itself and triggering multiple refreshes --- src/downloadmanager.cpp | 85 +++++++++++++++++++++++++++++++++++++++++++++++-- src/downloadmanager.h | 16 ++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 8aba42aa..ea8253be 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -180,7 +180,7 @@ QString DownloadManager::DownloadInfo::currentURL() DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent) - : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false), + : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_DirWatcherDisabler(0), m_ShowHidden(false), m_DateExpression("/Date\\((\\d+)\\)/") { connect(&m_DirWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); @@ -209,8 +209,10 @@ bool DownloadManager::downloadsInProgress() return false; } + void DownloadManager::pauseAll() { + // first loop: pause all downloads for (int i = 0; i < m_ActiveDownloads.count(); ++i) { if (m_ActiveDownloads[i]->m_State < STATE_READY) { @@ -238,6 +240,7 @@ void DownloadManager::pauseAll() ::Sleep(100); } } + } @@ -276,9 +279,30 @@ void DownloadManager::setPluginContainer(PluginContainer *pluginContainer) m_NexusInterface->setPluginContainer(pluginContainer); } + +void DownloadManager::startDisableDirWatcher() +{ + m_DirWatcherDisabler++; +} + + +void DownloadManager::endDisableDirWatcher() +{ + QCoreApplication::processEvents(); + if (m_DirWatcherDisabler > 0) + m_DirWatcherDisabler--; + else + m_DirWatcherDisabler = 0; +} + + + void DownloadManager::refreshList() { try { + //avoid triggering other refreshes + startDisableDirWatcher(); + int downloadsBefore = m_ActiveDownloads.size(); // remove finished downloads @@ -339,6 +363,10 @@ void DownloadManager::refreshList() qDebug("downloads after refresh: %d", m_ActiveDownloads.size()); } emit update(-1); + + //let watcher trigger refreshes again + endDisableDirWatcher(); + } catch (const std::bad_alloc&) { reportError(tr("Memory allocation error (in refreshing directory).")); } @@ -388,7 +416,10 @@ bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, baseName = dispoName; } } + + startDisableDirWatcher(); newDownload->setName(getDownloadFileName(baseName), false); + endDisableDirWatcher(); startDownload(reply, newDownload, false); // emit update(-1); @@ -458,7 +489,9 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl else setState(newDownload, STATE_CANCELING); } else { + startDisableDirWatcher(); newDownload->setName(getDownloadFileName(newDownload->m_FileName, true), true); + endDisableDirWatcher(); if (newDownload->m_State == STATE_PAUSED) resumeDownload(indexByName(newDownload->m_FileName)); else @@ -530,6 +563,9 @@ void DownloadManager::addNXMDownload(const QString &url) void DownloadManager::removeFile(int index, bool deleteFile) { + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + if (index >= m_ActiveDownloads.size()) { throw MyException(tr("remove: invalid download index %1").arg(index)); } @@ -540,6 +576,7 @@ void DownloadManager::removeFile(int index, bool deleteFile) (download->m_State == STATE_DOWNLOADING)) { // shouldn't have been possible qCritical("tried to remove active download"); + endDisableDirWatcher(); return; } @@ -550,6 +587,7 @@ void DownloadManager::removeFile(int index, bool deleteFile) if (deleteFile) { if (!shellDelete(QStringList(filePath), true)) { reportError(tr("failed to delete %1").arg(filePath)); + endDisableDirWatcher(); return; } @@ -561,6 +599,8 @@ void DownloadManager::removeFile(int index, bool deleteFile) QSettings metaSettings(filePath.append(".meta"), QSettings::IniFormat); metaSettings.setValue("removed", true); } + + endDisableDirWatcher(); } class LessThanWrapper @@ -606,14 +646,22 @@ void DownloadManager::restoreDownload(int index) QString filePath = m_OutputDirectory + "/" + download->m_FileName; + //avoid dirWatcher triggering refreshes + startDisableDirWatcher(); + QSettings metaSettings(filePath.append(".meta"), QSettings::IniFormat); metaSettings.setValue("removed", false); + + endDisableDirWatcher(); } void DownloadManager::removeDownload(int index, bool deleteFile) { try { + //avoid dirWatcher triggering refreshes + startDisableDirWatcher(); + emit aboutToUpdate(); if (index < 0) { @@ -634,6 +682,7 @@ void DownloadManager::removeDownload(int index, bool deleteFile) } else { if (index >= m_ActiveDownloads.size()) { reportError(tr("remove: invalid download index %1").arg(index)); + endDisableDirWatcher(); return; } @@ -641,6 +690,7 @@ void DownloadManager::removeDownload(int index, bool deleteFile) delete m_ActiveDownloads.at(index); m_ActiveDownloads.erase(m_ActiveDownloads.begin() + index); } + endDisableDirWatcher(); emit update(-1); } catch (const std::exception &e) { qCritical("failed to remove download: %s", e.what()); @@ -970,11 +1020,16 @@ void DownloadManager::markInstalled(int index) throw MyException(tr("mark installed: invalid download index %1").arg(index)); } + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + DownloadInfo *info = m_ActiveDownloads.at(index); QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat); metaFile.setValue("installed", true); metaFile.setValue("uninstalled", false); + endDisableDirWatcher(); + setState(m_ActiveDownloads.at(index), STATE_INSTALLED); } @@ -986,10 +1041,15 @@ void DownloadManager::markInstalled(QString fileName) } else { DownloadInfo *info = getDownloadInfo(fileName); if (info != nullptr) { + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat); metaFile.setValue("installed", true); metaFile.setValue("uninstalled", false); delete info; + + endDisableDirWatcher(); } } } @@ -1005,10 +1065,15 @@ void DownloadManager::markUninstalled(int index) throw MyException(tr("mark uninstalled: invalid download index %1").arg(index)); } + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + DownloadInfo *info = m_ActiveDownloads.at(index); QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat); metaFile.setValue("uninstalled", true); + endDisableDirWatcher(); + setState(m_ActiveDownloads.at(index), STATE_UNINSTALLED); } @@ -1022,9 +1087,15 @@ void DownloadManager::markUninstalled(QString fileName) QString filePath = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + fileName; DownloadInfo *info = getDownloadInfo(filePath); if (info != nullptr) { + + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat); metaFile.setValue("uninstalled", true); delete info; + + endDisableDirWatcher(); } } } @@ -1183,6 +1254,9 @@ void DownloadManager::downloadReadyRead() void DownloadManager::createMetaFile(DownloadInfo *info) { + //Avoid triggering refreshes from DirWatcher + startDisableDirWatcher(); + QSettings metaFile(QString("%1.meta").arg(info->m_Output.fileName()), QSettings::IniFormat); metaFile.setValue("gameName", info->m_FileInfo->gameName); metaFile.setValue("modID", info->m_FileInfo->modID); @@ -1204,6 +1278,7 @@ void DownloadManager::createMetaFile(DownloadInfo *info) (info->m_State == DownloadManager::STATE_ERROR)); metaFile.setValue("removed", info->m_Hidden); + endDisableDirWatcher(); // slightly hackish... for (int i = 0; i < m_ActiveDownloads.size(); ++i) { if (m_ActiveDownloads[i] == info) { @@ -1572,11 +1647,14 @@ void DownloadManager::downloadFinished(int index) QString newName = getFileNameFromNetworkReply(reply); QString oldName = QFileInfo(info->m_Output).fileName(); + + startDisableDirWatcher(); if (!newName.isEmpty() && (oldName.isEmpty())) { info->setName(getDownloadFileName(newName), true); } else { info->setName(m_OutputDirectory + "/" + info->m_FileName, true); // don't rename but remove the ".unfinished" extension } + endDisableDirWatcher(); if (!isNexus) { setState(info, STATE_READY); @@ -1616,7 +1694,9 @@ void DownloadManager::metaDataChanged() if (info != nullptr) { QString newName = getFileNameFromNetworkReply(info->m_Reply); if (!newName.isEmpty() && (info->m_FileName.isEmpty())) { + startDisableDirWatcher(); info->setName(getDownloadFileName(newName), true); + endDisableDirWatcher(); refreshAlphabeticalTranslation(); if (!info->m_Output.isOpen() && !info->m_Output.open(QIODevice::WriteOnly | QIODevice::Append)) { reportError(tr("failed to re-open %1").arg(info->m_FileName)); @@ -1630,7 +1710,8 @@ void DownloadManager::metaDataChanged() void DownloadManager::directoryChanged(const QString&) { - refreshList(); + if(m_DirWatcherDisabler==0) + refreshList(); } void DownloadManager::managedGameChanged(MOBase::IPluginGame const *managedGame) diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 17ae8a35..74627be5 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -144,6 +144,17 @@ public: **/ void setOutputDirectory(const QString &outputDirectory); + /** + * @brief disables feedback from the downlods fileSystemWhatcher untill disableDownloadsWatcherEnd() is called + * + **/ + void startDisableDirWatcher(); + + /** + * @brief re-enables feedback from the downlods fileSystemWhatcher after disableDownloadsWatcherStart() was called + **/ + void endDisableDirWatcher(); + /** * @return current download directory **/ @@ -519,6 +530,11 @@ private: QFileSystemWatcher m_DirWatcher; + //The dirWatcher is actually triggering off normal Mo operations such as deleting downloads or editing .meta files + //so it needs to be disabled during operations that are known to cause the creation or deletion of files in the Downloads folder. + //Notably using QSettings to edit a file creates a temporarily .lock file that causes the Watcher to trigger multiple listRefreshes freezing the ui. + int m_DirWatcherDisabler; + std::map m_DownloadFails; bool m_ShowHidden; -- cgit v1.3.1 From 8731159bfbaa34c431343b4e415590ae079a24bb Mon Sep 17 00:00:00 2001 From: Al12rs Date: Thu, 17 May 2018 00:01:54 +0200 Subject: Huge performace improvements for downloads tab by disabling the dirWatcher triggered refreshes. Avoid app freezes caused by stacked refresh calls. Added chack to see if a download is alerady hidden before trying to hide it. Made sure to refresh Download tab where it was othwerwise relying in the dirwatcher to refresh. --- src/downloadmanager.cpp | 50 +++++++++++++++++++++++++++++-------------------- src/downloadmanager.h | 7 ++++--- 2 files changed, 34 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index ea8253be..6cd86ece 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -54,6 +54,7 @@ using namespace MOBase; static const char UNFINISHED[] = ".unfinished"; unsigned int DownloadManager::DownloadInfo::s_NextDownloadID = 1U; +int DownloadManager::m_DirWatcherDisabler = 0; DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const ModRepositoryFileInfo *fileInfo, const QStringList &URLs) @@ -142,6 +143,25 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(con return info; } +void DownloadManager::startDisableDirWatcher() +{ + DownloadManager::m_DirWatcherDisabler++; +} + + +void DownloadManager::endDisableDirWatcher() +{ + if (DownloadManager::m_DirWatcherDisabler > 0) + { + if (DownloadManager::m_DirWatcherDisabler == 1) + QCoreApplication::processEvents(); + DownloadManager::m_DirWatcherDisabler--; + } + else { + DownloadManager::m_DirWatcherDisabler = 0; + } +} + void DownloadManager::DownloadInfo::setName(QString newName, bool renameFile) { QString oldMetaFileName = QString("%1.meta").arg(m_FileName); @@ -180,7 +200,7 @@ QString DownloadManager::DownloadInfo::currentURL() DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent) - : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_DirWatcherDisabler(0), m_ShowHidden(false), + : IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false), m_DateExpression("/Date\\((\\d+)\\)/") { connect(&m_DirWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); @@ -280,20 +300,7 @@ void DownloadManager::setPluginContainer(PluginContainer *pluginContainer) } -void DownloadManager::startDisableDirWatcher() -{ - m_DirWatcherDisabler++; -} - -void DownloadManager::endDisableDirWatcher() -{ - QCoreApplication::processEvents(); - if (m_DirWatcherDisabler > 0) - m_DirWatcherDisabler--; - else - m_DirWatcherDisabler = 0; -} @@ -359,9 +366,9 @@ void DownloadManager::refreshList() } } - if (m_ActiveDownloads.size() != downloadsBefore) { + //if (m_ActiveDownloads.size() != downloadsBefore) { qDebug("downloads after refresh: %d", m_ActiveDownloads.size()); - } + //} emit update(-1); //let watcher trigger refreshes again @@ -597,9 +604,10 @@ void DownloadManager::removeFile(int index, bool deleteFile) } } else { QSettings metaSettings(filePath.append(".meta"), QSettings::IniFormat); - metaSettings.setValue("removed", true); + if(!download->m_Hidden) + metaSettings.setValue("removed", true); } - + endDisableDirWatcher(); } @@ -682,6 +690,7 @@ void DownloadManager::removeDownload(int index, bool deleteFile) } else { if (index >= m_ActiveDownloads.size()) { reportError(tr("remove: invalid download index %1").arg(index)); + //emit update(-1); endDisableDirWatcher(); return; } @@ -690,11 +699,12 @@ void DownloadManager::removeDownload(int index, bool deleteFile) delete m_ActiveDownloads.at(index); m_ActiveDownloads.erase(m_ActiveDownloads.begin() + index); } - endDisableDirWatcher(); emit update(-1); + endDisableDirWatcher(); } catch (const std::exception &e) { qCritical("failed to remove download: %s", e.what()); } + refreshList(); } @@ -1710,7 +1720,7 @@ void DownloadManager::metaDataChanged() void DownloadManager::directoryChanged(const QString&) { - if(m_DirWatcherDisabler==0) + if(DownloadManager::m_DirWatcherDisabler==0) refreshList(); } diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 74627be5..aa859b13 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -148,12 +148,12 @@ public: * @brief disables feedback from the downlods fileSystemWhatcher untill disableDownloadsWatcherEnd() is called * **/ - void startDisableDirWatcher(); + static void startDisableDirWatcher(); /** * @brief re-enables feedback from the downlods fileSystemWhatcher after disableDownloadsWatcherStart() was called **/ - void endDisableDirWatcher(); + static void endDisableDirWatcher(); /** * @return current download directory @@ -533,7 +533,8 @@ private: //The dirWatcher is actually triggering off normal Mo operations such as deleting downloads or editing .meta files //so it needs to be disabled during operations that are known to cause the creation or deletion of files in the Downloads folder. //Notably using QSettings to edit a file creates a temporarily .lock file that causes the Watcher to trigger multiple listRefreshes freezing the ui. - int m_DirWatcherDisabler; + static int m_DirWatcherDisabler; + std::map m_DownloadFails; -- cgit v1.3.1 From 2fc55ccc869c22aaea2524e05b0f0221399fb44d Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 16 May 2018 23:54:50 -0500 Subject: Updated strings for downloads --- src/organizer_en.ts | 208 ++++++++++++++++++++++++++-------------------------- 1 file changed, 106 insertions(+), 102 deletions(-) (limited to 'src') diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 0a604b27..1b9c2eeb 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -620,276 +620,281 @@ p, li { white-space: pre-wrap; } DownloadManager - + failed to rename "%1" to "%2" - + Memory allocation error (in refreshing directory). - + failed to download %1: could not open output file: %2 - + Download again? - + A file with the same name has already been downloaded. Do you want to download it again? The new file will receive a different name. - + Wrong Game - + The download link is for a mod for "%1" but this instance of MO has been set up for "%2". - - + + Already Started - + A download for this mod file has already been queued. - + There is already a download started for this file (mod: %1, file: %2). - - + + remove: invalid download index %1 - + failed to delete %1 - + failed to delete meta file for %1 - + restore: invalid download index: %1 - + cancel: invalid download index %1 - + pause: invalid download index %1 - + resume: invalid download index %1 - + resume (int): invalid download index %1 - + No known download urls. Sorry, this download can't be resumed. - + query: invalid download index %1 - + Please enter the nexus mod id - + Mod ID: - + Please select the source game code for %1 - + get pending: invalid download index %1 - + get path: invalid download index %1 - + Main - + Update - + Optional - + Old - + Misc - + Unknown - + display name: invalid download index %1 - + file name: invalid download index %1 - + file time: invalid download index %1 - + file size: invalid download index %1 - + progress: invalid download index %1 - + state: invalid download index %1 - + infocomplete: invalid download index %1 - - + + mod id: invalid download index %1 - + ishidden: invalid download index %1 - + file info: invalid download index %1 - + mark installed: invalid download index %1 - + mark uninstalled: invalid download index %1 - + Memory allocation error (in processing progress event). - + Memory allocation error (in processing downloaded data). - + Information updated - - + + No matching file found on Nexus! Maybe this file is no longer available or it was renamed? - + No file on Nexus matches the selected file by name. Please manually choose the correct one. - + No download server available. Please try again later. - + Failed to request file info from nexus: %1 - + Warning: Content type is: %1 - + Download header content length: %1 downloaded file size: %2 - + Download failed: %1 (%2) - + + We were unable to download the file due to errors after four retries. There may be an issue with the Nexus servers. + + + + failed to re-open %1 @@ -1256,7 +1261,6 @@ p, li { white-space: pre-wrap; } None of the available installer plugins were able to handle that archive. This is likely due to a corrupted or incompatible download or unrecognized archive format. - None of the available installer plugins were able to handle that archive @@ -3749,113 +3753,113 @@ p, li { white-space: pre-wrap; } - + drag&drop failed: %1 - + Confirm - + Are you sure you want to remove "%1"? - + Flags - + Content - + Mod Name - + Version - + Priority - + Category - + Source Game - + Nexus ID - + Installation - - + + unknown - + Name of your mods - + Version of the mod (if available) - + Installation priority of your mod. The higher, the more "important" it is and thus overwrites files from mods with lower priority. - + Category of the mod. - + The source game which was the origin of this mod. - + Id of the mod as used on Nexus. - + Emblemes to highlight things that might require attention. - + Depicts the content of the mod:<br><table cellspacing=7><tr><td><img src=":/MO/gui/content/plugin" width=32/></td><td>Game plugins (esp/esm/esl)</td></tr><tr><td><img src=":/MO/gui/content/interface" width=32/></td><td>Interface</td></tr><tr><td><img src=":/MO/gui/content/mesh" width=32/></td><td>Meshes</td></tr><tr><td><img src=":/MO/gui/content/bsa" width=32/></td><td>BSA</td></tr><tr><td><img src=":/MO/gui/content/texture" width=32/></td><td>Textures</td></tr><tr><td><img src=":/MO/gui/content/sound" width=32/></td><td>Sounds</td></tr><tr><td><img src=":/MO/gui/content/music" width=32/></td><td>Music</td></tr><tr><td><img src=":/MO/gui/content/string" width=32/></td><td>Strings</td></tr><tr><td><img src=":/MO/gui/content/script" width=32/></td><td>Scripts (Papyrus)</td></tr><tr><td><img src=":/MO/gui/content/skse" width=32/></td><td>Script Extender plugins</td></tr><tr><td><img src=":/MO/gui/content/skyproc" width=32/></td><td>SkyProc Patcher</td></tr><tr><td><img src=":/MO/gui/content/menu" width=32/></td><td>Mod Configuration Menu</td></tr></table> - + Time this mod was installed @@ -3933,17 +3937,17 @@ p, li { white-space: pre-wrap; } NexusInterface - + Failed to guess mod id for "%1", please pick the correct one - + empty response - + invalid response @@ -5038,33 +5042,33 @@ If the folder was still in use, restart MO and try again. - + Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!) - + failed to start shortcut: %1 - + failed to start application: %1 - + Mod Organizer - + An instance of Mod Organizer is already running - + Failed to set up instance @@ -5281,58 +5285,58 @@ Start elevated anyway? (you will be asked if you want to allow ModOrganizer.exe - + New update available (%1) - + Do you want to install update? All your mods and setup will be left untouched. Select Show Details option to see the full change-log. - + Install - + Download failed - + Failed to find correct download, please try again later. - + Update - + Download in progress - + Download failed: %1 - + Failed to install update: %1 - + Failed to start %1: %2 - + Error -- cgit v1.3.1 From 6617be8ef9a015dd4109729155fc9a772997b5ed Mon Sep 17 00:00:00 2001 From: Diana Date: Fri, 18 May 2018 07:45:13 -0400 Subject: Fix a memory leak in updateToolBar updateToolBar was creating new QActions and QWidgets every call, without cleaning them up. The spacer, help widgets, and toolbuttons only need to be created once. Those have been moved out into the MainWindow constructor updateToolBar is called in various places, importantly when adding/removing executable shortcuts. Also adds a deleteLater() call to clean up executable shortcut actions. --- src/mainwindow.cpp | 66 ++++++++++++++++++++++++++++-------------------------- src/mainwindow.h | 2 ++ 2 files changed, 36 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5b6525bd..4d1a3b03 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -242,7 +242,28 @@ MainWindow::MainWindow(QSettings &initSettings updateProblemsButton(); - updateToolBar(); + // Setup toolbar + QWidget *spacer = new QWidget(ui->toolBar); + spacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + QWidget *widget = ui->toolBar->widgetForAction(ui->actionTool); + QToolButton *toolBtn = qobject_cast(widget); + + if (toolBtn->menu() == nullptr) { + actionToToolButton(ui->actionTool); + } + + actionToToolButton(ui->actionHelp); + createHelpWidget(); + + for (QAction *action : ui->toolBar->actions()) { + if (action->isSeparator()) { + // insert spacers + ui->toolBar->insertWidget(action, spacer); + m_Sep = action; + // m_Sep would only use the last seperator anyway, and we only have the one anyway? + break; + } + } TaskProgressManager::instance().tryCreateTaskbar(); @@ -560,41 +581,22 @@ void MainWindow::updateToolBar() for (QAction *action : ui->toolBar->actions()) { if (action->objectName().startsWith("custom__")) { ui->toolBar->removeAction(action); + action->deleteLater(); } } - QWidget *spacer = new QWidget(ui->toolBar); - spacer->setObjectName("custom__spacer"); - spacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); - QWidget *widget = ui->toolBar->widgetForAction(ui->actionTool); - QToolButton *toolBtn = qobject_cast(widget); - - if (toolBtn->menu() == nullptr) { - actionToToolButton(ui->actionTool); - } - - actionToToolButton(ui->actionHelp); - createHelpWidget(); - - for (QAction *action : ui->toolBar->actions()) { - if (action->isSeparator()) { - // insert spacers - ui->toolBar->insertWidget(action, spacer); - - std::vector::iterator begin, end; - m_OrganizerCore.executablesList()->getExecutables(begin, end); - for (auto iter = begin; iter != end; ++iter) { - if (iter->isShownOnToolbar()) { - QAction *exeAction = new QAction(iconForExecutable(iter->m_BinaryInfo.filePath()), - iter->m_Title, - ui->toolBar); - exeAction->setObjectName(QString("custom__") + iter->m_Title); - if (!connect(exeAction, SIGNAL(triggered()), this, SLOT(startExeAction()))) { - qDebug("failed to connect trigger?"); - } - ui->toolBar->insertAction(action, exeAction); - } + std::vector::iterator begin, end; + m_OrganizerCore.executablesList()->getExecutables(begin, end); + for (auto iter = begin; iter != end; ++iter) { + if (iter->isShownOnToolbar()) { + QAction *exeAction = new QAction(iconForExecutable(iter->m_BinaryInfo.filePath()), + iter->m_Title, + ui->toolBar); + exeAction->setObjectName(QString("custom__") + iter->m_Title); + if (!connect(exeAction, SIGNAL(triggered()), this, SLOT(startExeAction()))) { + qDebug("failed to connect trigger?"); } + ui->toolBar->insertAction(m_Sep, exeAction); } } } diff --git a/src/mainwindow.h b/src/mainwindow.h index bbff0d93..02094344 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -304,6 +304,8 @@ private: Ui::MainWindow *ui; + QAction *m_Sep; // Executable Shortcuts are added after this. Non owning. + bool m_WasVisible; MOBase::TutorialControl m_Tutorial; -- cgit v1.3.1 From 4b66c6c1ef52a68cbca752be69fc57b47a25104c Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 20 May 2018 23:02:50 +0200 Subject: Added "Open Plugins folder" option to Open folder menu. --- src/mainwindow.cpp | 17 +++++++++-------- src/mainwindow.h | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4d1a3b03..a428d5b3 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3155,6 +3155,13 @@ void MainWindow::openInstallFolder() ::ShellExecuteW(nullptr, L"explore", ToWString(qApp->applicationDirPath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } +void MainWindow::openPluginsFolder() +{ + QString pluginsPath = QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath()); + ::ShellExecuteW(nullptr, L"explore", ToWString(pluginsPath).c_str(), nullptr, nullptr, SW_SHOWNORMAL); +} + + void MainWindow::openProfileFolder() { ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.currentProfile()->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); @@ -3366,15 +3373,9 @@ QMenu *MainWindow::openFolderMenu() FolderMenu->addAction(tr("Open MO2 Install folder"), this, SLOT(openInstallFolder())); - FolderMenu->addAction(tr("Open MO2 Logs folder"), this, SLOT(openLogsFolder())); - - - - - - - + FolderMenu->addAction(tr("Open MO2 Plugins folder"), this, SLOT(openPluginsFolder())); + FolderMenu->addAction(tr("Open MO2 Logs folder"), this, SLOT(openLogsFolder())); return FolderMenu; diff --git a/src/mainwindow.h b/src/mainwindow.h index 02094344..737a1534 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -500,6 +500,7 @@ private slots: void openInstanceFolder(); void openLogsFolder(); void openInstallFolder(); + void openPluginsFolder(); void openDownloadsFolder(); void openProfileFolder(); void openGameFolder(); -- cgit v1.3.1 From 6d6444c126c59c4307873755b08fc5773b3d3955 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 20 May 2018 23:53:57 +0200 Subject: Made downlods file sizes human readable instead of just being KB --- src/downloadlistwidget.cpp | 21 ++++++++++++++++++++- src/downloadlistwidget.h | 2 ++ src/downloadlistwidget.ui | 3 +++ 3 files changed, 25 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index 2af74cc2..94394778 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -87,6 +87,25 @@ void DownloadListWidgetDelegate::drawCache(QPainter *painter, const QStyleOption } +QString DownloadListWidgetDelegate::sizeFormat(quint64 size) const +{ + qreal calc = size; + QStringList list; + list << "KB" << "MB" << "GB" << "TB"; + + QStringListIterator i(list); + QString unit("byte(s)"); + + while (calc >= 1024.0 && i.hasNext()) + { + unit = i.next(); + calc /= 1024.0; + } + + return QString().setNum(calc, 'f', 2) + " " + unit; +} + + void DownloadListWidgetDelegate::paintPendingDownload(int downloadIndex) const { std::tuple nexusids = m_Manager->getPendingDownload(downloadIndex); @@ -106,7 +125,7 @@ void DownloadListWidgetDelegate::paintRegularDownload(int downloadIndex) const name.append("..."); } m_NameLabel->setText(name); - m_SizeLabel->setText(QString::number(m_Manager->getFileSize(downloadIndex) / 1024)); + m_SizeLabel->setText(sizeFormat(m_Manager->getFileSize(downloadIndex) )); DownloadManager::DownloadState state = m_Manager->getState(downloadIndex); if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) { QPalette labelPalette; diff --git a/src/downloadlistwidget.h b/src/downloadlistwidget.h index c1dfe4cd..62f21837 100644 --- a/src/downloadlistwidget.h +++ b/src/downloadlistwidget.h @@ -74,11 +74,13 @@ signals: protected: + QString sizeFormat(quint64 size) const; bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); private: + void drawCache(QPainter *painter, const QStyleOptionViewItem &option, const QPixmap &cache) const; private slots: diff --git a/src/downloadlistwidget.ui b/src/downloadlistwidget.ui index 7a6ce8ba..9e238509 100644 --- a/src/downloadlistwidget.ui +++ b/src/downloadlistwidget.ui @@ -86,6 +86,9 @@ KB + + + false -- cgit v1.3.1 From 186f26b71e4597e9999a3d946b0d9497255fc986 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Mon, 21 May 2018 01:27:13 +0200 Subject: Added un-hide all option to downlods tab. Improved performace when hiding/unhiding. There is still room for improvement for hiding but it requires refactoring the function --- src/downloadlistwidget.cpp | 24 +++++++++++++++++------ src/downloadlistwidget.h | 1 + src/downloadlistwidgetcompact.cpp | 22 +++++++++++++++------ src/downloadlistwidgetcompact.h | 1 + src/downloadmanager.cpp | 40 +++++++++++++++++++++++++++------------ 5 files changed, 64 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index 94394778..d7bd01bb 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -255,9 +255,15 @@ void DownloadListWidgetDelegate::issueRemoveFromView() void DownloadListWidgetDelegate::issueRestoreToView() { - emit restoreDownload(m_ContextRow); + emit restoreDownload(m_ContextRow); } +void DownloadListWidgetDelegate::issueRestoreToViewAll() +{ + emit restoreDownload(-1); +} + + void DownloadListWidgetDelegate::issueCancel() { emit cancelDownload(m_ContextRow); @@ -353,11 +359,17 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel * } menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted())); menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll())); - if (!hidden) { - menu.addSeparator(); - menu.addAction(tr("Hide Installed..."), this, SLOT(issueRemoveFromViewCompleted())); - menu.addAction(tr("Hide All..."), this, SLOT(issueRemoveFromViewAll())); - } + + if (!hidden) { + menu.addSeparator(); + menu.addAction(tr("Hide Installed..."), this, SLOT(issueRemoveFromViewCompleted())); + menu.addAction(tr("Hide All..."), this, SLOT(issueRemoveFromViewAll())); + } + if (hidden) { + menu.addSeparator(); + menu.addAction(tr("Un-Hide All..."), this, SLOT(issueRestoreToViewAll())); + } + menu.exec(mouseEvent->globalPos()); event->accept(); diff --git a/src/downloadlistwidget.h b/src/downloadlistwidget.h index 62f21837..6e628cc7 100644 --- a/src/downloadlistwidget.h +++ b/src/downloadlistwidget.h @@ -89,6 +89,7 @@ private slots: void issueDelete(); void issueRemoveFromView(); void issueRestoreToView(); + void issueRestoreToViewAll(); void issueCancel(); void issuePause(); void issueResume(); diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index 898d400a..97e3655b 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -219,9 +219,15 @@ void DownloadListWidgetCompactDelegate::issueRemoveFromView() void DownloadListWidgetCompactDelegate::issueRestoreToView() { - emit restoreDownload(m_ContextIndex.row()); + emit restoreDownload(m_ContextIndex.row()); } +void DownloadListWidgetCompactDelegate::issueRestoreToViewAll() +{ + emit restoreDownload(-1); +} + + void DownloadListWidgetCompactDelegate::issueCancel() { emit cancelDownload(m_ContextIndex.row()); @@ -318,11 +324,15 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem } menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted())); menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll())); - if (!hidden) { - menu.addSeparator(); - menu.addAction(tr("Hide Installed..."), this, SLOT(issueRemoveFromViewCompleted())); - menu.addAction(tr("Hide All..."), this, SLOT(issueRemoveFromViewAll())); - } + if (!hidden) { + menu.addSeparator(); + menu.addAction(tr("Hide Installed..."), this, SLOT(issueRemoveFromViewCompleted())); + menu.addAction(tr("Hide All..."), this, SLOT(issueRemoveFromViewAll())); + } + if (hidden) { + menu.addSeparator(); + menu.addAction(tr("Un-Hide All..."), this, SLOT(issueRestoreToViewAll())); + } menu.exec(mouseEvent->globalPos()); event->accept(); diff --git a/src/downloadlistwidgetcompact.h b/src/downloadlistwidgetcompact.h index bf855d5f..df1a5f58 100644 --- a/src/downloadlistwidgetcompact.h +++ b/src/downloadlistwidgetcompact.h @@ -87,6 +87,7 @@ private slots: void issueDelete(); void issueRemoveFromView(); void issueRestoreToView(); + void issueRestoreToViewAll(); void issueCancel(); void issuePause(); void issueResume(); diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 6cd86ece..93a46d78 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -645,22 +645,38 @@ void DownloadManager::refreshAlphabeticalTranslation() void DownloadManager::restoreDownload(int index) { - if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("restore: invalid download index: %1").arg(index)); - } - DownloadInfo *download = m_ActiveDownloads.at(index); - download->m_Hidden = false; + if (index < 0) { + DownloadState minState = STATE_READY ; + index = 0; - QString filePath = m_OutputDirectory + "/" + download->m_FileName; + for (QVector::const_iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end(); ++iter ) { + + if ((*iter)->m_State >= minState) { + restoreDownload(index); + } + index++; + } + } + else { + if (index >= m_ActiveDownloads.size()) { + throw MyException(tr("restore: invalid download index: %1").arg(index)); + } - //avoid dirWatcher triggering refreshes - startDisableDirWatcher(); + DownloadInfo *download = m_ActiveDownloads.at(index); + if (download->m_Hidden) { + download->m_Hidden = false; - QSettings metaSettings(filePath.append(".meta"), QSettings::IniFormat); - metaSettings.setValue("removed", false); + QString filePath = m_OutputDirectory + "/" + download->m_FileName; - endDisableDirWatcher(); + //avoid dirWatcher triggering refreshes + startDisableDirWatcher(); + QSettings metaSettings(filePath.append(".meta"), QSettings::IniFormat); + metaSettings.setValue("removed", false); + + endDisableDirWatcher(); + } + } } @@ -680,7 +696,7 @@ void DownloadManager::removeDownload(int index, bool deleteFile) removeFile(index, deleteFile); delete *iter; iter = m_ActiveDownloads.erase(iter); - QCoreApplication::processEvents(); + //QCoreApplication::processEvents(); } else { ++iter; -- cgit v1.3.1 From ddb40b712edf0676f6083a0ecd5a67509e60e6fe Mon Sep 17 00:00:00 2001 From: Al12rs Date: Fri, 25 May 2018 15:33:53 +0200 Subject: *Avoided some unnecessary refreshes while removing mods. *Added "Visit on Nexus" menu entry in the downloads tab. *Added confirmation message when deleting a single download. *Changed text of messages to more easily distinguish deleting from hiding. --- src/downloadlistwidget.cpp | 20 +++++++++++++++++--- src/downloadlistwidget.h | 6 ++++-- src/downloadlistwidgetcompact.cpp | 33 ++++++++++++++++++++++----------- src/downloadlistwidgetcompact.h | 7 ++++--- src/downloadmanager.cpp | 35 ++++++++++++++++++++++++++++++++--- src/downloadmanager.h | 2 ++ src/mainwindow.cpp | 11 +++++++---- 7 files changed, 88 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index d7bd01bb..75470056 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -245,7 +245,11 @@ void DownloadListWidgetDelegate::issueQueryInfo() void DownloadListWidgetDelegate::issueDelete() { - emit removeDownload(m_ContextRow, true); + if (QMessageBox::question(nullptr, tr("Delete Files?"), + tr("This will permanently delete the selected download."), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + emit removeDownload(m_ContextRow, true); + } } void DownloadListWidgetDelegate::issueRemoveFromView() @@ -263,6 +267,11 @@ void DownloadListWidgetDelegate::issueRestoreToViewAll() emit restoreDownload(-1); } +void DownloadListWidgetDelegate::issueVisitOnNexus() +{ + emit visitOnNexus(m_ContextRow); +} + void DownloadListWidgetDelegate::issueCancel() { @@ -281,7 +290,7 @@ void DownloadListWidgetDelegate::issueResume() void DownloadListWidgetDelegate::issueDeleteAll() { - if (QMessageBox::question(nullptr, tr("Are you sure?"), + if (QMessageBox::question(nullptr, tr("Delete Files?"), tr("This will remove all finished downloads from this list and from disk."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit removeDownload(-1, true); @@ -290,7 +299,7 @@ void DownloadListWidgetDelegate::issueDeleteAll() void DownloadListWidgetDelegate::issueDeleteCompleted() { - if (QMessageBox::question(nullptr, tr("Are you sure?"), + if (QMessageBox::question(nullptr, tr("Delete Files?"), tr("This will remove all installed downloads from this list and from disk."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit removeDownload(-2, true); @@ -340,7 +349,12 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel * menu.addAction(tr("Install"), this, SLOT(issueInstall())); if (m_Manager->isInfoIncomplete(m_ContextRow)) { menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfo())); + }else { + menu.addAction(tr("Visit on Nexus"), this,SLOT(issueVisitOnNexus())); } + + menu.addSeparator(); + menu.addAction(tr("Delete"), this, SLOT(issueDelete())); if (hidden) { menu.addAction(tr("Un-Hide"), this, SLOT(issueRestoreToView())); diff --git a/src/downloadlistwidget.h b/src/downloadlistwidget.h index 6e628cc7..e2746f3a 100644 --- a/src/downloadlistwidget.h +++ b/src/downloadlistwidget.h @@ -71,6 +71,7 @@ signals: void cancelDownload(int index); void pauseDownload(int index); void resumeDownload(int index); + void visitOnNexus(int index); protected: @@ -80,7 +81,7 @@ protected: private: - + void drawCache(QPainter *painter, const QStyleOptionViewItem &option, const QPixmap &cache) const; private slots: @@ -89,7 +90,8 @@ private slots: void issueDelete(); void issueRemoveFromView(); void issueRestoreToView(); - void issueRestoreToViewAll(); + void issueRestoreToViewAll(); + void issueVisitOnNexus(); void issueCancel(); void issuePause(); void issueResume(); diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index 97e3655b..cdce8d7f 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -209,7 +209,11 @@ void DownloadListWidgetCompactDelegate::issueQueryInfo() void DownloadListWidgetCompactDelegate::issueDelete() { - emit removeDownload(m_ContextIndex.row(), true); + if (QMessageBox::question(nullptr, tr("Are you sure?"), + tr("This will permanently delete the selected download."), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + emit removeDownload(m_ContextIndex.row(), true); + } } void DownloadListWidgetCompactDelegate::issueRemoveFromView() @@ -217,6 +221,11 @@ void DownloadListWidgetCompactDelegate::issueRemoveFromView() emit removeDownload(m_ContextIndex.row(), false); } +void DownloadListWidgetCompactDelegate::issueVisitOnNexus() +{ + emit visitOnNexus(m_ContextIndex.row()); +} + void DownloadListWidgetCompactDelegate::issueRestoreToView() { emit restoreDownload(m_ContextIndex.row()); @@ -305,7 +314,10 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem menu.addAction(tr("Install"), this, SLOT(issueInstall())); if (m_Manager->isInfoIncomplete(m_ContextIndex.row())) { menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfo())); + }else { + menu.addAction(tr("Visit on Nexus"), this, SLOT(issueVisitOnNexus())); } + menu.addSeparator(); menu.addAction(tr("Delete"), this, SLOT(issueDelete())); if (hidden) { menu.addAction(tr("Un-Hide"), this, SLOT(issueRestoreToView())); @@ -324,15 +336,15 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem } menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted())); menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll())); - if (!hidden) { - menu.addSeparator(); - menu.addAction(tr("Hide Installed..."), this, SLOT(issueRemoveFromViewCompleted())); - menu.addAction(tr("Hide All..."), this, SLOT(issueRemoveFromViewAll())); - } - if (hidden) { - menu.addSeparator(); - menu.addAction(tr("Un-Hide All..."), this, SLOT(issueRestoreToViewAll())); - } + if (!hidden) { + menu.addSeparator(); + menu.addAction(tr("Hide Installed..."), this, SLOT(issueRemoveFromViewCompleted())); + menu.addAction(tr("Hide All..."), this, SLOT(issueRemoveFromViewAll())); + } + if (hidden) { + menu.addSeparator(); + menu.addAction(tr("Un-Hide All..."), this, SLOT(issueRestoreToViewAll())); + } menu.exec(mouseEvent->globalPos()); event->accept(); @@ -345,4 +357,3 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem return QItemDelegate::editorEvent(event, model, option, index); } - diff --git a/src/downloadlistwidgetcompact.h b/src/downloadlistwidgetcompact.h index df1a5f58..28a376eb 100644 --- a/src/downloadlistwidgetcompact.h +++ b/src/downloadlistwidgetcompact.h @@ -35,7 +35,7 @@ class DownloadListWidgetCompact; class DownloadListWidgetCompact : public QWidget { Q_OBJECT - + public: explicit DownloadListWidgetCompact(QWidget *parent = 0); ~DownloadListWidgetCompact(); @@ -69,6 +69,7 @@ signals: void cancelDownload(int index); void pauseDownload(int index); void resumeDownload(int index); + void visitOnNexus(int index); protected: @@ -87,7 +88,8 @@ private slots: void issueDelete(); void issueRemoveFromView(); void issueRestoreToView(); - void issueRestoreToViewAll(); + void issueRestoreToViewAll(); + void issueVisitOnNexus(); void issueCancel(); void issuePause(); void issueResume(); @@ -120,4 +122,3 @@ private: }; #endif // DOWNLOADLISTWIDGETCOMPACT_H - diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 93a46d78..0341152a 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -36,6 +36,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -607,7 +608,7 @@ void DownloadManager::removeFile(int index, bool deleteFile) if(!download->m_Hidden) metaSettings.setValue("removed", true); } - + endDisableDirWatcher(); } @@ -651,7 +652,7 @@ void DownloadManager::restoreDownload(int index) index = 0; for (QVector::const_iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end(); ++iter ) { - + if ((*iter)->m_State >= minState) { restoreDownload(index); } @@ -876,6 +877,34 @@ void DownloadManager::queryInfo(int index) setState(info, STATE_FETCHINGMODINFO); } +void DownloadManager::visitOnNexus(int index) +{ + if ((index < 0) || (index >= m_ActiveDownloads.size())) { + reportError(tr("VisitNexus: invalid download index %1").arg(index)); + return; + } + DownloadInfo *info = m_ActiveDownloads[index]; + + if (info->m_FileInfo->repository != "Nexus") { + qWarning("Visiting mod page is currently only possible with Nexus"); + return; + } + + if (info->m_State < DownloadManager::STATE_READY) { + // UI shouldn't allow this + return; + } + int modID = info->m_FileInfo->modID; + + QString gameName = info->m_FileInfo->gameName; + if (modID > 0) { + QDesktopServices::openUrl(QUrl(m_NexusInterface->getModURL(modID, gameName))); + } + else { + emit showMessage(tr("Nexus ID for this Mod is unknown")); + } +} + int DownloadManager::numTotalDownloads() const { @@ -1756,4 +1785,4 @@ void DownloadManager::checkDownloadTimeout() resumeDownload(i); } } -} \ No newline at end of file +} diff --git a/src/downloadmanager.h b/src/downloadmanager.h index aa859b13..24bc6d7f 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -435,6 +435,8 @@ public slots: void queryInfo(int index); + void visitOnNexus(int index); + void nxmDescriptionAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); void nxmFilesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a428d5b3..7ddd35c5 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -254,7 +254,7 @@ MainWindow::MainWindow(QSettings &initSettings actionToToolButton(ui->actionHelp); createHelpWidget(); - + for (QAction *action : ui->toolBar->actions()) { if (action->isSeparator()) { // insert spacers @@ -400,7 +400,7 @@ MainWindow::MainWindow(QSettings &initSettings new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Enter), this, SLOT(openExplorer_activated())); new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return), this, SLOT(openExplorer_activated())); - + new QShortcut(QKeySequence::Refresh, this, SLOT(refreshProfile_activated())); @@ -2336,9 +2336,11 @@ void MainWindow::removeMod_clicked() tr("Remove the following mods?
    %1
").arg(mods), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { // use mod names instead of indexes because those become invalid during the removal + DownloadManager::startDisableDirWatcher(); for (QString name : modNames) { m_OrganizerCore.modList()->removeRowForce(ModInfo::getIndex(name), QModelIndex()); } + DownloadManager::endDisableDirWatcher(); } } else { m_OrganizerCore.modList()->removeRow(m_ContextRow, QModelIndex()); @@ -2500,7 +2502,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, connect(&dialog, SIGNAL(modOpenPrev(int)), this, SLOT(modOpenPrev(int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); connect(&dialog, SIGNAL(endorseMod(ModInfo::Ptr)), this, SLOT(endorseMod(ModInfo::Ptr))); - + //Open the tab first if we want to use the standard indexes of the tabs. if (tab != -1) { dialog.openTab(tab); @@ -2687,7 +2689,7 @@ void MainWindow::openExplorer_activated() QModelIndex idx = selection->currentIndex(); QString fileName = idx.data().toString(); - + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); std::vector flags = modInfo->getFlags(); @@ -4239,6 +4241,7 @@ void MainWindow::updateDownloadListDelegate() connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), &m_OrganizerCore, SLOT(installDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(queryInfo(int)), m_OrganizerCore.downloadManager(), SLOT(queryInfo(int))); + connect(ui->downloadView->itemDelegate(), SIGNAL(visitOnNexus(int)), m_OrganizerCore.downloadManager(), SLOT(visitOnNexus(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(removeDownload(int, bool)), m_OrganizerCore.downloadManager(), SLOT(removeDownload(int, bool))); connect(ui->downloadView->itemDelegate(), SIGNAL(restoreDownload(int)), m_OrganizerCore.downloadManager(), SLOT(restoreDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(cancelDownload(int)), m_OrganizerCore.downloadManager(), SLOT(cancelDownload(int))); -- cgit v1.3.1 From f09ec53fcfc83f2647bf4d04cfa280f172ae0ed7 Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 8 Jun 2018 18:05:01 -0500 Subject: Updated strings --- src/organizer_en.ts | 737 ++++++++++++++++++++++++++++------------------------ 1 file changed, 394 insertions(+), 343 deletions(-) (limited to 'src') diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 1b9c2eeb..60fd66e7 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -326,27 +326,27 @@ p, li { white-space: pre-wrap; } - - - + + + Done - Double Click to install - - + + Paused - Double Click to resume - - + + Installed - Double Click to re-install - - + + Uninstalled - Double Click to re-install @@ -408,493 +408,539 @@ p, li { white-space: pre-wrap; } - - - - + + + + + Are you sure? - + + This will permanently delete the selected download. + + + + This will remove all finished downloads from this list and from disk. - + This will remove all installed downloads from this list and from disk. - + This will permanently remove all finished downloads from this list (but NOT from disk). - + This will permanently remove all installed downloads from this list (but NOT from disk). - + Install - + Query Info - + + Visit on Nexus + + + + Delete - + Un-Hide - + Hide - + Cancel - + Pause - + Remove - + Resume - + Delete Installed... - + Delete All... - + Hide Installed... - + Hide All... + + + Un-Hide All... + +
DownloadListWidgetDelegate - + < game %1 mod %2 file %3 > - + Pending - + Fetching Info 1 - + Fetching Info 2 - - - - + + Are you sure? - + This will remove all finished downloads from this list and from disk. - + + + + Delete Files? + + + + + This will permanently delete the selected download. + + + + This will remove all installed downloads from this list and from disk. - + This will remove all finished downloads from this list (but NOT from disk). - + This will remove all installed downloads from this list (but NOT from disk). - + Install - + Query Info - + + Visit on Nexus + + + + Delete - + Un-Hide - + Hide - + Cancel - + Pause - + Remove - + Resume - + Delete Installed... - + Delete All... - + Hide Installed... - + Hide All... + + + Un-Hide All... + + DownloadManager - + failed to rename "%1" to "%2" - + Memory allocation error (in refreshing directory). - + failed to download %1: could not open output file: %2 - + Download again? - + A file with the same name has already been downloaded. Do you want to download it again? The new file will receive a different name. - + Wrong Game - + The download link is for a mod for "%1" but this instance of MO has been set up for "%2". - - + + Already Started - + A download for this mod file has already been queued. - + There is already a download started for this file (mod: %1, file: %2). - - + + remove: invalid download index %1 - + failed to delete %1 - + failed to delete meta file for %1 - + restore: invalid download index: %1 - + cancel: invalid download index %1 - + pause: invalid download index %1 - + resume: invalid download index %1 - + resume (int): invalid download index %1 - + No known download urls. Sorry, this download can't be resumed. - + query: invalid download index %1 - + Please enter the nexus mod id - + Mod ID: - + Please select the source game code for %1 - + + VisitNexus: invalid download index %1 + + + + + Nexus ID for this Mod is unknown + + + + get pending: invalid download index %1 - + get path: invalid download index %1 - + Main - + Update - + Optional - + Old - + Misc - + Unknown - + display name: invalid download index %1 - + file name: invalid download index %1 - + file time: invalid download index %1 - + file size: invalid download index %1 - + progress: invalid download index %1 - + state: invalid download index %1 - + infocomplete: invalid download index %1 - - + + mod id: invalid download index %1 - + ishidden: invalid download index %1 - + file info: invalid download index %1 - + mark installed: invalid download index %1 - + mark uninstalled: invalid download index %1 - + Memory allocation error (in processing progress event). - + Memory allocation error (in processing downloaded data). - + Information updated - - + + No matching file found on Nexus! Maybe this file is no longer available or it was renamed? - + No file on Nexus matches the selected file by name. Please manually choose the correct one. - + No download server available. Please try again later. - + Failed to request file info from nexus: %1 - + Warning: Content type is: %1 - + Download header content length: %1 downloaded file size: %2 - + Download failed: %1 (%2) - + We were unable to download the file due to errors after four retries. There may be an issue with the Nexus servers. - + failed to re-open %1 @@ -1642,8 +1688,8 @@ p, li { white-space: pre-wrap; } - - + + Refresh @@ -1817,7 +1863,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1828,7 +1874,7 @@ p, li { white-space: pre-wrap; } - + No Problems @@ -1858,7 +1904,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -1883,688 +1929,693 @@ Right now this has very limited functionality - + Toolbar - + Desktop - + Start Menu - + There is no supported sort mechanism for this game. You will probably have to use a third-party tool. - + Problems - + There are potential problems with your setup - + Everything seems to be in order - + Help on UI - + Documentation Wiki - + Report Issue - + Tutorials - + About - + About Qt - + Name - + Please enter a name for the new profile - + failed to create profile: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress - + There are still downloads in progress, do you really want to quit? - + Plugin "%1" failed: %2 - + Plugin "%1" failed - + Browse Mod Page - + Also in: <br> - + No conflict - + <Edit...> - + This bsa is enabled in the ini file so it may be required! - + Activating Network Proxy - + Notice: Your current MO version (%1) is lower than the previous version (%2).<br>The GUI may not downgrade gracefully, so you may experience oddities.<br>However, there should be no serious issues. - + Choose Mod - + Mod Archive - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - + failed to spawn notepad.exe: %1 - + failed to change origin name: %1 - + failed to move "%1" from mod "%2" to "%3": %4 - + <Contains %1> - + <Checked> - + <Unchecked> - + <Update> - + <Managed by MO> - + <Managed outside MO> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - + You need to be logged in with Nexus to resume a download - - + + You need to be logged in with Nexus to endorse - + Failed to display overwrite dialog: %1 - + Nexus ID for this Mod is unknown - + Web page for this mod is unknown - - - + + + Create Mod... - + This will create an empty mod. Please enter a name: - - + + A mod with this name already exists - + This will move all files from overwrite into a new, regular mod. Please enter a name: - - + + Are you sure? - + About to recursively delete: - + Not logged in, endorsement information will be wrong - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Export to csv - + CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet. You can also use online editors and converters instead. - + Select what mods you want export: - + All installed mods - + Only active (checked) mods from your current profile - + All currently visible mods in the mod list - + Choose what Columns to export: - + Mod_Priority - + Mod_Name - + Mod_Status - + Primary_Category - + Nexus_ID - + Mod_Nexus_URL - + Mod_Version - + Install_Date - + Download_File_Name - + export failed: %1 - + Open Game folder - + Open MyGames folder - + Open Instance folder - + Open Profile folder - + Open Downloads folder - + Open MO2 Install folder - + + Open MO2 Plugins folder + + + + Open MO2 Logs folder - + Install Mod... - + Create empty mod - + Enable all visible - + Disable all visible - + Check all for update - + Export to csv... - + All Mods - + Sync to Mods... - + Clear Overwrite... - - + + Open in explorer - + Restore Backup - + Remove Backup... - + Change Categories - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... - + Reinstall Mod - + Remove Mod... - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Mark as converted/working - + Visit on Nexus - + Visit web page - + Information... - - + + Exception: - - + + Unknown exception - + <All> - + <Multiple> - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -2572,12 +2623,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -2585,319 +2636,319 @@ You can also use online editors and converters instead. - + failed to remove %1 - + failed to create %1 - + Can't change download directory while downloads are in progress! - + failed to write to file %1 - + %1 written - + Select binary - + Binary - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Thank you! - + Thank you for your endorsement! - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - - + + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Extract... - + This will restart MO, continue? - + Edit Categories... - + Deselect filter - + Remove - + Enable all - + Disable all - + Unlock load order - + Lock load order - + depends on missing "%1" - + incompatible with "%1" - + Please wait while LOOT is running - + loot failed. Exit code was: %1 - + failed to start loot - + failed to run loot: %1 - + Errors occured - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of modlist created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file @@ -5073,18 +5124,18 @@ If the folder was still in use, restart MO and try again. - + Please use "Help" from the toolbar to get usage instructions to all elements - - + + <Manage...> - + failed to parse profile %1: %2 -- cgit v1.3.1 From cacf7e05428fa7ff8d3c6d1d5413460f21395d1c Mon Sep 17 00:00:00 2001 From: Lost Dragonist Date: Fri, 8 Jun 2018 22:38:43 -0500 Subject: Allow sorting by "source game" column --- src/modlistsortproxy.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 435967c9..88084ae1 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -208,6 +208,16 @@ bool ModListSortProxy::lessThan(const QModelIndex &left, if (leftTime != rightTime) return leftTime < rightTime; } break; + case ModList::COL_GAME: { + if (leftMod->getGameName() != rightMod->getGameName()) { + lt = leftMod->getGameName() < rightMod->getGameName(); + } + else { + int comp = QString::compare(leftMod->name(), rightMod->name(), Qt::CaseInsensitive); + if (comp != 0) + lt = comp < 0; + } + } break; case ModList::COL_PRIORITY: { // nop, already compared by priority } break; -- cgit v1.3.1 From 0c4b1e9a928236542703a50cbe2c4b2d216feb0e Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sat, 9 Jun 2018 10:44:23 -0700 Subject: Prevent instances from ending in a space Windows directories are not allowed to end in a space. Allowing this creates a broken directory that is difficult for the user to remove. --- src/instancemanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 43ae152b..2b718cb2 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -146,7 +146,7 @@ QString InstanceManager::queryInstanceName(const QStringList &instanceList) cons if (dialog.exec() == QDialog::Rejected) { throw MOBase::MyException(QObject::tr("Canceled")); } - instanceId = dialog.textValue().replace(QRegExp("[^0-9a-zA-Z ]"), ""); + instanceId = dialog.textValue().replace(QRegExp("[^0-9a-zA-Z ]"), "").remove(QRegExp("( )*$")); bool alreadyExists=false; for (const QString &instance : instanceList) { -- cgit v1.3.1 From b6aa12b323a81f4ac8e1bb76c2fea780a1698d97 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 19 Jun 2018 23:13:06 +0200 Subject: Fix for downloads getting insta-completed if there isn't enough bandwidth for them to procede. Now they will instead remain in the download state and continue once the bandwidth is available. There is much room for improvement here but for now I guess this will avoid MO simply stopping the download. --- src/downloadmanager.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 0341152a..6ed93ec1 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -785,7 +785,13 @@ void DownloadManager::resumeDownloadInt(int index) return; } - if (info->isPausedState()) { + if (info->isPausedState() || info->m_State == STATE_PAUSING) { + if (info->m_State == STATE_PAUSING) { + if (info->m_Output.isOpen()) { + info->m_Output.write(info->m_Reply->readAll()); + setState(info, STATE_PAUSED); + } + } if ((info->m_Urls.size() == 0) || ((info->m_Urls.size() == 1) && (info->m_Urls[0].size() == 0))) { emit showMessage(tr("No known download urls. Sorry, this download can't be resumed.")); @@ -1662,6 +1668,7 @@ void DownloadManager::downloadFinished(int index) } else if (info->m_State == STATE_PAUSING) { if (info->m_Output.isOpen()) { info->m_Output.write(info->m_Reply->readAll()); + setState(info, STATE_PAUSED); } } @@ -1673,7 +1680,7 @@ void DownloadManager::downloadFinished(int index) if (error) emit showMessage(tr("We were unable to download the file due to errors after four retries. There may be an issue with the Nexus servers.")); emit update(-1); - } else if (info->isPausedState()) { + } else if (info->isPausedState() || info->m_State == STATE_PAUSING) { info->m_Output.close(); createMetaFile(info); emit update(index); -- cgit v1.3.1 From 30f170c10d7838a855524c5466eb9d7ea70d34db Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sat, 16 Jun 2018 23:18:25 -0500 Subject: Extend mod backups and load order backups to a maximum of 10 --- src/mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7ddd35c5..fc666a49 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4971,7 +4971,7 @@ bool MainWindow::createBackup(const QString &filePath, const QDateTime &time) QString outPath = filePath + "." + time.toString(PATTERN_BACKUP_DATE); if (shellCopy(QStringList(filePath), QStringList(outPath), this)) { QFileInfo fileInfo(filePath); - removeOldFiles(fileInfo.absolutePath(), fileInfo.fileName() + PATTERN_BACKUP_GLOB, 3, QDir::Name); + removeOldFiles(fileInfo.absolutePath(), fileInfo.fileName() + PATTERN_BACKUP_GLOB, 10, QDir::Name); return true; } else { return false; -- cgit v1.3.1 From b7d6bbb4513028340a2720070c7a7a968ded1681 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 20 Jun 2018 19:33:56 +0200 Subject: Fix for downloads getting stuck after pausing them. Disabled timer check as it appeared to terminate some downloads early. --- src/downloadlistwidget.cpp | 6 +++--- src/downloadlistwidgetcompact.cpp | 6 +++--- src/downloadmanager.cpp | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index 75470056..5ab5d397 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -127,7 +127,7 @@ void DownloadListWidgetDelegate::paintRegularDownload(int downloadIndex) const m_NameLabel->setText(name); m_SizeLabel->setText(sizeFormat(m_Manager->getFileSize(downloadIndex) )); DownloadManager::DownloadState state = m_Manager->getState(downloadIndex); - if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) { + if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR) || (state == DownloadManager::STATE_PAUSING)) { QPalette labelPalette; m_InstallLabel->setVisible(true); m_Progress->setVisible(false); @@ -332,7 +332,7 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel * QModelIndex sourceIndex = qobject_cast(model)->mapToSource(index); if (m_Manager->getState(sourceIndex.row()) >= DownloadManager::STATE_READY) { emit installDownload(sourceIndex.row()); - } else if (m_Manager->getState(sourceIndex.row()) >= DownloadManager::STATE_PAUSED) { + } else if ((m_Manager->getState(sourceIndex.row()) >= DownloadManager::STATE_PAUSED) || (m_Manager->getState(sourceIndex.row()) == DownloadManager::STATE_PAUSING)) { emit resumeDownload(sourceIndex.row()); } return true; @@ -364,7 +364,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) || (state == DownloadManager::STATE_ERROR)) { + } else if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR) || (state == DownloadManager::STATE_PAUSING)) { 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 cdce8d7f..99cbd7cb 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -114,7 +114,7 @@ void DownloadListWidgetCompactDelegate::paintRegularDownload(int downloadIndex) m_SizeLabel->setText(QString::number(m_Manager->getFileSize(downloadIndex) / 1048576)); } - if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) { + if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR) || (state == DownloadManager::STATE_PAUSING)) { m_DoneLabel->setVisible(true); m_Progress->setVisible(false); m_DoneLabel->setText(QString("%1").arg(tr("Paused"))); @@ -297,7 +297,7 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem QModelIndex sourceIndex = qobject_cast(model)->mapToSource(index); if (m_Manager->getState(sourceIndex.row()) >= DownloadManager::STATE_READY) { emit installDownload(sourceIndex.row()); - } else if (m_Manager->getState(sourceIndex.row()) >= DownloadManager::STATE_PAUSED) { + } else if ((m_Manager->getState(sourceIndex.row()) >= DownloadManager::STATE_PAUSED) || (m_Manager->getState(sourceIndex.row()) == DownloadManager::STATE_PAUSING)) { emit resumeDownload(sourceIndex.row()); } return true; @@ -327,7 +327,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) || (state == DownloadManager::STATE_ERROR)) { + } else if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR) || (state == DownloadManager::STATE_PAUSING)) { 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 6ed93ec1..ecbcc03b 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -206,7 +206,7 @@ DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent { connect(&m_DirWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); m_TimeoutTimer.setSingleShot(false); - connect(&m_TimeoutTimer, SIGNAL(timeout()), this, SLOT(checkDownloadTimeout())); + //connect(&m_TimeoutTimer, SIGNAL(timeout()), this, SLOT(checkDownloadTimeout())); m_TimeoutTimer.start(5 * 1000); } @@ -1668,8 +1668,8 @@ void DownloadManager::downloadFinished(int index) } else if (info->m_State == STATE_PAUSING) { if (info->m_Output.isOpen()) { info->m_Output.write(info->m_Reply->readAll()); - setState(info, STATE_PAUSED); } + setState(info, STATE_PAUSED); } if (info->m_State == STATE_CANCELED || (info->m_Tries == 0 && error)) { -- cgit v1.3.1 From 2d49ba2bef029d753c1afb923be887c7276d94f3 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Thu, 21 Jun 2018 18:54:20 +0200 Subject: Add open in explorer button to Overwrite dialog. --- src/overwriteinfodialog.cpp | 4 ++++ src/overwriteinfodialog.h | 1 + src/overwriteinfodialog.ui | 7 +++++++ 3 files changed, 12 insertions(+) (limited to 'src') diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp index 3bb67354..1fe3f645 100644 --- a/src/overwriteinfodialog.cpp +++ b/src/overwriteinfodialog.cpp @@ -261,6 +261,10 @@ void OverwriteInfoDialog::createDirectoryTriggered() ui->filesView->edit(newIndex); } +void OverwriteInfoDialog::on_explorerButton_clicked() +{ + ::ShellExecuteW(nullptr, L"explore", ToWString(m_ModInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); +} void OverwriteInfoDialog::on_filesView_customContextMenuRequested(const QPoint &pos) { diff --git a/src/overwriteinfodialog.h b/src/overwriteinfodialog.h index 7d44c9e8..4b731736 100644 --- a/src/overwriteinfodialog.h +++ b/src/overwriteinfodialog.h @@ -56,6 +56,7 @@ private slots: void openTriggered(); void createDirectoryTriggered(); + void on_explorerButton_clicked(); void on_filesView_customContextMenuRequested(const QPoint &pos); private: diff --git a/src/overwriteinfodialog.ui b/src/overwriteinfodialog.ui index 5e5986ca..8a09ce59 100644 --- a/src/overwriteinfodialog.ui +++ b/src/overwriteinfodialog.ui @@ -14,6 +14,13 @@ Overwrite + + + + Open in Explorer + + + -- cgit v1.3.1 From 1f9103c24e1a9bc929507bd67814df3973c9e1db Mon Sep 17 00:00:00 2001 From: Al12rs Date: Fri, 22 Jun 2018 02:47:58 +0200 Subject: Added the path of the managed game in the settings menu under the path tab, it's read only for now as allowing the user to change it would probably require a lot more work. --- src/settings.cpp | 3 +- src/settings.h | 1 + src/settingsdialog.ui | 478 +++++++++++++++++++++++++++----------------------- 3 files changed, 257 insertions(+), 225 deletions(-) (limited to 'src') diff --git a/src/settings.cpp b/src/settings.cpp index 2bcf2d02..db4ea565 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -712,9 +712,10 @@ Settings::PathsTab::PathsTab(Settings *parent, SettingsDialog &dialog) , m_cacheDirEdit(m_dialog.findChild("cacheDirEdit")) , m_profilesDirEdit(m_dialog.findChild("profilesDirEdit")) , m_overwriteDirEdit(m_dialog.findChild("overwriteDirEdit")) + , m_managedGameDirEdit(m_dialog.findChild("managedGameDirEdit")) { m_baseDirEdit->setText(m_parent->getBaseDirectory()); - + m_managedGameDirEdit->setText(m_parent->m_GamePlugin->gameDirectory().absoluteFilePath(m_parent->m_GamePlugin->binaryName())); QString basePath = parent->getBaseDirectory(); QDir baseDir(basePath); for (const auto &dir : { diff --git a/src/settings.h b/src/settings.h index 76ab55da..c49edfcb 100644 --- a/src/settings.h +++ b/src/settings.h @@ -403,6 +403,7 @@ private: QLineEdit *m_cacheDirEdit; QLineEdit *m_profilesDirEdit; QLineEdit *m_overwriteDirEdit; + QLineEdit *m_managedGameDirEdit; }; class DiagnosticsTab : public SettingsTab diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 85688cb8..374e1b84 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -17,7 +17,7 @@ - 0 + 1 @@ -170,26 +170,23 @@ If you use pre-releases, never contact me directly by e-mail or via private mess - - + + - Base Directory + ... - - + + - ... + ... - - - @@ -197,12 +194,8 @@ If you use pre-releases, never contact me directly by e-mail or via private mess - - - - Overwrite - - + + @@ -211,37 +204,57 @@ If you use pre-releases, never contact me directly by e-mail or via private mess - - + + - ... + Overwrite + + + + + + + Directory where downloads are stored. + + + Directory where downloads are stored. - - + + - Downloads + ... - - + + - ... + Downloads - - - - Directory where mods are stored. + + + + Qt::Vertical - - Directory where mods are stored. Please note that changing this will break all associations of profiles with mods that don't exist in the new location (with the same name). + + + 20 + 40 + + + + + + + + Profiles @@ -258,20 +271,16 @@ If you use pre-releases, never contact me directly by e-mail or via private mess - - + + + + + - Directory where downloads are stored. + Directory where mods are stored. - Directory where downloads are stored. - - - - - - - Mods + Directory where mods are stored. Please note that changing this will break all associations of profiles with mods that don't exist in the new location (with the same name). @@ -282,18 +291,25 @@ If you use pre-releases, never contact me directly by e-mail or via private mess - - + + + + false + + + true + + - - + + - Profiles + Mods - - + + Qt::Vertical @@ -305,15 +321,29 @@ If you use pre-releases, never contact me directly by e-mail or via private mess + + + + Managed Game + + + + + + + Base Directory + + + + + + + Use %BASE_DIR% to refer to the Base Directory. + + + - - - - Use %BASE_DIR% to refer to the Base Directory. - - - @@ -336,7 +366,7 @@ If you use pre-releases, never contact me directly by e-mail or via private mess - + Nexus @@ -890,8 +920,8 @@ tl;dr-version: If Nexus-features don't work, insert the current version number o - - false + + false Enforces that inactive ESPs and ESMs are never loaded. @@ -986,187 +1016,187 @@ For the other games this is not a sufficient replacement for AI! - - Diagnostics - - - - - - - - Log Level - - - - - - - Decides the amount of data printed to "ModOrganizer.log" - - - + + Diagnostics + + + + + + + + Log Level + + + + + + + Decides the amount of data printed to "ModOrganizer.log" + + + Decides the amount of data printed to "ModOrganizer.log". "Debug" produces very useful information for finding problems. There is usually no noteworthy performance impact but the file may become rather large. If this is a problem you may prefer the "Info" level for regluar use. On the "Error" level the log file usually remains empty. - - - - Debug - - - - - Info (recommended) - - - - - Warning - - - - - Error - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Crash Dumps - - - - - - - Decides which type of crash dumps are collected when injected processes crash. - - - + + + + Debug + + + + + Info (recommended) + + + + + Warning + + + + + Error + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Crash Dumps + + + + + + + Decides which type of crash dumps are collected when injected processes crash. + + + Decides which type of crash dumps are collected when injected processes crash. "None" Disables the generation of crash dumps by MO. "Mini" Default level which generates small dumps (only stack traces). "Data" Much larger dumps with additional information which may be need (also data segments). "Full" Even larger dumps with a full memory dump of the process. - - - - None - - - - - Mini (recommended) - - - - - Data - - - - - Full - - - - - - - - - - - - Max Dumps To Keep - - - - - - - Qt::Horizontal - - - - 60 - 20 - - - - - - - - Maximum number of crash dumps to keep on disk. Use 0 for unlimited. - - - + + + + None + + + + + Mini (recommended) + + + + + Data + + + + + Full + + + + + + + + + + + + Max Dumps To Keep + + + + + + + Qt::Horizontal + + + + 60 + 20 + + + + + + + + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. + + + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. - Set "Crash Dumps" above to None to disable crash dump collection. + Set "Crash Dumps" above to None to disable crash dump collection. - - - - - - - - - + + + + + + + + + Hint: right click link and copy link location + + + Logs and crash dumps are stored under your current instance in the <a href="LOGS_FULL_PATH">LOGS_DIR</a> and <a href="DUMPS_FULL_PATH">DUMPS_DIR</a> folders. Sending logs and/or crash dumps to the developers can help investigate issues. It is recommended to compress large log and dmp files before sending. - - - true - - - Hint: right click link and copy link location - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 232 - - - - - - + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 232 + + + + + + -- cgit v1.3.1 From 181de232f43941b5ffefbccca78a6076e1e854a9 Mon Sep 17 00:00:00 2001 From: Silarn Date: Mon, 25 Jun 2018 20:50:49 -0500 Subject: Potential fix for corrupted downloads --- src/downloadmanager.cpp | 5 +++-- src/downloadmanager.h | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index ecbcc03b..6d8658c9 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1251,6 +1251,7 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) try { DownloadInfo *info = findDownload(this->sender(), &index); if (info != nullptr) { + info->m_HasData = true; if (info->m_State == STATE_CANCELING) { setState(info, STATE_CANCELED); } else if (info->m_State == STATE_PAUSING) { @@ -1637,7 +1638,7 @@ void DownloadManager::downloadFinished(int index) if (info != nullptr) { QNetworkReply *reply = info->m_Reply; QByteArray data; - if (reply->isOpen()) { + if (reply->isOpen() && info->m_HasData) { data = reply->readAll(); info->m_Output.write(data); } @@ -1666,7 +1667,7 @@ void DownloadManager::downloadFinished(int index) if (info->m_State == STATE_CANCELING) { setState(info, STATE_CANCELED); } else if (info->m_State == STATE_PAUSING) { - if (info->m_Output.isOpen()) { + if (info->m_Output.isOpen() && info->m_HasData) { info->m_Output.write(info->m_Reply->readAll()); } setState(info, STATE_PAUSED); diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 24bc6d7f..fa3764b7 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -78,6 +78,7 @@ private: qint64 m_PreResumeSize; std::pair m_Progress; std::tuple m_SpeedDiff; + bool m_HasData; DownloadState m_State; int m_CurrentUrl; QStringList m_Urls; @@ -115,7 +116,7 @@ private: private: static unsigned int s_NextDownloadID; private: - DownloadInfo() : m_TotalSize(0), m_ReQueried(false), m_Hidden(false), m_SpeedDiff(std::tuple(0,0,0,0,0)) {} + DownloadInfo() : m_TotalSize(0), m_ReQueried(false), m_Hidden(false), m_SpeedDiff(std::tuple(0,0,0,0,0)), m_HasData(false) {} }; public: -- cgit v1.3.1 From 5efddbd5156f914de9406775f06111b899060177 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Mon, 16 Jul 2018 14:18:25 +0200 Subject: Chnged download speed to be a little more consistant in display, to avoid the progrss bar jumping around all the time. Chnaged the kb->MB jumps to happen at 1000 instead of 1024 to avoid going in the 4 digits, but still divide by 1024 to keep units acuurate. --- src/downloadmanager.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 6d8658c9..9761fee4 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -64,7 +64,7 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const Mo info->m_DownloadID = s_NextDownloadID++; info->m_StartTime.start(); info->m_PreResumeSize = 0LL; - info->m_Progress = std::make_pair(0, "0 bytes/sec"); + info->m_Progress = std::make_pair(0, " 0.0 Bytes/s "); info->m_ResumePos = 0; info->m_FileInfo = new ModRepositoryFileInfo(*fileInfo); info->m_Urls = URLs; @@ -1277,19 +1277,19 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) double speed = (std::get<4>(info->m_SpeedDiff) * 1000.0) / (5 * 1000); QString unit; - if (speed < 1024) { - unit = "bytes/sec"; + if (speed < 1000) { + unit = "Bytes/s"; } - else if (speed < 1024 * 1024) { + else if (speed/1024 < 1000) { speed /= 1024; - unit = "kB/s"; + unit = "KB/s"; } else { speed /= 1024 * 1024; unit = "MB/s"; } - info->m_Progress.second = QString::fromLatin1("%1% - %2 %3").arg(info->m_Progress.first).arg(speed, 3, 'f', 1).arg(unit); + info->m_Progress.second = QString::fromLatin1("%1% - %2 %3").arg(info->m_Progress.first).arg(speed, 8, 'f', 1,' ').arg(unit, -8, ' '); TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal); emit update(index); -- cgit v1.3.1 From 2524d65441e99cbb14037144b8af60e1d25e1179 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Mon, 16 Jul 2018 20:56:47 +0200 Subject: Added "Open in Folder" option to downloads tab. --- src/downloadlistwidget.cpp | 10 +++++++++- src/downloadlistwidget.h | 2 ++ src/downloadlistwidgetcompact.cpp | 9 ++++++++- src/downloadlistwidgetcompact.h | 2 ++ src/downloadmanager.cpp | 25 +++++++++++++++++++++++++ src/downloadmanager.h | 2 ++ src/mainwindow.cpp | 1 + 7 files changed, 49 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index 5ab5d397..0ef408fb 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -272,6 +272,10 @@ void DownloadListWidgetDelegate::issueVisitOnNexus() emit visitOnNexus(m_ContextRow); } +void DownloadListWidgetDelegate::issueOpenInDownloadsFolder() +{ + emit openInDownloadsFolder(m_ContextRow); +} void DownloadListWidgetDelegate::issueCancel() { @@ -352,6 +356,8 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel * }else { menu.addAction(tr("Visit on Nexus"), this,SLOT(issueVisitOnNexus())); } + + menu.addAction(tr("Show in Folder"), this, SLOT(issueOpenInDownloadsFolder())); menu.addSeparator(); @@ -364,9 +370,11 @@ 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())); + menu.addAction(tr("Show in Folder"), this, SLOT(issueOpenInDownloadsFolder())); } else if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR) || (state == DownloadManager::STATE_PAUSING)) { - menu.addAction(tr("Remove"), this, SLOT(issueDelete())); + menu.addAction(tr("Delete"), this, SLOT(issueDelete())); menu.addAction(tr("Resume"), this, SLOT(issueResume())); + menu.addAction(tr("Show in Folder"), this, SLOT(issueOpenInDownloadsFolder())); } menu.addSeparator(); diff --git a/src/downloadlistwidget.h b/src/downloadlistwidget.h index e2746f3a..4dcbea99 100644 --- a/src/downloadlistwidget.h +++ b/src/downloadlistwidget.h @@ -72,6 +72,7 @@ signals: void pauseDownload(int index); void resumeDownload(int index); void visitOnNexus(int index); + void openInDownloadsFolder(int index); protected: @@ -92,6 +93,7 @@ private slots: void issueRestoreToView(); void issueRestoreToViewAll(); void issueVisitOnNexus(); + void issueOpenInDownloadsFolder(); void issueCancel(); void issuePause(); void issueResume(); diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index 99cbd7cb..1d1805ef 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -226,6 +226,11 @@ void DownloadListWidgetCompactDelegate::issueVisitOnNexus() emit visitOnNexus(m_ContextIndex.row()); } +void DownloadListWidgetCompactDelegate::issueOpenInDownloadsFolder() +{ + emit openInDownloadsFolder(m_ContextIndex.row()); +} + void DownloadListWidgetCompactDelegate::issueRestoreToView() { emit restoreDownload(m_ContextIndex.row()); @@ -317,6 +322,7 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem }else { menu.addAction(tr("Visit on Nexus"), this, SLOT(issueVisitOnNexus())); } + menu.addAction(tr("Show in Folder"), this, SLOT(issueOpenInDownloadsFolder())); menu.addSeparator(); menu.addAction(tr("Delete"), this, SLOT(issueDelete())); if (hidden) { @@ -327,11 +333,12 @@ 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())); + menu.addAction(tr("Show in Folder"), this, SLOT(issueOpenInDownloadsFolder())); } else if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR) || (state == DownloadManager::STATE_PAUSING)) { menu.addAction(tr("Remove"), this, SLOT(issueDelete())); menu.addAction(tr("Resume"), this, SLOT(issueResume())); + menu.addAction(tr("Show in Folder"), this, SLOT(issueOpenInDownloadsFolder())); } - menu.addSeparator(); } menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted())); diff --git a/src/downloadlistwidgetcompact.h b/src/downloadlistwidgetcompact.h index 28a376eb..c25b7e99 100644 --- a/src/downloadlistwidgetcompact.h +++ b/src/downloadlistwidgetcompact.h @@ -70,6 +70,7 @@ signals: void pauseDownload(int index); void resumeDownload(int index); void visitOnNexus(int index); + void openInDownloadsFolder(int index); protected: @@ -90,6 +91,7 @@ private slots: void issueRestoreToView(); void issueRestoreToViewAll(); void issueVisitOnNexus(); + void issueOpenInDownloadsFolder(); void issueCancel(); void issuePause(); void issueResume(); diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 9761fee4..1a2934a5 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -911,6 +911,31 @@ void DownloadManager::visitOnNexus(int index) } } +void DownloadManager::openInDownloadsFolder(int index) +{ + if ((index < 0) || (index >= m_ActiveDownloads.size())) { + reportError(tr("VisitNexus: invalid download index %1").arg(index)); + return; + } + QString params = "/select,\""; + QDir path = QDir(m_OutputDirectory); + if (path.exists(getFileName(index))) { + params = params + QDir::toNativeSeparators(getFilePath(index)) + "\""; + + ::ShellExecuteW(nullptr, nullptr, L"explorer", ToWString(params).c_str(), nullptr, SW_SHOWNORMAL); + return; + } + else if (path.exists(getFileName(index) + ".unfinished")) { + params = params + QDir::toNativeSeparators(getFilePath(index)+".unfinished") + "\""; + + ::ShellExecuteW(nullptr, nullptr, L"explorer", ToWString(params).c_str(), nullptr, SW_SHOWNORMAL); + return; + } + + ::ShellExecuteW(nullptr, L"explore", ToWString(QDir::toNativeSeparators(m_OutputDirectory)).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + return; +} + int DownloadManager::numTotalDownloads() const { diff --git a/src/downloadmanager.h b/src/downloadmanager.h index fa3764b7..514402ee 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -438,6 +438,8 @@ public slots: void visitOnNexus(int index); + void openInDownloadsFolder(int index); + void nxmDescriptionAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); void nxmFilesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index fc666a49..436d4d26 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4242,6 +4242,7 @@ void MainWindow::updateDownloadListDelegate() connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), &m_OrganizerCore, SLOT(installDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(queryInfo(int)), m_OrganizerCore.downloadManager(), SLOT(queryInfo(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(visitOnNexus(int)), m_OrganizerCore.downloadManager(), SLOT(visitOnNexus(int))); + connect(ui->downloadView->itemDelegate(), SIGNAL(openInDownloadsFolder(int)), m_OrganizerCore.downloadManager(), SLOT(openInDownloadsFolder(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(removeDownload(int, bool)), m_OrganizerCore.downloadManager(), SLOT(removeDownload(int, bool))); connect(ui->downloadView->itemDelegate(), SIGNAL(restoreDownload(int)), m_OrganizerCore.downloadManager(), SLOT(restoreDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(cancelDownload(int)), m_OrganizerCore.downloadManager(), SLOT(cancelDownload(int))); -- cgit v1.3.1 From 7f3dc586b0422a31d34d00fcf23b2f2922418650 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Wed, 11 Jul 2018 05:45:58 -0500 Subject: Redirect endorsements of MO2 to the Skyrim SE nexus --- src/mainwindow.cpp | 17 ++++++++++++----- src/modinfo.cpp | 6 +++--- 2 files changed, 15 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 436d4d26..01f37d75 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4204,12 +4204,16 @@ void MainWindow::on_actionUpdate_triggered() void MainWindow::on_actionEndorseMO_triggered() { + // Normally this would be the managed game but MO2 is only uploaded to the Skyrim SE site right now + IPluginGame * game = m_OrganizerCore.getGame("skyrimse"); + if (!game) return; + if (QMessageBox::question(this, tr("Endorse Mod Organizer"), tr("Do you want to endorse Mod Organizer on %1 now?").arg( - NexusInterface::instance(&m_PluginContainer)->getGameURL(m_OrganizerCore.managedGame()->gameShortName())), + NexusInterface::instance(&m_PluginContainer)->getGameURL(game->gameShortName())), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { NexusInterface::instance(&m_PluginContainer)->requestToggleEndorsement( - m_OrganizerCore.managedGame()->gameShortName(), m_OrganizerCore.managedGame()->nexusModOrganizerID(), true, this, QVariant(), QString()); + game->gameShortName(), game->nexusModOrganizerID(), true, this, QVariant(), QString()); } } @@ -4274,8 +4278,11 @@ void MainWindow::nxmUpdatesAvailable(const std::vector &modIDs, QVariant us QVariantList resultList = resultData.toList(); for (auto iter = resultList.begin(); iter != resultList.end(); ++iter) { QVariantMap result = iter->toMap(); - if (result["id"].toInt() == m_OrganizerCore.managedGame()->nexusModOrganizerID() - && result["game_id"].toInt() == m_OrganizerCore.managedGame()->nexusGameID()) { + // Normally this would be the managed game but MO2 is only uploaded to the Skyrim SE site right now + IPluginGame * game = m_OrganizerCore.getGame("skyrimse"); + if (game + && result["id"].toInt() == game->nexusModOrganizerID() + && result["game_id"].toInt() == game->nexusGameID()) { if (!result["voted_by_user"].toBool()) { ui->actionEndorseMO->setVisible(true); } @@ -4326,7 +4333,7 @@ void MainWindow::nxmEndorsementToggled(QString, int, QVariant, QVariant resultDa { if (resultData.toBool()) { ui->actionEndorseMO->setVisible(false); - QMessageBox::question(this, tr("Thank you!"), tr("Thank you for your endorsement!")); + QMessageBox::information(this, tr("Thank you!"), tr("Thank you for your endorsement!")); } if (!disconnect(sender(), SIGNAL(nxmEndorsementToggled(QString, int, QVariant, QVariant, int)), diff --git a/src/modinfo.cpp b/src/modinfo.cpp index bd4c1254..1f2520c9 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -286,9 +286,9 @@ int ModInfo::checkAllForUpdate(PluginContainer *pluginContainer, QObject *receiv int result = 0; std::vector modIDs; - //I ought to store this, it's used elsewhere - IPluginGame const *game = qApp->property("managed_game").value(); - if (game->nexusModOrganizerID()) { + // Normally this would be the managed game but MO2 is only uploaded to the Skyrim SE site right now + IPluginGame const *game = pluginContainer->managedGame("Skyrim Special Edition"); + if (game && game->nexusModOrganizerID()) { modIDs.push_back(game->nexusModOrganizerID()); checkChunkForUpdate(pluginContainer, modIDs, receiver, game->gameShortName()); modIDs.clear(); -- cgit v1.3.1 From 942e656a71f452cd3cdbca2a4264db3cc2014ae9 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Mon, 16 Jul 2018 15:00:35 -0500 Subject: Fix download index in downloadFinished An index of 0 is valid as this is a bog standard array. This resolves an instance of the warning "no download index 0". --- src/downloadmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 1a2934a5..9e523bf9 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1655,7 +1655,7 @@ void DownloadManager::nxmRequestFailed(QString gameName, int modID, int fileID, void DownloadManager::downloadFinished(int index) { DownloadInfo *info; - if (index) + if (index >= 0) info = m_ActiveDownloads[index]; else info = findDownload(this->sender(), &index); -- cgit v1.3.1 From f20a1f6a849510438ca5c9abe8fdac1dada263d2 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Mon, 16 Jul 2018 15:22:51 -0500 Subject: Allow "ignore missing data" to work with multiple mods --- src/mainwindow.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 01f37d75..1f5cef2f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2619,12 +2619,25 @@ void MainWindow::displayModInformation(int row, int tab) void MainWindow::ignoreMissingData_clicked() { - ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); - QDir(info->absolutePath()).mkdir("textures"); - info->testValid(); - connect(this, SIGNAL(modListDataChanged(QModelIndex,QModelIndex)), m_OrganizerCore.modList(), SIGNAL(dataChanged(QModelIndex,QModelIndex))); + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { + for (QModelIndex idx : selection->selectedRows()) { + int row_idx = idx.data(Qt::UserRole + 1).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(row_idx); + QDir(info->absolutePath()).mkdir("textures"); + info->testValid(); + connect(this, SIGNAL(modListDataChanged(QModelIndex, QModelIndex)), m_OrganizerCore.modList(), SIGNAL(dataChanged(QModelIndex, QModelIndex))); - emit modListDataChanged(m_OrganizerCore.modList()->index(m_ContextRow, 0), m_OrganizerCore.modList()->index(m_ContextRow, m_OrganizerCore.modList()->columnCount() - 1)); + emit modListDataChanged(m_OrganizerCore.modList()->index(row_idx, 0), m_OrganizerCore.modList()->index(row_idx, m_OrganizerCore.modList()->columnCount() - 1)); + } + } else { + ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); + QDir(info->absolutePath()).mkdir("textures"); + info->testValid(); + connect(this, SIGNAL(modListDataChanged(QModelIndex, QModelIndex)), m_OrganizerCore.modList(), SIGNAL(dataChanged(QModelIndex, QModelIndex))); + + emit modListDataChanged(m_OrganizerCore.modList()->index(m_ContextRow, 0), m_OrganizerCore.modList()->index(m_ContextRow, m_OrganizerCore.modList()->columnCount() - 1)); + } } void MainWindow::markConverted_clicked() -- cgit v1.3.1 From b4530eb84321f684e0ad764ed3cdafbbd9e4315c Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Mon, 16 Jul 2018 15:31:08 -0500 Subject: Allow "mark as converted" to work with multiple mods --- src/mainwindow.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1f5cef2f..5041f661 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2642,10 +2642,21 @@ void MainWindow::ignoreMissingData_clicked() void MainWindow::markConverted_clicked() { - ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); - info->markConverted(true); - connect(this, SIGNAL(modListDataChanged(QModelIndex, QModelIndex)), m_OrganizerCore.modList(), SIGNAL(dataChanged(QModelIndex, QModelIndex))); - emit modListDataChanged(m_OrganizerCore.modList()->index(m_ContextRow, 0), m_OrganizerCore.modList()->index(m_ContextRow, m_OrganizerCore.modList()->columnCount() - 1)); + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { + for (QModelIndex idx : selection->selectedRows()) { + int row_idx = idx.data(Qt::UserRole + 1).toInt(); + ModInfo::Ptr info = ModInfo::getByIndex(row_idx); + info->markConverted(true); + connect(this, SIGNAL(modListDataChanged(QModelIndex, QModelIndex)), m_OrganizerCore.modList(), SIGNAL(dataChanged(QModelIndex, QModelIndex))); + emit modListDataChanged(m_OrganizerCore.modList()->index(row_idx, 0), m_OrganizerCore.modList()->index(row_idx, m_OrganizerCore.modList()->columnCount() - 1)); + } + } else { + ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); + info->markConverted(true); + connect(this, SIGNAL(modListDataChanged(QModelIndex, QModelIndex)), m_OrganizerCore.modList(), SIGNAL(dataChanged(QModelIndex, QModelIndex))); + emit modListDataChanged(m_OrganizerCore.modList()->index(m_ContextRow, 0), m_OrganizerCore.modList()->index(m_ContextRow, m_OrganizerCore.modList()->columnCount() - 1)); + } } -- cgit v1.3.1 From 952f1fe8cd6dda5f97fd29fce9e7e8372093c3f4 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Mon, 16 Jul 2018 15:35:11 -0500 Subject: Allow "open in explorer" to work with multiple mods --- src/mainwindow.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5041f661..dc4ede28 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2683,9 +2683,17 @@ void MainWindow::visitWebPage_clicked() void MainWindow::openExplorer_clicked() { - ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); - - ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { + for (QModelIndex idx : selection->selectedRows()) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + ::ShellExecuteW(nullptr, L"explore", ToWString(info->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } + } + else { + ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); + ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } } void MainWindow::openExplorer_activated() -- cgit v1.3.1 From eaf3655492e34a5b613a3f75576c872f8f932d17 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Mon, 16 Jul 2018 15:35:41 -0500 Subject: Allow "ignore update" to work with multiple mods --- src/mainwindow.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index dc4ede28..19f61ae8 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3108,8 +3108,17 @@ void MainWindow::changeVersioningScheme() { } void MainWindow::ignoreUpdate() { - ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); - info->ignoreUpdate(true); + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { + for (QModelIndex idx : selection->selectedRows()) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + info->ignoreUpdate(true); + } + } + else { + ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); + info->ignoreUpdate(true); + } } void MainWindow::unignoreUpdate() -- cgit v1.3.1 From a830f3e09efe60d10dc7d97fa645a164f54994a0 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Mon, 16 Jul 2018 15:36:06 -0500 Subject: Allow "unignore update" to work with multiple mods --- src/mainwindow.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 19f61ae8..f70e66a6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3123,8 +3123,17 @@ void MainWindow::ignoreUpdate() { void MainWindow::unignoreUpdate() { - ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); - info->ignoreUpdate(false); + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { + for (QModelIndex idx : selection->selectedRows()) { + ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + info->ignoreUpdate(false); + } + } + else { + ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow); + info->ignoreUpdate(false); + } } void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, -- cgit v1.3.1 From 6552054e58d8c108c59b38335870529da78a27f1 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Mon, 9 Jul 2018 23:59:35 -0500 Subject: Detect if the Windows Event Log service is not running --- src/organizercore.cpp | 109 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 35486f98..142faabe 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -186,6 +186,86 @@ QStringList toStringList(InputIterator current, InputIterator end) return result; } +bool checkService() +{ + SC_HANDLE serviceManagerHandle = NULL; + SC_HANDLE serviceHandle = NULL; + LPSERVICE_STATUS_PROCESS serviceStatus = NULL; + LPQUERY_SERVICE_CONFIG serviceConfig = NULL; + bool serviceRunning = true; + + DWORD bytesNeeded; + + try { + serviceManagerHandle = OpenSCManager(NULL, NULL, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); + if (!serviceManagerHandle) { + qWarning("failed to open service manager (query status) (error %d)", GetLastError()); + throw 1; + } + + serviceHandle = OpenService(serviceManagerHandle, L"EventLog", SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG); + if (!serviceHandle) { + qWarning("failed to open EventLog service (query status) (error %d)", GetLastError()); + throw 2; + } + + if (QueryServiceConfig(serviceHandle, NULL, 0, &bytesNeeded) + || (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) { + qWarning("failed to get size of service config (error %d)", GetLastError()); + throw 3; + } + + DWORD serviceConfigSize = bytesNeeded; + serviceConfig = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LMEM_FIXED, serviceConfigSize); + if (!QueryServiceConfig(serviceHandle, serviceConfig, serviceConfigSize, &bytesNeeded)) { + qWarning("failed to query service config (error %d)", GetLastError()); + throw 4; + } + + if (serviceConfig->dwStartType == SERVICE_DISABLED) { + qCritical("Windows Event Log service is disabled!"); + serviceRunning = false; + } + + if (QueryServiceStatusEx(serviceHandle, SC_STATUS_PROCESS_INFO, NULL, 0, &bytesNeeded) + || (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) { + qWarning("failed to get size of service status (error %d)", GetLastError()); + throw 5; + } + + DWORD serviceStatusSize = bytesNeeded; + serviceStatus = (LPSERVICE_STATUS_PROCESS)LocalAlloc(LMEM_FIXED, serviceStatusSize); + if (!QueryServiceStatusEx(serviceHandle, SC_STATUS_PROCESS_INFO, (LPBYTE)serviceStatus, serviceStatusSize, &bytesNeeded)) { + qWarning("failed to query service status (error %d)", GetLastError()); + throw 6; + } + + if (serviceStatus->dwCurrentState != SERVICE_RUNNING) { + qCritical("Windows Event Log service is not running"); + serviceRunning = false; + } + } + catch (int e) { + UNUSED_VAR(e); + serviceRunning = false; + } + + if (serviceStatus) { + LocalFree(serviceStatus); + } + if (serviceConfig) { + LocalFree(serviceConfig); + } + if (serviceHandle) { + CloseServiceHandle(serviceHandle); + } + if (serviceManagerHandle) { + CloseServiceHandle(serviceManagerHandle); + } + + return serviceRunning; +} + OrganizerCore::OrganizerCore(const QSettings &initSettings) : m_UserInterface(nullptr) , m_PluginContainer(nullptr) @@ -1190,6 +1270,11 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, ToWString(m_Settings.getSteamAppID()).c_str()); } + QWidget *window = qApp->activeWindow(); + if ((window != nullptr) && (!window->isVisible())) { + window = nullptr; + } + // This could possibly be extracted somewhere else but it's probably for when // we have more than one provider of game registration. if ((QFileInfo( @@ -1200,16 +1285,12 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, .exists()) && (m_Settings.getLoadMechanism() == LoadMechanism::LOAD_MODORGANIZER)) { if (!testForSteam()) { - QWidget *window = qApp->activeWindow(); - if ((window != nullptr) && (!window->isVisible())) { - window = nullptr; - } if (QuestionBoxMemory::query(window, "steamQuery", binary.fileName(), tr("Start Steam?"), tr("Steam is required to be running already to correctly start the game. " "Should MO try to start steam now?"), QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::Yes) { - startSteam(qApp->activeWindow()); + startSteam(window); } } } @@ -1229,10 +1310,24 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, try { m_USVFS.updateMapping(fileMapping(profileName, customOverwrite)); } catch (const std::exception &e) { - QMessageBox::warning(qApp->activeWindow(), tr("Error"), e.what()); + QMessageBox::warning(window, tr("Error"), e.what()); return INVALID_HANDLE_VALUE; } + // Check if the Windows Event Logging service is running. For some reason, this seems to be + // critical to the successful running of usvfs. + if (!checkService()) { + if (QuestionBoxMemory::query(window, QString("eventLogService"), binary.fileName(), + tr("Windows Event Log Error"), + tr("The Windows Event Log service is disabled and/or not running. This prevents" + " USVFS from running properly. Your mods may not be working in the executable" + " that you are launching. Note that you may have to restart MO and/or your PC" + " after the service is fixed.\n\nContinue launching %1?").arg(binary.fileName()), + QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::No) { + return INVALID_HANDLE_VALUE; + } + } + QString modsPath = settings().getModDirectory(); // Check if this a request with either an executable or a working directory under our mods folder @@ -2204,4 +2299,4 @@ std::vector OrganizerCore::fileMapping( result.insert(result.end(), subRes.begin(), subRes.end()); } return result; -} +} \ No newline at end of file -- cgit v1.3.1 From 1867c20f02d44f98154955a89686fe9edf09d7f5 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Jul 2018 00:10:03 +0200 Subject: Added sortable "Size" column to the downlaods tab. --- src/downloadlist.cpp | 3 ++- src/downloadlist.h | 3 ++- src/downloadlistsortproxy.cpp | 2 ++ src/downloadlistwidget.cpp | 4 ++-- src/downloadlistwidgetcompact.cpp | 25 +++++++++++++++++++++---- 5 files changed, 29 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/downloadlist.cpp b/src/downloadlist.cpp index f13cdef1..1e31973d 100644 --- a/src/downloadlist.cpp +++ b/src/downloadlist.cpp @@ -40,7 +40,7 @@ int DownloadList::rowCount(const QModelIndex&) const int DownloadList::columnCount(const QModelIndex&) const { - return 3; + return 4; } @@ -63,6 +63,7 @@ QVariant DownloadList::headerData(int section, Qt::Orientation orientation, int switch (section) { case COL_NAME: return tr("Name"); case COL_FILETIME: return tr("Filetime"); + case COL_SIZE: return tr("Size"); default: return tr("Done"); } } else { diff --git a/src/downloadlist.h b/src/downloadlist.h index 2316dddc..e8833f0f 100644 --- a/src/downloadlist.h +++ b/src/downloadlist.h @@ -39,7 +39,8 @@ public: enum EColumn { COL_NAME = 0, COL_FILETIME, - COL_STATUS + COL_STATUS, + COL_SIZE }; public: diff --git a/src/downloadlistsortproxy.cpp b/src/downloadlistsortproxy.cpp index 2780f973..f791617a 100644 --- a/src/downloadlistsortproxy.cpp +++ b/src/downloadlistsortproxy.cpp @@ -47,6 +47,8 @@ bool DownloadListSortProxy::lessThan(const QModelIndex &left, return m_Manager->getFileTime(leftIndex) < m_Manager->getFileTime(rightIndex); } else if (left.column() == DownloadList::COL_STATUS) { return m_Manager->getState(leftIndex) < m_Manager->getState(rightIndex); + } else if(left.column() == DownloadList::COL_SIZE){ + return m_Manager->getFileSize(leftIndex) < m_Manager->getFileSize(rightIndex); } else { return leftIndex < rightIndex; } diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index 0ef408fb..1a401d10 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -82,7 +82,7 @@ void DownloadListWidgetDelegate::drawCache(QPainter *painter, const QStyleOption { QRect rect = option.rect; rect.setLeft(0); - rect.setWidth(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2)); + rect.setWidth(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2) + m_View->columnWidth(3)); painter->drawPixmap(rect, cache); } @@ -193,7 +193,7 @@ void DownloadListWidgetDelegate::paint(QPainter *painter, const QStyleOptionView return; } - m_ItemWidget->resize(QSize(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2), option.rect.height())); + m_ItemWidget->resize(QSize(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2) + m_View->columnWidth(3), option.rect.height())); int downloadIndex = index.data().toInt(); diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index 1d1805ef..ad9e0994 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -81,10 +81,27 @@ void DownloadListWidgetCompactDelegate::drawCache(QPainter *painter, const QStyl { QRect rect = option.rect; rect.setLeft(0); - rect.setWidth(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2)); + rect.setWidth(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2) + m_View->columnWidth(3)); painter->drawPixmap(rect, cache); } +QString DownloadListWidgetCompactDelegate::sizeFormat(quint64 size) const +{ + qreal calc = size; + QStringList list; + list << "KB" << "MB" << "GB" << "TB"; + + QStringListIterator i(list); + QString unit("byte(s)"); + + while (calc >= 1024.0 && i.hasNext()) + { + unit = i.next(); + calc /= 1024.0; + } + + return QString().setNum(calc, 'f', 2) + " " + unit; +} void DownloadListWidgetCompactDelegate::paintPendingDownload(int downloadIndex) const { @@ -110,8 +127,8 @@ void DownloadListWidgetCompactDelegate::paintRegularDownload(int downloadIndex) DownloadManager::DownloadState state = m_Manager->getState(downloadIndex); - if ((m_SizeLabel != nullptr) && (state >= DownloadManager::STATE_READY)) { - m_SizeLabel->setText(QString::number(m_Manager->getFileSize(downloadIndex) / 1048576)); + if ((m_SizeLabel != nullptr)) { + m_SizeLabel->setText(sizeFormat(m_Manager->getFileSize(downloadIndex))); } if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR) || (state == DownloadManager::STATE_PAUSING)) { @@ -153,7 +170,7 @@ void DownloadListWidgetCompactDelegate::paint(QPainter *painter, const QStyleOpt return; } - m_ItemWidget->resize(QSize(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2), option.rect.height())); + m_ItemWidget->resize(QSize(m_View->columnWidth(0) + m_View->columnWidth(1) + m_View->columnWidth(2) + m_View->columnWidth(3), option.rect.height())); if (index.row() % 2 == 1) { m_ItemWidget->setBackgroundRole(QPalette::AlternateBase); } else { -- cgit v1.3.1 From 9ddb26ea5100581c832920015f79de8d7366463f Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Jul 2018 00:10:30 +0200 Subject: Added size label to compact download view. --- src/downloadlistwidgetcompact.h | 1 + src/downloadlistwidgetcompact.ui | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/downloadlistwidgetcompact.h b/src/downloadlistwidgetcompact.h index c25b7e99..1fcab3e6 100644 --- a/src/downloadlistwidgetcompact.h +++ b/src/downloadlistwidgetcompact.h @@ -74,6 +74,7 @@ signals: protected: + QString sizeFormat(quint64 size) const; bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); diff --git a/src/downloadlistwidgetcompact.ui b/src/downloadlistwidgetcompact.ui index b43f1fb3..a3cf3f02 100644 --- a/src/downloadlistwidgetcompact.ui +++ b/src/downloadlistwidgetcompact.ui @@ -19,7 +19,7 @@ true - + 2 @@ -57,6 +57,26 @@ + + + + Qt::Horizontal + + + + 10 + 20 + + + + + + + + + + + -- cgit v1.3.1 From 872c33fe5592eb84a2f6c01a2e47b72602b6139e Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Jul 2018 00:49:41 +0200 Subject: Added new "Hide Uninstalled" and "Delete Uninstalled" options to downloads tab. --- src/downloadlistwidget.cpp | 20 ++++++++++++++++++++ src/downloadlistwidget.h | 2 ++ src/downloadlistwidgetcompact.cpp | 20 ++++++++++++++++++++ src/downloadlistwidgetcompact.h | 2 ++ src/downloadmanager.cpp | 8 +++++++- 5 files changed, 51 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index 1a401d10..ad694107 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -310,6 +310,15 @@ void DownloadListWidgetDelegate::issueDeleteCompleted() } } +void DownloadListWidgetDelegate::issueDeleteUninstalled() +{ + if (QMessageBox::question(nullptr, tr("Delete Files?"), + tr("This will remove all uninstalled downloads from this list and from disk."), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + emit removeDownload(-3, true); + } +} + void DownloadListWidgetDelegate::issueRemoveFromViewAll() { if (QMessageBox::question(nullptr, tr("Are you sure?"), @@ -328,6 +337,15 @@ void DownloadListWidgetDelegate::issueRemoveFromViewCompleted() } } +void DownloadListWidgetDelegate::issueRemoveFromViewUninstalled() +{ + if (QMessageBox::question(nullptr, tr("Are you sure?"), + tr("This will remove all uninstalled downloads from this list (but NOT from disk)."), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + emit removeDownload(-3, false); + } +} + bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) { @@ -380,11 +398,13 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel * menu.addSeparator(); } menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted())); + menu.addAction(tr("Delete Uninstalled..."), this, SLOT(issueDeleteUninstalled())); menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll())); if (!hidden) { menu.addSeparator(); menu.addAction(tr("Hide Installed..."), this, SLOT(issueRemoveFromViewCompleted())); + menu.addAction(tr("Hide Uninstalled..."), this, SLOT(issueRemoveFromViewUninstalled())); menu.addAction(tr("Hide All..."), this, SLOT(issueRemoveFromViewAll())); } if (hidden) { diff --git a/src/downloadlistwidget.h b/src/downloadlistwidget.h index 4dcbea99..2dd73e73 100644 --- a/src/downloadlistwidget.h +++ b/src/downloadlistwidget.h @@ -99,8 +99,10 @@ private slots: void issueResume(); void issueDeleteAll(); void issueDeleteCompleted(); + void issueDeleteUninstalled(); void issueRemoveFromViewAll(); void issueRemoveFromViewCompleted(); + void issueRemoveFromViewUninstalled(); void issueQueryInfo(); void stateChanged(int row, DownloadManager::DownloadState); diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index ad9e0994..34e31006 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -292,6 +292,15 @@ void DownloadListWidgetCompactDelegate::issueDeleteCompleted() } } +void DownloadListWidgetCompactDelegate::issueDeleteUninstalled() +{ + if (QMessageBox::question(nullptr, tr("Are you sure?"), + tr("This will remove all uninstalled downloads from this list and from disk."), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + emit removeDownload(-3, true); + } +} + void DownloadListWidgetCompactDelegate::issueRemoveFromViewAll() { if (QMessageBox::question(nullptr, tr("Are you sure?"), @@ -310,6 +319,15 @@ void DownloadListWidgetCompactDelegate::issueRemoveFromViewCompleted() } } +void DownloadListWidgetCompactDelegate::issueRemoveFromViewUninstalled() +{ + if (QMessageBox::question(nullptr, tr("Are you sure?"), + tr("This will permanently remove all uninstalled downloads from this list (but NOT from disk)."), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + emit removeDownload(-3, false); + } +} + bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) @@ -359,10 +377,12 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem menu.addSeparator(); } menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted())); + menu.addAction(tr("Delete Uninstalled..."), this, SLOT(issueDeleteUninstalled())); menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll())); if (!hidden) { menu.addSeparator(); menu.addAction(tr("Hide Installed..."), this, SLOT(issueRemoveFromViewCompleted())); + menu.addAction(tr("Hide Uninstalled..."), this, SLOT(issueRemoveFromViewUninstalled())); menu.addAction(tr("Hide All..."), this, SLOT(issueRemoveFromViewAll())); } if (hidden) { diff --git a/src/downloadlistwidgetcompact.h b/src/downloadlistwidgetcompact.h index 1fcab3e6..b1b3c617 100644 --- a/src/downloadlistwidgetcompact.h +++ b/src/downloadlistwidgetcompact.h @@ -98,8 +98,10 @@ private slots: void issueResume(); void issueDeleteAll(); void issueDeleteCompleted(); + void issueDeleteUninstalled(); void issueRemoveFromViewAll(); void issueRemoveFromViewCompleted(); + void issueRemoveFromViewUninstalled(); void issueQueryInfo(); void stateChanged(int row, DownloadManager::DownloadState); diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 1a2934a5..0c83bf92 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -690,7 +690,13 @@ void DownloadManager::removeDownload(int index, bool deleteFile) emit aboutToUpdate(); if (index < 0) { - DownloadState minState = index == -1 ? STATE_READY : STATE_INSTALLED; + DownloadState minState; + if (index == -3) { + minState = STATE_UNINSTALLED; + } + else + minState = index == -1 ? STATE_READY : STATE_INSTALLED; + index = 0; for (QVector::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end();) { if ((*iter)->m_State >= minState) { -- cgit v1.3.1 From 61b33a83519d1473b6b7a62d3d571c5097572204 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Jul 2018 12:38:17 +0200 Subject: Added feather and scroll content icon to rappresent ini files. Icon curtesy of WolfHeartAurora. --- src/resources.qrc | 1 + src/resources/contents/feather-and-scroll.png | Bin 0 -> 616 bytes 2 files changed, 1 insertion(+) create mode 100644 src/resources/contents/feather-and-scroll.png (limited to 'src') diff --git a/src/resources.qrc b/src/resources.qrc index a18baf45..f3459ea7 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -93,6 +93,7 @@ resources/contents/conversation.png resources/contents/locked-chest.png resources/contents/config.png + resources/contents/feather-and-scroll.png qt.conf diff --git a/src/resources/contents/feather-and-scroll.png b/src/resources/contents/feather-and-scroll.png new file mode 100644 index 00000000..f82694ca Binary files /dev/null and b/src/resources/contents/feather-and-scroll.png differ -- cgit v1.3.1 From 5a0b8b431c3ffd5de2f4359492854310da32405e Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Jul 2018 13:06:38 +0200 Subject: Added Contains INI files content filter and checking. --- src/modinfo.cpp | 1 + src/modinfo.h | 5 +++-- src/modinforegular.cpp | 4 ++++ src/modlist.cpp | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 1f2520c9..91f3c1a1 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -102,6 +102,7 @@ QString ModInfo::getContentTypeName(int contentType) case CONTENT_SKSE: return tr("Script Extender"); case CONTENT_SKYPROC: return tr("SkyProc Tools"); case CONTENT_MCM: return tr("MCM Data"); + case CONTENT_INI: return tr("INI files"); default: throw MyException(tr("invalid content type %1").arg(contentType)); } } diff --git a/src/modinfo.h b/src/modinfo.h index 001a78dc..4cdd7bcf 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -84,10 +84,11 @@ public: CONTENT_SCRIPT, CONTENT_SKSE, CONTENT_SKYPROC, - CONTENT_MCM + CONTENT_MCM, + CONTENT_INI }; - static const int NUM_CONTENT_TYPES = CONTENT_MCM + 1; + static const int NUM_CONTENT_TYPES = CONTENT_INI + 1; enum EHighlight { HIGHLIGHT_NONE = 0, diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index c19294f1..5b6ddcda 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -466,6 +466,10 @@ std::vector ModInfoRegular::getContents() const if (dir.entryList(QStringList() << "*.bsa" << "*.ba2").size() > 0) { m_CachedContent.push_back(CONTENT_BSA); } + //use >1 for ini files since there is meta.ini in all mods already. + if (dir.entryList(QStringList() << "*.ini").size() > 1) { + m_CachedContent.push_back(CONTENT_INI); + } ScriptExtender *extender = qApp->property("managed_game") .value() diff --git a/src/modlist.cpp b/src/modlist.cpp index bf57ef6e..aaa4ba79 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -73,6 +73,7 @@ ModList::ModList(PluginContainer *pluginContainer, QObject *parent) m_ContentIcons[ModInfo::CONTENT_SOUND] = std::make_tuple(":/MO/gui/content/sound", tr("Sound or Music")); m_ContentIcons[ModInfo::CONTENT_TEXTURE] = std::make_tuple(":/MO/gui/content/texture", tr("Textures")); m_ContentIcons[ModInfo::CONTENT_MCM] = std::make_tuple(":/MO/gui/content/menu", tr("MCM Configuration")); + m_ContentIcons[ModInfo::CONTENT_INI] = std::make_tuple(":/MO/gui/content/inifile", tr("INI files")); m_LastCheck.start(); } @@ -1109,6 +1110,7 @@ QString ModList::getColumnToolTip(int column) "Script Extender plugins" "SkyProc Patcher" "Mod Configuration Menu" + "INI files" ""); case COL_INSTALLTIME: return tr("Time this mod was installed"); default: return tr("unknown"); -- cgit v1.3.1 From 75478f68a12c89af790ba2aae127d4834c6d44a5 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Jul 2018 13:32:53 +0200 Subject: Avoid asking users if they are sure they want to close of all downlaods are paused. --- src/downloadmanager.cpp | 10 ++++++++++ src/downloadmanager.h | 7 +++++++ src/mainwindow.cpp | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 385d2246..beb498c1 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -230,6 +230,16 @@ bool DownloadManager::downloadsInProgress() return false; } +bool DownloadManager::downloadsInProgressNoPause() +{ + for (QVector::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end(); ++iter) { + if ((*iter)->m_State < STATE_READY && (*iter)->m_State != STATE_PAUSED) { + return true; + } + } + return false; +} + void DownloadManager::pauseAll() { diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 514402ee..136ecf2a 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -138,6 +138,13 @@ public: **/ bool downloadsInProgress(); + /** + * @brief determine if a download is currently in progress, does not count paused ones. + * + * @return true if there is currently a download in progress (that is not paused already). + **/ + bool downloadsInProgressNoPause(); + /** * @brief set the output directory to write to * diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f70e66a6..5999608c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -883,7 +883,7 @@ void MainWindow::closeEvent(QCloseEvent* event) { m_closing = true; - if (m_OrganizerCore.downloadManager()->downloadsInProgress()) { + if (m_OrganizerCore.downloadManager()->downloadsInProgressNoPause()) { if (QMessageBox::question(this, tr("Downloads in progress"), tr("There are still downloads in progress, do you really want to quit?"), QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Cancel) { -- cgit v1.3.1 From 35bbe0001efa9aac48c56503643b98ec03c080e7 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Jul 2018 14:37:50 +0200 Subject: Added a flag to the plugin list indicating if a certain plugin has one or more archives attached. Changed the tooltip to list the loaded archives. Fixed tool tip to have titles for subsections. --- src/pluginlist.cpp | 26 ++++++++++++++++++++++---- src/pluginlist.h | 3 ++- 2 files changed, 24 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index a6ae8fa7..9153df72 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -181,9 +181,17 @@ void PluginList::refresh(const QString &profileName try { FilesOrigin &origin = baseDirectory.getOriginByID(current->getOrigin(archive)); + QString iniPath = QFileInfo(filename).baseName() + ".ini"; bool hasIni = baseDirectory.findFile(ToWString(iniPath)).get() != nullptr; + std::set loadedArchives; + QString originPath = QString::fromWCharArray(origin.getPath().c_str()); + QDir dir(QDir::toNativeSeparators(originPath)); + for (QString filename : dir.entryList(QStringList() << QFileInfo(filename).baseName() + "*.bsa" << QFileInfo(filename).baseName() + "*.ba2")) { + loadedArchives.insert(filename); + } + QString originName = ToQString(origin.getName()); unsigned int modIndex = ModInfo::getIndex(originName); if (modIndex != UINT_MAX) { @@ -191,7 +199,7 @@ void PluginList::refresh(const QString &profileName originName = modInfo->name(); } - m_ESPs.push_back(ESPInfo(filename, forceEnabled, originName, ToQString(current->getFullPath()), hasIni)); + m_ESPs.push_back(ESPInfo(filename, forceEnabled, originName, ToQString(current->getFullPath()), hasIni, loadedArchives)); m_ESPs.rbegin()->m_Priority = -1; } catch (const std::exception &e) { reportError(tr("failed to update esp info for file %1 (source id: %2), error: %3").arg(filename).arg(current->getOrigin(archive)).arg(e.what())); @@ -888,8 +896,15 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const if (!enabledMasters.empty()) { text += "
" + tr("Enabled Masters") + ": " + SetJoin(enabledMasters, ", "); } + if (!m_ESPs[index].m_Archives.empty()) { + text += "
" + tr("Loads Archives") + ": " + SetJoin(m_ESPs[index].m_Archives, ", "); + text += "
" + tr("There are Archives connected to this plugin. " + "Their assets will be added to your game, overwriting in case of conflicts following the plugin order. " + "Loose files will always overwrite assets from Archives."); + } if (m_ESPs[index].m_HasIni) { - text += "
" + tr("There is an ini file connected to this esp. " + text += "
" + tr("Loads INI settings") + ": "; + text += "
" + tr("There is an ini file connected to this plugin. " "Its settings will be added to your game settings, overwriting in case of conflicts."); } if (m_ESPs[index].m_IsLightFlagged && !m_ESPs[index].m_IsLight) { @@ -917,6 +932,9 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const if (m_ESPs[index].m_HasIni) { result.append(":/MO/gui/attachment"); } + if (!m_ESPs[index].m_Archives.empty()) { + result.append(":/MO/gui/archive_conflict_neutral"); + } if (m_ESPs[index].m_IsLightFlagged && !m_ESPs[index].m_IsLight) { result.append(":/MO/gui/awaiting"); } @@ -1228,9 +1246,9 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, const QString &originName, const QString &fullPath, - bool hasIni) + bool hasIni, std::set archives) : m_Name(name), m_FullPath(fullPath), m_Enabled(enabled), m_ForceEnabled(enabled), - m_Priority(0), m_LoadOrder(-1), m_OriginName(originName), m_HasIni(hasIni), m_ModSelected(false) + m_Priority(0), m_LoadOrder(-1), m_OriginName(originName), m_HasIni(hasIni), m_Archives(archives), m_ModSelected(false) { try { ESP::File file(ToWString(fullPath)); diff --git a/src/pluginlist.h b/src/pluginlist.h index f6745aa8..583c7272 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -277,7 +277,7 @@ private: struct ESPInfo { - ESPInfo(const QString &name, bool enabled, const QString &originName, const QString &fullPath, bool hasIni); + ESPInfo(const QString &name, bool enabled, const QString &originName, const QString &fullPath, bool hasIni, std::set archives); QString m_Name; QString m_FullPath; bool m_Enabled; @@ -294,6 +294,7 @@ private: QString m_Author; QString m_Description; bool m_HasIni; + std::set m_Archives; std::set m_Masters; mutable std::set m_MasterUnset; bool operator < (const ESPInfo& str) const -- cgit v1.3.1 From 023cacab7fc2a9c36749fe235255fce8c4edd65b Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Jul 2018 16:26:17 +0200 Subject: Added ability to open mod information window by double cliking on plugins. --- src/mainwindow.cpp | 38 ++++++++++++++++++ src/mainwindow.h | 1 + src/mainwindow.ui | 115 +++++++++++++++++------------------------------------ src/pluginlist.cpp | 7 ++++ src/pluginlist.h | 6 +++ 5 files changed, 88 insertions(+), 79 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5999608c..c4e888ac 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2872,6 +2872,44 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index) } } +void MainWindow::on_espList_doubleClicked(const QModelIndex &index) +{ + if (!index.isValid()) { + return; + } + + if (m_OrganizerCore.pluginList()->timeElapsedSinceLastChecked() <= QApplication::doubleClickInterval()) { + // don't interpret double click if we only just checked a plugin + return; + } + + QModelIndex sourceIdx = mapToModel(m_OrganizerCore.pluginList(), index); + if (!sourceIdx.isValid()) { + return; + } + try { + + QItemSelectionModel *selection = ui->espList->selectionModel(); + + if (selection->hasSelection() && selection->selectedRows().count() == 1) { + + QModelIndex idx = selection->currentIndex(); + QString fileName = idx.data().toString(); + + + displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + // workaround to cancel the editor that might have opened because of + // selection-click + ui->espList->closePersistentEditor(index); + + + } + } + catch (const std::exception &e) { + reportError(e.what()); + } +} + bool MainWindow::populateMenuCategories(QMenu *menu, int targetID) { ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow); diff --git a/src/mainwindow.h b/src/mainwindow.h index 737a1534..773bf298 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -574,6 +574,7 @@ private slots: // ui slots void on_executablesListBox_currentIndexChanged(int index); void on_modList_customContextMenuRequested(const QPoint &pos); void on_modList_doubleClicked(const QModelIndex &index); + void on_espList_doubleClicked(const QModelIndex &index); void on_profileBox_currentIndexChanged(int index); void on_savegameList_customContextMenuRequested(const QPoint &pos); void on_startButton_clicked(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index e0aa6f36..d8873d4f 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -96,9 +96,9 @@
- - false - + + false + 0 @@ -194,7 +194,7 @@ - + Pick a module collection @@ -248,7 +248,7 @@ p, li { white-space: pre-wrap; } 16 - + @@ -265,16 +265,6 @@ p, li { white-space: pre-wrap; } - - - - - - - - - - Restore Backup... @@ -509,7 +499,7 @@ p, li { white-space: pre-wrap; } - + Qt::Horizontal @@ -521,7 +511,7 @@ p, li { white-space: pre-wrap; } - + @@ -536,12 +526,12 @@ p, li { white-space: pre-wrap; } 22 - + 95 0 - + false @@ -567,7 +557,7 @@ p, li { white-space: pre-wrap; } - + Qt::Horizontal @@ -579,14 +569,14 @@ p, li { white-space: pre-wrap; } - - - + + + 220 0 - + Qt::ClickFocus @@ -608,13 +598,13 @@ p, li { white-space: pre-wrap; } - - + + 220 0 - + Namefilter @@ -835,12 +825,12 @@ p, li { white-space: pre-wrap; } - - true - + + true + Sort - + :/MO/gui/sort:/MO/gui/sort @@ -954,6 +944,9 @@ p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This list contains the esps, esms, and esls contained in the active mods. These require their own load order. Use drag&amp;drop to modify this load order. Please note that MO will only save the load order for mods that are active/checked.<br />There is a great tool named &quot;BOSS&quot; to automatically sort these files.</span></p></body></html> + + QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + true @@ -987,6 +980,9 @@ p, li { white-space: pre-wrap; } true + + false + false @@ -1009,15 +1005,12 @@ p, li { white-space: pre-wrap; } - - - + + false + Archives - - true - 6 @@ -1032,17 +1025,7 @@ p, li { white-space: pre-wrap; } 6 - - - - - - - - - - - + @@ -1072,50 +1055,24 @@ p, li { white-space: pre-wrap; } BSAs checked here are loaded in such a way that your installation order is obeyed properly. - - QAbstractItemView::NoEditTriggers - false - + false - + false - - QAbstractItemView::NoDragDrop - - - Qt::IgnoreAction - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - + 20 - + true - + 1 - - false - - - 200 - - - - File - - diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 9153df72..c6cea045 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -77,6 +77,7 @@ PluginList::PluginList(QObject *parent) , m_FontMetrics(QFont()) { connect(this, SIGNAL(writePluginsList()), this, SLOT(generatePluginIndexes())); + m_LastCheck.start(); } PluginList::~PluginList() @@ -583,6 +584,11 @@ void PluginList::disconnectSlots() { m_PluginStateChanged.disconnect_all_slots(); } +int PluginList::timeElapsedSinceLastChecked() const +{ + return m_LastCheck.elapsed(); +} + QStringList PluginList::pluginNames() const { QStringList result; @@ -954,6 +960,7 @@ bool PluginList::setData(const QModelIndex &modIndex, const QVariant &value, int if (role == Qt::CheckStateRole) { m_ESPs[modIndex.row()].m_Enabled = value.toInt() == Qt::Checked || m_ESPs[modIndex.row()].m_ForceEnabled; + m_LastCheck.restart(); emit dataChanged(modIndex, modIndex); refreshLoadOrder(); diff --git a/src/pluginlist.h b/src/pluginlist.h index 583c7272..b8e35c32 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -28,6 +28,7 @@ namespace MOBase { class IPluginGame; } #include #include #include +#include #include #pragma warning(push) @@ -192,6 +193,8 @@ public: */ int enabledCount() const; + int timeElapsedSinceLastChecked() const; + QString getName(int index) const { return m_ESPs.at(index).m_Name; } int getPriority(int index) const { return m_ESPs.at(index).m_Priority; } QString getIndexPriority(int index) const; @@ -273,6 +276,7 @@ signals: void writePluginsList(); + private: struct ESPInfo { @@ -347,6 +351,8 @@ private: QTemporaryFile m_TempFile; + QTime m_LastCheck; + const MOBase::IPluginGame *m_GamePlugin; }; -- cgit v1.3.1 From 344b83957e0f4491925055feab38091fdee4e751 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Jul 2018 18:35:32 +0200 Subject: Revert "Fix download index in downloadFinished" This reverts commit 942e656a71f452cd3cdbca2a4264db3cc2014ae9. --- src/downloadmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index beb498c1..67c43d4b 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1671,7 +1671,7 @@ void DownloadManager::nxmRequestFailed(QString gameName, int modID, int fileID, void DownloadManager::downloadFinished(int index) { DownloadInfo *info; - if (index >= 0) + if (index) info = m_ActiveDownloads[index]; else info = findDownload(this->sender(), &index); -- cgit v1.3.1 From f824d255a2044e81208e0813c76f9d31e630267c Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Jul 2018 19:51:30 +0200 Subject: Added clarification to loads archives tooltip. --- src/pluginlist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index c6cea045..58364e78 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -906,7 +906,7 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const text += "
" + tr("Loads Archives") + ": " + SetJoin(m_ESPs[index].m_Archives, ", "); text += "
" + tr("There are Archives connected to this plugin. " "Their assets will be added to your game, overwriting in case of conflicts following the plugin order. " - "Loose files will always overwrite assets from Archives."); + "Loose files will always overwrite assets from Archives. (This flag only checks for Archives from the same mod as the plugin)"); } if (m_ESPs[index].m_HasIni) { text += "
" + tr("Loads INI settings") + ": "; -- cgit v1.3.1 From b38957d853c2c3a62d243654d78597b52de38287 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 18 Jul 2018 12:00:55 +0200 Subject: Small visual fix for size positioning in compact downloads. --- src/downloadlistwidgetcompact.ui | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'src') diff --git a/src/downloadlistwidgetcompact.ui b/src/downloadlistwidgetcompact.ui index a3cf3f02..a3fa958c 100644 --- a/src/downloadlistwidgetcompact.ui +++ b/src/downloadlistwidgetcompact.ui @@ -19,7 +19,7 @@ true - + 2 @@ -77,19 +77,6 @@
- - - - Qt::Horizontal - - - - 40 - 20 - - - - -- cgit v1.3.1 From d5f3bee642f6ab25f10d0ba34d6f85d4876fcf2d Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 18 Jul 2018 12:01:50 +0200 Subject: Changed unit check. --- src/downloadmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 67c43d4b..33899f46 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1321,7 +1321,7 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) if (speed < 1000) { unit = "Bytes/s"; } - else if (speed/1024 < 1000) { + else if (speed < 1000*1024) { speed /= 1024; unit = "KB/s"; } -- cgit v1.3.1 From ce2494ba200a6da56634aa3e6cdb6e22884a56e6 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 18 Jul 2018 12:03:02 +0200 Subject: Don't show info dialog for unmanaged pluugins. --- src/mainwindow.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c4e888ac..2e14a201 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2896,13 +2896,20 @@ void MainWindow::on_espList_doubleClicked(const QModelIndex &index) QModelIndex idx = selection->currentIndex(); QString fileName = idx.data().toString(); + if (ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName)) == UINT_MAX) + return; + + ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + std::vector flags = modInfo->getFlags(); - displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); - // workaround to cancel the editor that might have opened because of - // selection-click - ui->espList->closePersistentEditor(index); - + if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { + displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + // workaround to cancel the editor that might have opened because of + // selection-click + ui->espList->closePersistentEditor(index); + + } } } catch (const std::exception &e) { -- cgit v1.3.1 From 4f14598cfc2ea2cf13cbf35fdd9d61338eb1f409 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 18 Jul 2018 12:06:35 +0200 Subject: Changed size rappresentation in downloads compact view. --- src/downloadlistwidgetcompact.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index 34e31006..663a224e 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -107,9 +107,9 @@ void DownloadListWidgetCompactDelegate::paintPendingDownload(int downloadIndex) { std::tuple nexusids = m_Manager->getPendingDownload(downloadIndex); m_NameLabel->setText(tr("< game %1 mod %2 file %3 >").arg(std::get<0>(nexusids)).arg(std::get<1>(nexusids)).arg(std::get<2>(nexusids))); - if (m_SizeLabel != nullptr) { - m_SizeLabel->setText("???"); - } + //if (m_SizeLabel != nullptr) { + // m_SizeLabel->setText("???"); + //} m_DoneLabel->setVisible(true); m_DoneLabel->setText(tr("Pending")); m_Progress->setVisible(false); @@ -127,9 +127,13 @@ void DownloadListWidgetCompactDelegate::paintRegularDownload(int downloadIndex) DownloadManager::DownloadState state = m_Manager->getState(downloadIndex); - if ((m_SizeLabel != nullptr)) { - m_SizeLabel->setText(sizeFormat(m_Manager->getFileSize(downloadIndex))); + if (m_SizeLabel != nullptr) { + m_SizeLabel->setText(sizeFormat(m_Manager->getFileSize(downloadIndex)) + " "); + m_SizeLabel->setVisible(true); } + //else { + // m_SizeLabel->setVisible(false); + //} if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR) || (state == DownloadManager::STATE_PAUSING)) { m_DoneLabel->setVisible(true); -- cgit v1.3.1 From f6ecb93d460b16c10933b35605f7ac1d60eac705 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 18 Jul 2018 16:00:19 +0200 Subject: Set delete Key to recursively clear overwrite with confirmation message. --- src/modlist.cpp | 9 +++++++++ src/modlist.h | 5 +++++ src/organizercore.cpp | 2 ++ 3 files changed, 16 insertions(+) (limited to 'src') diff --git a/src/modlist.cpp b/src/modlist.cpp index aaa4ba79..87cbbfb2 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -999,6 +999,15 @@ bool ModList::removeRows(int row, int count, const QModelIndex &parent) bool success = false; + if (count == 1) { + ModInfo::Ptr modInfo = ModInfo::getByIndex(row); + std::vector flags = modInfo->getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { + emit clearOverwrite(); + success = true; + } + } + for (int i = 0; i < count; ++i) { ModInfo::Ptr modInfo = ModInfo::getByIndex(row + i); if (!modInfo->isRegular()) { diff --git a/src/modlist.h b/src/modlist.h index b5f18e98..2db98bd1 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -244,6 +244,11 @@ signals: */ void fileMoved(const QString &relativePath, const QString &oldOriginName, const QString &newOriginName); + /** + * @brief emitted to have the overwrite folder cleared + */ + void clearOverwrite(); + void aboutToChangeData(); void postDataChanged(); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 142faabe..74ec752e 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -552,6 +552,8 @@ void OrganizerCore::setUserInterface(IUserInterface *userInterface, SLOT(modRemoved(QString))); connect(&m_ModList, SIGNAL(removeSelectedMods()), widget, SLOT(removeMod_clicked())); + connect(&m_ModList, SIGNAL(clearOverwrite()), widget, + SLOT(clearOverwrite())); connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), widget, SLOT(displayColumnSelection(QPoint))); connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), widget, -- cgit v1.3.1 From e80bb3a8370aeea37183d94f62e7bb3a843722b5 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 18 Jul 2018 16:14:16 +0200 Subject: Check if there is actually something in overwrite to delete before asking confirmation. --- src/modlist.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/modlist.cpp b/src/modlist.cpp index 87cbbfb2..27bccf63 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1002,7 +1002,7 @@ bool ModList::removeRows(int row, int count, const QModelIndex &parent) if (count == 1) { ModInfo::Ptr modInfo = ModInfo::getByIndex(row); std::vector flags = modInfo->getFlags(); - if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) { + if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) && (QDir(modInfo->absolutePath()).count() > 2)) { emit clearOverwrite(); success = true; } -- cgit v1.3.1 From 8f28e0af36d6246f1230a8aa296cfd5485b34242 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sat, 21 Jul 2018 20:01:40 +0200 Subject: Temporarely disable the BSA list refresh if it is hidden from view. this will need to be reversted once the Bsa changes are added I presume. --- src/mainwindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2e14a201..f2a17c17 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2544,7 +2544,10 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, , modInfo->stealFiles() , modInfo->archives()); DirectoryRefresher::cleanStructure(m_OrganizerCore.directoryStructure()); - m_OrganizerCore.refreshLists(); + //TODO: change this to always work once the BSA parsing is back in place. + if (ui->bsaList->isVisible()) + m_OrganizerCore.refreshBSAList(); + m_OrganizerCore.refreshESPList(); } } } -- cgit v1.3.1 From cc63717060229ce40279f4ac1edd0907110fee87 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sat, 21 Jul 2018 20:09:25 +0200 Subject: Added "Open Mods folder" to "Open Folder" context menu. --- src/mainwindow.cpp | 7 +++++++ src/mainwindow.h | 1 + 2 files changed, 8 insertions(+) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f2a17c17..17db79d6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3272,6 +3272,11 @@ void MainWindow::openDownloadsFolder() ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.settings().getDownloadDirectory()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } +void MainWindow::openModsFolder() +{ + ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.settings().getModDirectory()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); +} + void MainWindow::openGameFolder() { ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.managedGame()->gameDirectory().absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); @@ -3465,6 +3470,8 @@ QMenu *MainWindow::openFolderMenu() FolderMenu->addAction(tr("Open Instance folder"), this, SLOT(openInstanceFolder())); + FolderMenu->addAction(tr("Open Mods folder"), this, SLOT(openModsFolder())); + FolderMenu->addAction(tr("Open Profile folder"), this, SLOT(openProfileFolder())); FolderMenu->addAction(tr("Open Downloads folder"), this, SLOT(openDownloadsFolder())); diff --git a/src/mainwindow.h b/src/mainwindow.h index 773bf298..6cf83301 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -502,6 +502,7 @@ private slots: void openInstallFolder(); void openPluginsFolder(); void openDownloadsFolder(); + void openModsFolder(); void openProfileFolder(); void openGameFolder(); void openMyGamesFolder(); -- cgit v1.3.1 From d02df7e4b4d6b539ed744135eee565530c24f006 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sat, 21 Jul 2018 23:43:04 +0200 Subject: Avoided downloads defaulting to "File Time" sorting at each startup. Improved downloads tab header sizes and resize policy. --- src/mainwindow.cpp | 4 ++-- src/mainwindow.ui | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 17db79d6..c9874b20 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4345,8 +4345,8 @@ void MainWindow::updateDownloadListDelegate() connect(ui->downloadFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(downloadFilterChanged(QString))); ui->downloadView->setModel(sortProxy); - ui->downloadView->sortByColumn(1, Qt::DescendingOrder); - ui->downloadView->header()->resizeSections(QHeaderView::Fixed); + //ui->downloadView->sortByColumn(1, Qt::DescendingOrder); + ui->downloadView->header()->resizeSections(QHeaderView::Stretch); connect(ui->downloadView->itemDelegate(), SIGNAL(installDownload(int)), &m_OrganizerCore, SLOT(installDownload(int))); connect(ui->downloadView->itemDelegate(), SIGNAL(queryInfo(int)), m_OrganizerCore.downloadManager(), SLOT(queryInfo(int))); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index d8873d4f..8de34485 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1243,7 +1243,7 @@ p, li { white-space: pre-wrap; } Qt::ScrollBarAlwaysOn - Qt::ScrollBarAlwaysOff + Qt::ScrollBarAsNeeded true @@ -1276,7 +1276,10 @@ p, li { white-space: pre-wrap; } true - 100 + 50 + + + 15 true -- cgit v1.3.1 From edf608ab23e98b4d7dbfd09628cb64770d4bdb1a Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 22 Jul 2018 15:49:40 +0200 Subject: Allow normal QAbstractItemModel event handling for mainwindow. Somehow before it wouldn't work. --- src/modlist.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/modlist.cpp b/src/modlist.cpp index 27bccf63..e077a5ee 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -1228,6 +1228,7 @@ bool ModList::eventFilter(QObject *obj, QEvent *event) } else if (keyEvent->key() == Qt::Key_Space) { return toggleSelection(itemView); } + return QAbstractItemModel::eventFilter(obj, event); } return QAbstractItemModel::eventFilter(obj, event); } -- cgit v1.3.1 From fc8a6b358fb7bda18979c7c5c23c13d7c2ae8dc8 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Fri, 20 Jul 2018 03:21:30 -0500 Subject: Improve robustness of endorsement checks At times, the voted_by_user item is returned as Invalid. toBool() causes this to be false, meaning the item is seen as not endorsed. Now, the endorsement state is not checked if the data is invalid. --- src/mainwindow.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c9874b20..d8dd6e45 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4388,7 +4388,8 @@ void MainWindow::nxmUpdatesAvailable(const std::vector &modIDs, QVariant us if (game && result["id"].toInt() == game->nexusModOrganizerID() && result["game_id"].toInt() == game->nexusGameID()) { - if (!result["voted_by_user"].toBool()) { + if (result["voted_by_user"].type() != QVariant::Invalid && + !result["voted_by_user"].toBool()) { ui->actionEndorseMO->setVisible(true); } } else { @@ -4412,7 +4413,8 @@ void MainWindow::nxmUpdatesAvailable(const std::vector &modIDs, QVariant us (*iter)->setNewestVersion(result["version"].toString()); (*iter)->setNexusDescription(result["description"].toString()); if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->loggedIn() && - result.contains("voted_by_user")) { + result.contains("voted_by_user") && + result["voted_by_user"].type() != QVariant::Invalid) { // don't use endorsement info if we're not logged in or if the response doesn't contain it (*iter)->setIsEndorsed(result["voted_by_user"].toBool()); } -- cgit v1.3.1 From 447a2169fec88b3239d48a3583226f647798388e Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Sat, 21 Jul 2018 19:58:19 -0500 Subject: Allow more characters in instance names and add an error message Previously allowed: "A-Za-z0-9 " Now allowed: "A-Za-z0-9 !@#$%^()_+-=[]{};'." --- src/instancemanager.cpp | 35 ++++++++++++++++++++++++++++++++--- src/instancemanager.h | 1 + 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 2b718cb2..1c6542e8 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -131,11 +131,12 @@ QString InstanceManager::manageInstances(const QStringList &instanceList) const QString InstanceManager::queryInstanceName(const QStringList &instanceList) const { QString instanceId; + QString dialogText; while (instanceId.isEmpty()) { QInputDialog dialog; - dialog.setWindowTitle(QObject::tr("Enter a Name for the new Instance")); - dialog.setLabelText(QObject::tr("Enter a new name or select one from the sugested list (only letters and numbers allowed):")); + dialog.setWindowTitle(QObject::tr("Enter a Name for the new Instance")); + dialog.setLabelText(QObject::tr("Enter a new name or select one from the suggested list:")); // would be neat if we could take the names from the game plugins but // the required initialization order requires the ini file to be // available *before* we load plugins @@ -146,7 +147,17 @@ QString InstanceManager::queryInstanceName(const QStringList &instanceList) cons if (dialog.exec() == QDialog::Rejected) { throw MOBase::MyException(QObject::tr("Canceled")); } - instanceId = dialog.textValue().replace(QRegExp("[^0-9a-zA-Z ]"), "").remove(QRegExp("( )*$")); + dialogText = dialog.textValue(); + instanceId = sanitizeInstanceName(dialogText); + if (instanceId != dialogText) { + if (QMessageBox::question( nullptr, + QObject::tr("Invalid instance name"), + QObject::tr("The instance name \"%1\" is invalid. Use the name \"%2\" instead?").arg(dialogText,instanceId), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { + instanceId=""; + continue; + } + } bool alreadyExists=false; for (const QString &instance : instanceList) { @@ -296,3 +307,21 @@ QString InstanceManager::determineDataPath() } } + +QString InstanceManager::sanitizeInstanceName(const QString &name) const +{ + QString new_name = name; + + // Restrict the allowed characters + new_name = new_name.remove(QRegExp("[^A-Za-z0-9 _=+;!@#$%^'\\-\\.\\[\\]\\{\\}\\(\\)]")); + + // Don't end in spaces and periods + new_name = new_name.remove(QRegExp("\\.*$")); + new_name = new_name.remove(QRegExp(" *$")); + + // Recurse until stuff stops changing + if (new_name != name) { + return sanitizeInstanceName(new_name); + } + return new_name; +} \ No newline at end of file diff --git a/src/instancemanager.h b/src/instancemanager.h index adedd78f..4efa6f03 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -50,6 +50,7 @@ private: QString manageInstances(const QStringList &instanceList) const; + QString sanitizeInstanceName(const QString &name) const; void setCurrentInstance(const QString &name); QString queryInstanceName(const QStringList &instanceList) const; -- cgit v1.3.1 From 385765ecddbb1e9d7f38cf313c3c6db6dc07be52 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Mon, 23 Jul 2018 14:13:12 -0500 Subject: Truncate strings longer than 1024 characters in the plugin list tooltip --- src/pluginlist.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 58364e78..76de436d 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -72,6 +72,15 @@ static bool ByDate(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RH return QFileInfo(LHS.m_FullPath).lastModified() < QFileInfo(RHS.m_FullPath).lastModified(); } +static QString TruncateString(const QString& text) { + QString new_text = text; + if (new_text.length() > 1024) { + new_text.truncate(1024); + new_text += "..."; + } + return new_text; +} + PluginList::PluginList(QObject *parent) : QAbstractItemModel(parent) , m_FontMetrics(QFont()) @@ -187,7 +196,7 @@ void PluginList::refresh(const QString &profileName bool hasIni = baseDirectory.findFile(ToWString(iniPath)).get() != nullptr; std::set loadedArchives; - QString originPath = QString::fromWCharArray(origin.getPath().c_str()); + QString originPath = QString::fromWCharArray(origin.getPath().c_str()); QDir dir(QDir::toNativeSeparators(originPath)); for (QString filename : dir.entryList(QStringList() << QFileInfo(filename).baseName() + "*.bsa" << QFileInfo(filename).baseName() + "*.ba2")) { loadedArchives.insert(filename); @@ -887,23 +896,23 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const } else { QString text = tr("Origin: %1").arg(m_ESPs[index].m_OriginName); if (m_ESPs[index].m_Author.size() > 0) { - text += "
" + tr("Author") + ": " + m_ESPs[index].m_Author; + text += "
" + tr("Author") + ": " + TruncateString(m_ESPs[index].m_Author); } if (m_ESPs[index].m_Description.size() > 0) { - text += "
" + tr("Description") + ": " + m_ESPs[index].m_Description; + text += "
" + tr("Description") + ": " + TruncateString(m_ESPs[index].m_Description); } if (m_ESPs[index].m_MasterUnset.size() > 0) { - text += "
" + tr("Missing Masters") + ": " + SetJoin(m_ESPs[index].m_MasterUnset, ", ") + ""; + text += "
" + tr("Missing Masters") + ": " + TruncateString(SetJoin(m_ESPs[index].m_MasterUnset, ", ")) + ""; } std::set enabledMasters; std::set_difference(m_ESPs[index].m_Masters.begin(), m_ESPs[index].m_Masters.end(), m_ESPs[index].m_MasterUnset.begin(), m_ESPs[index].m_MasterUnset.end(), std::inserter(enabledMasters, enabledMasters.end())); if (!enabledMasters.empty()) { - text += "
" + tr("Enabled Masters") + ": " + SetJoin(enabledMasters, ", "); + text += "
" + tr("Enabled Masters") + ": " + TruncateString(SetJoin(enabledMasters, ", ")); } if (!m_ESPs[index].m_Archives.empty()) { - text += "
" + tr("Loads Archives") + ": " + SetJoin(m_ESPs[index].m_Archives, ", "); + text += "
" + tr("Loads Archives") + ": " + TruncateString(SetJoin(m_ESPs[index].m_Archives, ", ")); text += "
" + tr("There are Archives connected to this plugin. " "Their assets will be added to your game, overwriting in case of conflicts following the plugin order. " "Loose files will always overwrite assets from Archives. (This flag only checks for Archives from the same mod as the plugin)"); -- cgit v1.3.1 From 2f8e5b4f690e6c6ad195a1120ce2ccafc66a3f23 Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Tue, 24 Jul 2018 14:00:26 -0500 Subject: Update translation file --- src/organizer_en.ts | 1329 ++++++++++++++++++++++++++++----------------------- 1 file changed, 725 insertions(+), 604 deletions(-) (limited to 'src') diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 60fd66e7..01a4abcb 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -303,16 +303,21 @@ p, li { white-space: pre-wrap; } + Size + + + + Done - + Information missing, please select "Query Info" from the context menu to re-retrieve. - + pending download @@ -360,7 +365,7 @@ p, li { white-space: pre-wrap; } - + Done @@ -368,151 +373,180 @@ p, li { white-space: pre-wrap; } DownloadListWidgetCompactDelegate - + < game %1 mod %2 file %3 > - + Pending - + Paused - + Fetching Info 1 - + Fetching Info 2 - + Installed - + Uninstalled - + Done - - - - - + + + + + + + Are you sure? - + This will permanently delete the selected download. - + This will remove all finished downloads from this list and from disk. - + This will remove all installed downloads from this list and from disk. - + + This will remove all uninstalled downloads from this list and from disk. + + + + This will permanently remove all finished downloads from this list (but NOT from disk). - + This will permanently remove all installed downloads from this list (but NOT from disk). - + + This will permanently remove all uninstalled downloads from this list (but NOT from disk). + + + + Install - + Query Info - + Visit on Nexus - + + + + Show in Folder + + + + Delete - + Un-Hide - + Hide - + Cancel - + Pause - + Remove - + Resume - + Delete Installed... - + + Delete Uninstalled... + + + + Delete All... - + Hide Installed... - + + Hide Uninstalled... + + + + Hide All... - + Un-Hide All... @@ -540,20 +574,22 @@ p, li { white-space: pre-wrap; } - - + + + Are you sure? - + This will remove all finished downloads from this list and from disk. - - + + + Delete Files? @@ -563,92 +599,115 @@ p, li { white-space: pre-wrap; } - + This will remove all installed downloads from this list and from disk. - + + This will remove all uninstalled downloads from this list and from disk. + + + + This will remove all finished downloads from this list (but NOT from disk). - + This will remove all installed downloads from this list (but NOT from disk). - + + This will remove all uninstalled downloads from this list (but NOT from disk). + + + + Install - + Query Info - + Visit on Nexus - + + + + Show in Folder + + + + + Delete - + Un-Hide - + Hide - + Cancel - + Pause - - Remove + + Resume - - Resume + + Delete Installed... - - Delete Installed... + + Delete Uninstalled... - + Delete All... - + Hide Installed... - + + Hide Uninstalled... + + + + Hide All... - + Un-Hide All... @@ -661,286 +720,287 @@ p, li { white-space: pre-wrap; } - + Memory allocation error (in refreshing directory). - + failed to download %1: could not open output file: %2 - + Download again? - + A file with the same name has already been downloaded. Do you want to download it again? The new file will receive a different name. - + Wrong Game - + The download link is for a mod for "%1" but this instance of MO has been set up for "%2". - - + + Already Started - + A download for this mod file has already been queued. - + There is already a download started for this file (mod: %1, file: %2). - - + + remove: invalid download index %1 - + failed to delete %1 - + failed to delete meta file for %1 - + restore: invalid download index: %1 - + cancel: invalid download index %1 - + pause: invalid download index %1 - + resume: invalid download index %1 - + resume (int): invalid download index %1 - + No known download urls. Sorry, this download can't be resumed. - + query: invalid download index %1 - + Please enter the nexus mod id - + Mod ID: - + Please select the source game code for %1 - + + VisitNexus: invalid download index %1 - + Nexus ID for this Mod is unknown - + get pending: invalid download index %1 - + get path: invalid download index %1 - + Main - + Update - + Optional - + Old - + Misc - + Unknown - + display name: invalid download index %1 - + file name: invalid download index %1 - + file time: invalid download index %1 - + file size: invalid download index %1 - + progress: invalid download index %1 - + state: invalid download index %1 - + infocomplete: invalid download index %1 - - + + mod id: invalid download index %1 - + ishidden: invalid download index %1 - + file info: invalid download index %1 - + mark installed: invalid download index %1 - + mark uninstalled: invalid download index %1 - + Memory allocation error (in processing progress event). - + Memory allocation error (in processing downloaded data). - + Information updated - - + + No matching file found on Nexus! Maybe this file is no longer available or it was renamed? - + No file on Nexus matches the selected file by name. Please manually choose the correct one. - + No download server available. Please try again later. - + Failed to request file info from nexus: %1 - + Warning: Content type is: %1 - + Download header content length: %1 downloaded file size: %2 - + Download failed: %1 (%2) - + We were unable to download the file due to errors after four retries. There may be an issue with the Nexus servers. - + failed to re-open %1 @@ -1447,7 +1507,7 @@ This is likely due to a corrupted or incompatible download or unrecognized archi MainWindow - + Categories @@ -1512,61 +1572,61 @@ p, li { white-space: pre-wrap; } - - + + Restore Backup... - - + + Create Backup - + List of available mods. - + This is a list of installed mods. Use the checkboxes to activate/deactivate mods and drag & drop mods to change their "installation" orders. - + Filter - + Clear all Filters - + No groups - + Nexus IDs - - - + + + Namefilter - + Pick a program to run. - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1576,12 +1636,12 @@ p, li { white-space: pre-wrap; } - + Run program - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1590,17 +1650,17 @@ p, li { white-space: pre-wrap; } - + Run - + Create a shortcut in your start menu or on the desktop to the specified program - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1609,27 +1669,27 @@ p, li { white-space: pre-wrap; } - + Shortcut - + Plugins - + Sort - + List of available esp/esm files - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1638,27 +1698,27 @@ p, li { white-space: pre-wrap; } - + Archives - + <html><head/><body><p>BSAs / BA2s are bundles of game assets (textures, scripts, etc.). By default, the engine loads these bundles in a separate step from loose files. <p>Their load order is specified by the priority of the corresponding plugin (right pane, plugins tab).</p><p>If there is a matching plugin, the game will load them no matter what.</p></body></html> - + <html><head/><body><p>Currently detected archives. (<a href="#"><span style=" text-decoration: underline; color:#0000ff;">What is an archive?</span></a>)</p></body></html> - + List of available BS Archives. Archives not checked here are not managed by MO and ignore installation order. - + BSA files are archives (comparable to .zip files) that contain data assets (meshes, textures, ...) to be used by the game. As such they "compete" with loose files in your data directory over which is loaded. By default, BSAs that share their base name with an enabled ESP (i.e. plugin.esp and plugin.bsa) are automatically loaded and will have precedence over all loose files, the installation order you set up to the left is then ignored! @@ -1666,61 +1726,60 @@ p, li { white-space: pre-wrap; } - - + File - + Data - + refresh data-directory overview - + Refresh the overview. This may take a moment. - - - + + + Refresh - + This is an overview of your data directory as visible to the game (and tools). - + Mod - - + + Filter the above list so that only conflicts are displayed. - + Show only conflicts - + Saves - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1731,155 +1790,155 @@ p, li { white-space: pre-wrap; } - + Downloads - + This is a list of mods you downloaded from Nexus. Double click one to install it. You can also drag an archive into here. - + Show Hidden - + Tool Bar - + Install Mod - + Install &Mod - + Install a new mod from an archive - + Ctrl+M - + Profiles - + &Profiles - + Configure Profiles - + Ctrl+P - + Executables - + &Executables - + Configure the executables that can be started through Mod Organizer - + Ctrl+E - - + + Tools - + &Tools - + Ctrl+I - + Settings - + &Settings - + Configure settings and workarounds - + Ctrl+S - + Nexus - + Search nexus network for more mods - + Ctrl+N - - + + Update - + Mod Organizer is up-to-date - + No Problems - + This button will be highlighted if MO discovered potential problems in your setup and provide tips on how to fix them. !Work in progress! @@ -1887,44 +1946,44 @@ Right now this has very limited functionality - - + + Help - + Ctrl+H - + Endorse MO - - + + Endorse Mod Organizer - + Copy Log to Clipboard - + Ctrl+C - + Change Game - + Open the Instance selection dialog to manage a different Game @@ -2175,16 +2234,16 @@ Right now this has very limited functionality - - + + failed to rename "%1" to "%2" - - - + + + Confirm @@ -2231,391 +2290,396 @@ Right now this has very limited functionality - + Nexus ID for this Mod is unknown - + Web page for this mod is unknown - - - + + + Create Mod... - + This will create an empty mod. Please enter a name: - - + + A mod with this name already exists - + This will move all files from overwrite into a new, regular mod. Please enter a name: - - + + Are you sure? - + About to recursively delete: - + Not logged in, endorsement information will be wrong - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Export to csv - + CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet. You can also use online editors and converters instead. - + Select what mods you want export: - + All installed mods - + Only active (checked) mods from your current profile - + All currently visible mods in the mod list - + Choose what Columns to export: - + Mod_Priority - + Mod_Name - + Mod_Status - + Primary_Category - + Nexus_ID - + Mod_Nexus_URL - + Mod_Version - + Install_Date - + Download_File_Name - + export failed: %1 - + Open Game folder - + Open MyGames folder - + Open Instance folder - + + Open Mods folder + + + + Open Profile folder - + Open Downloads folder - + Open MO2 Install folder - + Open MO2 Plugins folder - + Open MO2 Logs folder - + Install Mod... - + Create empty mod - + Enable all visible - + Disable all visible - + Check all for update - + Export to csv... - + All Mods - + Sync to Mods... - + Clear Overwrite... - - + + Open in explorer - + Restore Backup - + Remove Backup... - + Change Categories - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... - + Reinstall Mod - + Remove Mod... - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Mark as converted/working - + Visit on Nexus - + Visit web page - + Information... - - + + Exception: - - + + Unknown exception - + <All> - + <Multiple> - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -2623,12 +2687,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -2636,319 +2700,319 @@ You can also use online editors and converters instead. - + failed to remove %1 - + failed to create %1 - + Can't change download directory while downloads are in progress! - + failed to write to file %1 - + %1 written - + Select binary - + Binary - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Thank you! - + Thank you for your endorsement! - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - - + + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Extract... - + This will restart MO, continue? - + Edit Categories... - + Deselect filter - + Remove - + Enable all - + Disable all - + Unlock load order - + Lock load order - + depends on missing "%1" - + incompatible with "%1" - + Please wait while LOOT is running - + loot failed. Exit code was: %1 - + failed to start loot - + failed to run loot: %1 - + Errors occured - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of modlist created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file @@ -3016,16 +3080,21 @@ You can also use online editors and converters instead. + INI files + + + + invalid content type %1 - + invalid mod index %1 - + remove: invalid mod index %1 @@ -3661,12 +3730,12 @@ p, li { white-space: pre-wrap; } - + %1 contains no esp/esm/esl and no asset (textures, meshes, interface, ...) directory - + Categories: <br> @@ -3724,193 +3793,198 @@ p, li { white-space: pre-wrap; } - + + INI files + + + + This entry contains files that have been created inside the virtual data tree (i.e. by the construction kit) - + Backup - + No valid game data - + Not endorsed yet - + Overwrites files - + Overwritten files - + Overwrites & Overwritten - + Redundant - + Alternate game source - + Non-MO - + invalid - + installed version: "%1", newest version: "%2" - + The newest version on Nexus seems to be older than the one you have installed. This could either mean the version you have has been withdrawn (i.e. due to a bug) or the author uses a non-standard versioning scheme and that newest version is actually newer. Either way you may want to "upgrade". - + Categories: <br> - + Invalid name - + Name is already in use by another mod - + drag&drop failed: %1 - + Confirm - + Are you sure you want to remove "%1"? - + Flags - + Content - + Mod Name - + Version - + Priority - + Category - + Source Game - + Nexus ID - + Installation - - + + unknown - + Name of your mods - + Version of the mod (if available) - + Installation priority of your mod. The higher, the more "important" it is and thus overwrites files from mods with lower priority. - + Category of the mod. - + The source game which was the origin of this mod. - + Id of the mod as used on Nexus. - + Emblemes to highlight things that might require attention. - - Depicts the content of the mod:<br><table cellspacing=7><tr><td><img src=":/MO/gui/content/plugin" width=32/></td><td>Game plugins (esp/esm/esl)</td></tr><tr><td><img src=":/MO/gui/content/interface" width=32/></td><td>Interface</td></tr><tr><td><img src=":/MO/gui/content/mesh" width=32/></td><td>Meshes</td></tr><tr><td><img src=":/MO/gui/content/bsa" width=32/></td><td>BSA</td></tr><tr><td><img src=":/MO/gui/content/texture" width=32/></td><td>Textures</td></tr><tr><td><img src=":/MO/gui/content/sound" width=32/></td><td>Sounds</td></tr><tr><td><img src=":/MO/gui/content/music" width=32/></td><td>Music</td></tr><tr><td><img src=":/MO/gui/content/string" width=32/></td><td>Strings</td></tr><tr><td><img src=":/MO/gui/content/script" width=32/></td><td>Scripts (Papyrus)</td></tr><tr><td><img src=":/MO/gui/content/skse" width=32/></td><td>Script Extender plugins</td></tr><tr><td><img src=":/MO/gui/content/skyproc" width=32/></td><td>SkyProc Patcher</td></tr><tr><td><img src=":/MO/gui/content/menu" width=32/></td><td>Mod Configuration Menu</td></tr></table> + + Depicts the content of the mod:<br><table cellspacing=7><tr><td><img src=":/MO/gui/content/plugin" width=32/></td><td>Game plugins (esp/esm/esl)</td></tr><tr><td><img src=":/MO/gui/content/interface" width=32/></td><td>Interface</td></tr><tr><td><img src=":/MO/gui/content/mesh" width=32/></td><td>Meshes</td></tr><tr><td><img src=":/MO/gui/content/bsa" width=32/></td><td>BSA</td></tr><tr><td><img src=":/MO/gui/content/texture" width=32/></td><td>Textures</td></tr><tr><td><img src=":/MO/gui/content/sound" width=32/></td><td>Sounds</td></tr><tr><td><img src=":/MO/gui/content/music" width=32/></td><td>Music</td></tr><tr><td><img src=":/MO/gui/content/string" width=32/></td><td>Strings</td></tr><tr><td><img src=":/MO/gui/content/script" width=32/></td><td>Scripts (Papyrus)</td></tr><tr><td><img src=":/MO/gui/content/skse" width=32/></td><td>Script Extender plugins</td></tr><tr><td><img src=":/MO/gui/content/skyproc" width=32/></td><td>SkyProc Patcher</td></tr><tr><td><img src=":/MO/gui/content/menu" width=32/></td><td>Mod Configuration Menu</td></tr><tr><td><img src=":/MO/gui/content/inifile" width=32/></td><td>INI files</td></tr></table> - + Time this mod was installed @@ -3918,7 +3992,7 @@ p, li { white-space: pre-wrap; } ModListSortProxy - + Drag&Drop is only supported when sorting by priority @@ -4006,189 +4080,201 @@ p, li { white-space: pre-wrap; } OrganizerCore - - + + Failed to write settings - + An error occured trying to update MO settings to %1: %2 - + File is write protected - + Invalid file format (probably a bug) - + Unknown error %1 - + An error occured trying to write back MO settings to %1: %2 - - + + Download started - + Download failed - - + + Installation successful - - + + Configure Mod - - + + This mod contains ini tweaks. Do you want to configure them now? - - + + mod "%1" not found - - + + Installation cancelled - - + + The mod was not installed completely. - + Executable "%1" not found - + Start Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? - + Error - + + Windows Event Log Error + + + + + The Windows Event Log service is disabled and/or not running. This prevents USVFS from running properly. Your mods may not be working in the executable that you are launching. Note that you may have to restart MO and/or your PC after the service is fixed. + +Continue launching %1? + + + + No profile set - + Failed to refresh list of esps: %1 - + Multiple esps/esls activated, please check that they don't conflict. - + Download? - + A download has been started but no installed page plugin recognizes it. If you download anyway no information (i.e. version) will be associated with the download. Continue? - + failed to update mod list: %1 - - + + login successful - + Login failed - + Login failed, try again? - + login failed: %1. Download will not be associated with an account - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + MO1 "Script Extender" load mechanism has left hook.dll in your game folder - - + + Description missing - + <a href="%1">hook.dll</a> has been found in your game folder (right click to copy the full path). This is most likely a leftover of setting the ModOrganizer 1 load mechanism to "Script Extender", in which case you must remove this file either by changing the load mechanism in ModOrganizer 1 or manually removing the file, otherwise the game is likely to crash and burn. - + failed to save load order: %1 - + The designated write target "%1" is not enabled. @@ -4201,7 +4287,12 @@ Continue? - + + Open in Explorer + + + + You can use drag&drop to move files and directories to regular mods. @@ -4289,120 +4380,135 @@ Continue? PluginList - + Name - + Priority - + Mod Index - + Flags - - + + unknown - + Name of your mods - + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. - + The modindex determines the formids of objects originating from this mods. - + failed to update esp info for file %1 (source id: %2), error: %3 - + esp not found: %1 - - + + Confirm - + Really enable all plugins? - + Really disable all plugins? - + The file containing locked plugin indices is broken - - + + <b>Origin</b>: %1 - + <br><b><i>This plugin can't be disabled (enforced by the game).</i></b> - + Author - + Description - + Missing Masters - + Enabled Masters - - There is an ini file connected to this esp. Its settings will be added to your game settings, overwriting in case of conflicts. + + Loads Archives + + + + + There are Archives connected to this plugin. Their assets will be added to your game, overwriting in case of conflicts following the plugin order. Loose files will always overwrite assets from Archives. (This flag only checks for Archives from the same mod as the plugin) - + + Loads INI settings + + + + + There is an ini file connected to this plugin. Its settings will be added to your game settings, overwriting in case of conflicts. + + + + This ESP is flagged as an ESL. It will adhere to the ESP load order but the records will be loaded in ESL space. - + failed to restore load order for %1 @@ -4758,7 +4864,7 @@ p, li { white-space: pre-wrap; } - + Error @@ -4920,84 +5026,94 @@ If the folder was still in use, restart MO and try again. - + Enter a Name for the new Instance - - Enter a new name or select one from the sugested list (only letters and numbers allowed): + + Enter a new name or select one from the suggested list: - - + + Canceled - + + Invalid instance name + + + + + The instance name "%1" is invalid. Use the name "%2" instead? + + + + The instance "%1" already exists. - + Please choose a different instance name, like: "%1 1" . - + Choose Instance - + Each Instance is a full set of MO data files (mods, downloads, profiles, configuration, ...). You can use multiple instances for different games. Instances are stored in Appdata and can be accessed by all MO installations. If your MO folder is writable, you can also store a single instance locally (called a Portable install, and all the MO data files will be inside the installation folder). - + New - + Create a new instance. - + Portable - + Use MO folder for data. - + Manage Instances - + Delete an Instance. - + failed to create %1 - + Data directory created - + New data directory created at %1. If you don't want to store a lot of data there, reconfigure the storage directories via settings. @@ -5067,7 +5183,7 @@ If the folder was still in use, restart MO and try again. - + Failed to create "%1". Your user account probably lacks permission. @@ -5109,7 +5225,7 @@ If the folder was still in use, restart MO and try again. - + Mod Organizer @@ -5130,7 +5246,7 @@ If the folder was still in use, restart MO and try again. - + <Manage...> @@ -5171,12 +5287,12 @@ If the folder was still in use, restart MO and try again. - + failed to access %1 - + failed to set file time %1 @@ -5186,12 +5302,12 @@ If the folder was still in use, restart MO and try again. - + Script Extender - + Proxy DLL @@ -5411,12 +5527,12 @@ Select Show Details option to see the full change-log. - + Error - + Failed to create "%1", you may not have the necessary permission. path remains unchanged. @@ -5543,81 +5659,86 @@ If you use pre-releases, never contact me directly by e-mail or via private mess - + Base Directory - - - + + + ... - + Overwrite - + Caches - + Downloads - + Directory where mods are stored. - + Directory where mods are stored. Please note that changing this will break all associations of profiles with mods that don't exist in the new location (with the same name). - - + + Directory where downloads are stored. - + Mods - + Profiles - + + Managed Game + + + + Use %BASE_DIR% to refer to the Base Directory. - + Important: All directories have to be writeable! - - + + Nexus - + Allows automatic log-in when the Nexus-Page for the game is clicked. - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -5626,144 +5747,144 @@ p, li { white-space: pre-wrap; } - + If checked and if correct credentials are entered below, log-in to Nexus (for browsing and downloading) is automatic. - + Automatically Log-In to Nexus - - + + Username - - + + Password - + Remove cache and cookies. Forces a new login. - + Clear Cache - + Disable automatic internet features - + Disable automatic internet features. This does not affect features that are explicitly invoked by the user (like checking mods for updates, endorsing, opening the web browser) - + Offline Mode - + Use a proxy for network connections. - + Use a proxy for network connections. This uses the system-wide settings which can be configured in Internet Explorer. Please note that MO will start up a few seconds slower on some systems when using a proxy. - + Use HTTP Proxy (Uses System Settings) - + Associate with "Download with manager" links - + Known Servers (updated on download) - + Preferred Servers (Drag & Drop) - + Steam - + If you save your steam user ID and password here, they will be used when logging into steam. Note, however, your password will be stored unencrypted, so make sure your computer is secure. - + Plugins - + Author: - + Version: - + Description: - + Key - + Value - + Blacklisted Plugins (use <del> to remove): - + Workarounds - + Steam App ID - + The Steam AppID for your game - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -5779,17 +5900,17 @@ p, li { white-space: pre-wrap; } - + Load Mechanism - + Select loading mechanism. See help for details. - + 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. @@ -5800,17 +5921,17 @@ If you use the Steam version of Oblivion the default will NOT work. In this case - + NMM Version - + The Version of Nexus Mod Manager to impersonate. - + Mod Organizer uses an API provided by the Nexus to provide features like checking for updates and downloading files. Unfortunately this API has not been made available officially to third party tools like MO so we have to impersonate the Nexus Mod Manager to be allowed in. On top of this Nexus has used the client identification to lock out outdated versions of NMM to force users to update. This means that MO also needs to impersonate the new version of NMM even if MO doesn't need an update. Therefore you can configure the version to identify as here. Please note that MO does identify itself as MO to the webserver, it's not lying about what it is. It is merely adding a "compatible" NMM version to the user agent. @@ -5819,44 +5940,44 @@ tl;dr-version: If Nexus-features don't work, insert the current version num - + Enforces that inactive ESPs and ESMs are never loaded. - + It seems that the Games occasionally load ESP or ESM files even if they haven't been activated as plugins. I don't yet know what the circumstances are, but user reports imply it is in some cases unwanted. If this is checked, ESPs and ESMs not checked in the List are invisible to the game and can not be loaded. - + Hide inactive ESPs/ESMs - + If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) - + If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) Uncheck this if you want to use Mod Organizer with total conversions (like Nehrim) but be aware that the game will crash if required files are not enabled. - + Force-enable game files - + Disable this to no longer display mods installed outside MO in the mod list (left pane). Assets from those mods will then be treated as having lowest mod priority together with the original game content. - + By default Mod Organizer will display esp+bsa bundles installed with foreign tools as mods (left pane). This allows you to control their priority in relation to other mods. This is particularly useful if you also use Steam Workshop to install mods. However, if you installed loose file mods outside MO which conflict with BSAs also installed outside MO those conflicts can't be resolved correctly. @@ -5864,44 +5985,44 @@ If you disable this feature, MO will only display official DLCs this way. Please - + Display mods installed outside MO - - + + For Skyrim, this can be used instead of Archive Invalidation. It should make AI redundant for all Profiles. For the other games this is not a sufficient replacement for AI! - + Back-date BSAs - + These are workarounds for problems with Mod Organizer. Please make sure you read the help text before changing anything here. - + Diagnostics - + Log Level - + Decides the amount of data printed to "ModOrganizer.log" - + Decides the amount of data printed to "ModOrganizer.log". "Debug" produces very useful information for finding problems. There is usually no noteworthy performance impact but the file may become rather large. If this is a problem you may prefer the "Info" level for regluar use. On the "Error" level the log file usually remains empty. @@ -5909,37 +6030,37 @@ For the other games this is not a sufficient replacement for AI! - + Debug - + Info (recommended) - + Warning - + Error - + Crash Dumps - + Decides which type of crash dumps are collected when injected processes crash. - + Decides which type of crash dumps are collected when injected processes crash. "None" Disables the generation of crash dumps by MO. @@ -5950,37 +6071,37 @@ For the other games this is not a sufficient replacement for AI! - + None - + Mini (recommended) - + Data - + Full - + Max Dumps To Keep - + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. - + Maximum number of crash dumps to keep on disk. Use 0 for unlimited. Set "Crash Dumps" above to None to disable crash dump collection. @@ -5988,7 +6109,7 @@ For the other games this is not a sufficient replacement for AI! - + Logs and crash dumps are stored under your current instance in the <a href="LOGS_FULL_PATH">LOGS_DIR</a> and <a href="DUMPS_FULL_PATH">DUMPS_DIR</a> folders. @@ -5998,7 +6119,7 @@ For the other games this is not a sufficient replacement for AI! - + Hint: right click link and copy link location -- cgit v1.3.1 From a02b920b19f4efcf0cefe6056ec7a91764e108bd Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Fri, 27 Jul 2018 12:19:16 -0500 Subject: Force nxmhandler.exe registration when the associate button is clicked This allows the register dialog to appear when the user had previously selected "No, don't ask again". If a user is clicking this button, there's a good chance they want MO to be the primary nxm handler. --- src/settingsdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 44fc9b5e..058d082a 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -241,7 +241,7 @@ void SettingsDialog::deleteBlacklistItem() void SettingsDialog::on_associateButton_clicked() { - Settings::instance().registerAsNXMHandler(false); + Settings::instance().registerAsNXMHandler(true); } void SettingsDialog::on_clearCacheButton_clicked() -- cgit v1.3.1 From 10be66a62a6d719355d6a494d365e4ea4dac1e9e Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 1 Aug 2018 17:07:35 +0200 Subject: Revert "Temporarely disable the BSA list refresh if it is hidden from view. this will need to be reversted once the Bsa changes are added I presume." This reverts commit 8f28e0af36d6246f1230a8aa296cfd5485b34242. --- src/mainwindow.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d8dd6e45..f587cc50 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2544,10 +2544,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, , modInfo->stealFiles() , modInfo->archives()); DirectoryRefresher::cleanStructure(m_OrganizerCore.directoryStructure()); - //TODO: change this to always work once the BSA parsing is back in place. - if (ui->bsaList->isVisible()) - m_OrganizerCore.refreshBSAList(); - m_OrganizerCore.refreshESPList(); + m_OrganizerCore.refreshLists(); } } } -- cgit v1.3.1 From 696bb3bc47daf7779a0520dba64affaac3a5b4ad Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 1 Aug 2018 17:18:07 +0200 Subject: Added CTRL+Double Click on Modlist to open in Explorer --- src/mainwindow.cpp | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index f587cc50..d3e3c613 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2861,14 +2861,30 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index) return; } - try { - m_ContextRow = m_ModListSortProxy->mapToSource(index).row(); - displayModInformation(sourceIdx.row()); - // workaround to cancel the editor that might have opened because of - // selection-click - ui->modList->closePersistentEditor(index); - } catch (const std::exception &e) { - reportError(e.what()); + Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers(); + if (modifiers.testFlag(Qt::ControlModifier)) { + try { + m_ContextRow = m_ModListSortProxy->mapToSource(index).row(); + openExplorer_clicked(); + // workaround to cancel the editor that might have opened because of + // selection-click + ui->modList->closePersistentEditor(index); + } + catch (const std::exception &e) { + reportError(e.what()); + } + } + else { + try { + m_ContextRow = m_ModListSortProxy->mapToSource(index).row(); + displayModInformation(sourceIdx.row()); + // workaround to cancel the editor that might have opened because of + // selection-click + ui->modList->closePersistentEditor(index); + } + catch (const std::exception &e) { + reportError(e.what()); + } } } -- cgit v1.3.1 From e1e367c7eae67471e760673a8149ccf30bac8685 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 1 Aug 2018 17:55:07 +0200 Subject: Added CTRL+Doubleclick on plugin list as well --- src/mainwindow.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d3e3c613..f6cfbfbd 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2920,11 +2920,20 @@ void MainWindow::on_espList_doubleClicked(const QModelIndex &index) if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { - displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); - // workaround to cancel the editor that might have opened because of - // selection-click - ui->espList->closePersistentEditor(index); + Qt::KeyboardModifiers modifiers = QApplication::queryKeyboardModifiers(); + if (modifiers.testFlag(Qt::ControlModifier)) { + openExplorer_activated(); + // workaround to cancel the editor that might have opened because of + // selection-click + ui->espList->closePersistentEditor(index); + } + else { + displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName))); + // workaround to cancel the editor that might have opened because of + // selection-click + ui->espList->closePersistentEditor(index); + } } } } -- cgit v1.3.1 From 7eb4b33214279aeb97541344e1c20da2b8dd79ff Mon Sep 17 00:00:00 2001 From: LostDragonist Date: Tue, 31 Jul 2018 03:52:55 -0500 Subject: Tweak shading of mods and plugins to better support dark themes --- src/modlist.cpp | 7 +++---- src/pluginlist.cpp | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modlist.cpp b/src/modlist.cpp index e077a5ee..0d084c22 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -354,7 +354,6 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const int highlight = modInfo->getHighlight(); if (highlight & ModInfo::HIGHLIGHT_IMPORTANT) return QBrush(Qt::darkRed); else if (highlight & ModInfo::HIGHLIGHT_INVALID) return QBrush(Qt::darkGray); - else if (highlight & ModInfo::HIGHLIGHT_PLUGIN) return QBrush(Qt::darkBlue); } else if (column == COL_VERSION) { if (!modInfo->getNewestVersion().isValid()) { return QVariant(); @@ -368,11 +367,11 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } else if ((role == Qt::BackgroundRole) || (role == ViewMarkingScrollBar::DEFAULT_ROLE)) { if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_PLUGIN) { - return QColor(0, 0, 255, 32); + return QColor(0, 0, 255, 64); } else if (m_Overwrite.find(modIndex) != m_Overwrite.end()) { - return QColor(0, 255, 0, 32); + return QColor(0, 255, 0, 64); } else if (m_Overwritten.find(modIndex) != m_Overwritten.end()) { - return QColor(255, 0, 0, 32); + return QColor(255, 0, 0, 64); } else { return QVariant(); } diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 76de436d..323cd98f 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -861,7 +861,7 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const } else if (role == Qt::BackgroundRole || (role == ViewMarkingScrollBar::DEFAULT_ROLE)) { if (m_ESPs[index].m_ModSelected) { - return QColor(0, 0, 255, 32); + return QColor(0, 0, 255, 64); } else { return QVariant(); } -- cgit v1.3.1 From fd51434e5ee9ef702eb904d0b77c937d6b147908 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 1 Aug 2018 20:04:59 +0200 Subject: Enabled Ctrl+C to copy selected stuff instead of the entire log. No one used it before and now it's much more practical. --- src/mainwindow.ui | 3 --- 1 file changed, 3 deletions(-) (limited to 'src') diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 8de34485..cf0f2aa4 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1561,9 +1561,6 @@ Right now this has very limited functionality Copy Log to Clipboard - - Ctrl+C - -- cgit v1.3.1