From 6b5680f6e4802b61cb77558565979b75761d5dbe Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 13 Apr 2018 14:46:04 +0100 Subject: Make pluginFileNames() include those of proxied plugins, too. --- src/plugincontainer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index c980c0a1..60d1130c 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -53,6 +53,12 @@ QStringList PluginContainer::pluginFileNames() const for (QPluginLoader *loader : m_PluginLoaders) { result.append(loader->fileName()); } + std::vector proxyList = bf::at_key(m_Plugins); + for (IPluginProxy *proxy : proxyList) { + QStringList proxiedPlugins = proxy->pluginList( + QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath())); + result.append(proxiedPlugins); + } return result; } -- cgit v1.3.1 From 25b6fe1471f665b86dc765232a0832daeffc8f8a Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 13 Apr 2018 14:49:59 +0100 Subject: Reload plugin translation databases when changing language. --- src/mainwindow.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index abcb5312..6cbe364e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3726,6 +3726,9 @@ void MainWindow::languageChange(const QString &newLanguage) installTranslator("qt"); installTranslator(ToQString(AppConfig::translationPrefix())); + for (const QString &fileName : m_PluginContainer.pluginFileNames()) { + installTranslator(QFileInfo(fileName).baseName()); + } ui->retranslateUi(this); qDebug("loaded language %s", qPrintable(newLanguage)); -- cgit v1.3.1 From cc2c4cc9187de096db2cf2e4cf5f00cfbe4f7064 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 15 Apr 2018 02:35:06 +0200 Subject: Fix for programs set to start from inside the top level of a mod folder not launching. --- src/organizercore.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 999c7732..20a16d35 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1232,12 +1232,17 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, if (virtualizedCwd || virtualizedBin) { if (virtualizedCwd) { int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1); - cwdPath = m_GamePlugin->dataDirectory().absolutePath() + cwdPath.mid(cwdOffset, -1); + cwdPath = m_GamePlugin->dataDirectory().absolutePath(); + if (cwdOffset >= 0) + cwdPath += cwdPath.mid(cwdOffset, -1); + } if (virtualizedBin) { int binOffset = binPath.indexOf('/', modsPath.length() + 1); - binPath = m_GamePlugin->dataDirectory().absolutePath() + binPath.mid(binOffset, -1); + binPath = m_GamePlugin->dataDirectory().absolutePath(); + if (binOffset>= 0) + binPath += binPath.mid(binOffset, -1); } QString cmdline -- cgit v1.3.1 From fa104b342f440a4fc1c80426e2b5e2c5905d4151 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 15 Apr 2018 02:54:16 +0200 Subject: Moved the Changelog to the detailed text to avoid the msgbox going below the scree. The readability is not graeat as QMessageBox does not allow resizing for some petty reason. --- src/selfupdater.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 8f962ba4..9706e1fb 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -170,10 +170,10 @@ void SelfUpdater::startUpdate() QMessageBox query(QMessageBox::Question, tr("New update available (%1)") - .arg(m_UpdateCandidate["tag_name"].toString()), - BBCode::convertToHTML(m_UpdateCandidate["body"].toString()), + .arg(m_UpdateCandidate["tag_name"].toString()), tr("Do you want to install update? All your mods and setup will be left untouched.\nSelect Show Details option to see the full change-log."), QMessageBox::Yes | QMessageBox::Cancel, m_Parent); - + + query.setDetailedText(m_UpdateCandidate["body"].toString()); query.button(QMessageBox::Yes)->setText(tr("Install")); int res = query.exec(); -- cgit v1.3.1 From ae4e4f9df84a547c554a8a68bb65b082adaae9f5 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 15 Apr 2018 12:59:04 +0100 Subject: Load qtbase_.qm when possible --- src/mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6cbe364e..23423194 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3725,6 +3725,7 @@ void MainWindow::languageChange(const QString &newLanguage) m_CurrentLanguage = newLanguage; installTranslator("qt"); + installTranslator("qtbase"); installTranslator(ToQString(AppConfig::translationPrefix())); for (const QString &fileName : m_PluginContainer.pluginFileNames()) { installTranslator(QFileInfo(fileName).baseName()); -- cgit v1.3.1 From 30c177cb371e92fe5e4cfc600cf27586402cf0a8 Mon Sep 17 00:00:00 2001 From: Silarn Date: Mon, 16 Apr 2018 12:47:17 -0500 Subject: Support for multi-game downloads --- src/downloadlistwidget.cpp | 4 +- src/downloadlistwidgetcompact.cpp | 4 +- src/downloadmanager.cpp | 76 ++++++++------- src/downloadmanager.h | 23 +++-- src/installationmanager.cpp | 18 +++- src/installationmanager.h | 2 +- src/main.cpp | 3 +- src/mainwindow.cpp | 62 ++++++------ src/mainwindow.h | 6 +- src/modinfo.cpp | 56 +++++++---- src/modinfo.h | 24 +++-- src/modinfobackup.cpp | 4 +- src/modinfobackup.h | 2 +- src/modinfodialog.cpp | 4 +- src/modinfoforeign.cpp | 5 +- src/modinfoforeign.h | 3 +- src/modinfooverwrite.cpp | 2 +- src/modinfooverwrite.h | 3 +- src/modinforegular.cpp | 32 ++++--- src/modinforegular.h | 17 +++- src/modinfowithconflictinfo.cpp | 4 +- src/modinfowithconflictinfo.h | 2 +- src/modlist.cpp | 23 ++++- src/modlist.h | 7 +- src/nexusinterface.cpp | 194 ++++++++++++++++++++++---------------- src/nexusinterface.h | 108 +++++++++------------ src/organizercore.cpp | 51 ++++++---- src/organizercore.h | 2 +- src/organizerproxy.cpp | 6 +- src/organizerproxy.h | 4 +- src/plugincontainer.cpp | 3 +- src/plugincontainer.h | 6 +- src/profile.cpp | 1 + src/selfupdater.cpp | 6 ++ src/selfupdater.h | 3 + src/settings.cpp | 4 +- src/settings.h | 3 +- src/settingsdialog.cpp | 6 +- src/settingsdialog.h | 6 +- 39 files changed, 466 insertions(+), 323 deletions(-) (limited to 'src') diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index 9a8ef572..cb7a49c3 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -88,8 +88,8 @@ void DownloadListWidgetDelegate::drawCache(QPainter *painter, const QStyleOption void DownloadListWidgetDelegate::paintPendingDownload(int downloadIndex) const { - std::pair nexusids = m_Manager->getPendingDownload(downloadIndex); - m_NameLabel->setText(tr("< mod %1 file %2 >").arg(nexusids.first).arg(nexusids.second)); + 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))); m_SizeLabel->setText("???"); m_InstallLabel->setVisible(true); m_InstallLabel->setText(tr("Pending")); diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index f7975150..b390bff4 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -88,8 +88,8 @@ void DownloadListWidgetCompactDelegate::drawCache(QPainter *painter, const QStyl void DownloadListWidgetCompactDelegate::paintPendingDownload(int downloadIndex) const { - std::pair nexusids = m_Manager->getPendingDownload(downloadIndex); - m_NameLabel->setText(tr("< mod %1 file %2 >").arg(nexusids.first).arg(nexusids.second)); + 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("???"); } diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 569a7d31..d54d6f24 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "nexusinterface.h" #include "nxmaccessmanager.h" #include "iplugingame.h" +#include "downloadmanager.h" #include #include #include "utility.h" @@ -113,15 +114,17 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(con info->m_Urls = metaFile.value("url", "").toString().split(";"); info->m_Tries = 0; info->m_TaskProgressId = TaskProgressManager::instance().getId(); + QString gameName = metaFile.value("gameName", "").toString(); int modID = metaFile.value("modID", 0).toInt(); int fileID = metaFile.value("fileID", 0).toInt(); - info->m_FileInfo = new ModRepositoryFileInfo(modID, fileID); + info->m_FileInfo = new ModRepositoryFileInfo(gameName, modID, fileID); info->m_FileInfo->name = metaFile.value("name", "").toString(); if (info->m_FileInfo->name == "0") { // bug in earlier version info->m_FileInfo->name = ""; } info->m_FileInfo->modName = metaFile.value("modName", "").toString(); + info->m_FileInfo->gameName = gameName; info->m_FileInfo->modID = modID; info->m_FileInfo->fileID = fileID; info->m_FileInfo->description = metaFile.value("description").toString(); @@ -260,6 +263,11 @@ void DownloadManager::setShowHidden(bool showHidden) refreshList(); } +void DownloadManager::setPluginContainer(PluginContainer *pluginContainer) +{ + m_NexusInterface->setPluginContainer(pluginContainer); +} + void DownloadManager::refreshList() { try { @@ -329,7 +337,7 @@ void DownloadManager::refreshList() } -bool DownloadManager::addDownload(const QStringList &URLs, +bool DownloadManager::addDownload(const QStringList &URLs, QString gameName, int modID, int fileID, const ModRepositoryFileInfo *fileInfo) { QString fileName = QFileInfo(URLs.first()).fileName(); @@ -340,7 +348,7 @@ bool DownloadManager::addDownload(const QStringList &URLs, QUrl preferredUrl = QUrl::fromEncoded(URLs.first().toLocal8Bit()); qDebug("selected download url: %s", qPrintable(preferredUrl.toString())); QNetworkRequest request(preferredUrl); - return addDownload(m_NexusInterface->getAccessManager()->get(request), URLs, fileName, modID, fileID, fileInfo); + return addDownload(m_NexusInterface->getAccessManager()->get(request), URLs, fileName, gameName, modID, fileID, fileInfo); } @@ -351,12 +359,12 @@ bool DownloadManager::addDownload(QNetworkReply *reply, const ModRepositoryFileI fileName = "unknown"; } - return addDownload(reply, QStringList(reply->url().toString()), fileName, fileInfo->modID, fileInfo->fileID, fileInfo); + return addDownload(reply, QStringList(reply->url().toString()), fileName, fileInfo->gameName, fileInfo->modID, fileInfo->fileID, fileInfo); } bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName, - int modID, int fileID, const ModRepositoryFileInfo *fileInfo) + QString gameName, int modID, int fileID, const ModRepositoryFileInfo *fileInfo) { // download invoked from an already open network reply (i.e. download link in the browser) DownloadInfo *newDownload = DownloadInfo::createNew(fileInfo, URLs); @@ -375,7 +383,7 @@ bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, (QMessageBox::question(nullptr, tr("Download again?"), tr("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."), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)) { - removePending(modID, fileID); + removePending(gameName, modID, fileID); delete newDownload; return false; } @@ -387,12 +395,12 @@ bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, } -void DownloadManager::removePending(int modID, int fileID) +void DownloadManager::removePending(QString gameName, int modID, int fileID) { emit aboutToUpdate(); - for (auto iter = m_PendingDownloads.begin(); iter != m_PendingDownloads.end(); ++iter) { - if ((iter->first == modID) && (iter->second == fileID)) { - m_PendingDownloads.erase(iter); + for (auto iter : m_PendingDownloads) { + if (gameName.compare(std::get<0>(iter), Qt::CaseInsensitive) == 0 && (std::get<1>(iter) == modID) && (std::get<2>(iter) == fileID)) { + m_PendingDownloads.removeAt(m_PendingDownloads.indexOf(iter)); break; } } @@ -431,7 +439,7 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl if (!resume) { newDownload->m_PreResumeSize = newDownload->m_Output.size(); - removePending(newDownload->m_FileInfo->modID, newDownload->m_FileInfo->fileID); + removePending(newDownload->m_FileInfo->gameName, newDownload->m_FileInfo->modID, newDownload->m_FileInfo->fileID); emit aboutToUpdate(); m_ActiveDownloads.append(newDownload); @@ -451,17 +459,19 @@ void DownloadManager::addNXMDownload(const QString &url) { NXMUrl nxmInfo(url); - QString managedGame = m_ManagedGame->gameShortName(); + QStringList validGames; + validGames.append(m_ManagedGame->gameShortName()); + validGames.append(m_ManagedGame->validShortNames()); qDebug("add nxm download: %s", qPrintable(url)); - if (nxmInfo.game().compare(managedGame, Qt::CaseInsensitive) != 0) { - qDebug("download requested for wrong game (game: %s, url: %s)", qPrintable(managedGame), qPrintable(nxmInfo.game())); + if (!validGames.contains(nxmInfo.game(), Qt::CaseInsensitive)) { + qDebug("download requested for wrong game (game: %s, url: %s)", qPrintable(m_ManagedGame->gameShortName()), qPrintable(nxmInfo.game())); QMessageBox::information(nullptr, tr("Wrong Game"), tr("The download link is for a mod for \"%1\" but this instance of MO " - "has been set up for \"%2\".").arg(nxmInfo.game()).arg(managedGame), QMessageBox::Ok); + "has been set up for \"%2\".").arg(nxmInfo.game()).arg(m_ManagedGame->gameShortName()), QMessageBox::Ok); return; } - for (auto pair : m_PendingDownloads) { - if (pair.first == nxmInfo.modId() && pair.second == nxmInfo.fileId()) { + for (auto tuple : m_PendingDownloads) { + if (std::get<0>(tuple).compare(nxmInfo.game(), Qt::CaseInsensitive) == 0, std::get<1>(tuple) == nxmInfo.modId() && std::get<2>(tuple) == nxmInfo.fileId()) { qDebug("download requested is already started (mod id: %s, file id: %s)", qPrintable(QString(nxmInfo.modId())), qPrintable(QString(nxmInfo.fileId()))); QMessageBox::information(nullptr, tr("Already Started"), tr("A download for this mod file has already been queued."), QMessageBox::Ok); return; @@ -483,11 +493,11 @@ void DownloadManager::addNXMDownload(const QString &url) emit aboutToUpdate(); - m_PendingDownloads.append(std::make_pair(nxmInfo.modId(), nxmInfo.fileId())); + m_PendingDownloads.append(std::make_tuple(nxmInfo.game(), nxmInfo.modId(), nxmInfo.fileId())); emit update(-1); emit downloadAdded(); - m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.modId(), nxmInfo.fileId(), this, nxmInfo.fileId(), "")); + m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.game(), nxmInfo.modId(), nxmInfo.fileId(), this, nxmInfo.fileId(), "")); } @@ -743,7 +753,7 @@ int DownloadManager::numPendingDownloads() const return m_PendingDownloads.size(); } -std::pair DownloadManager::getPendingDownload(int index) +std::tuple DownloadManager::getPendingDownload(int index) { if ((index < 0) || (index >= m_PendingDownloads.size())) { throw MyException(tr("get pending: invalid download index %1").arg(index)); @@ -1003,10 +1013,10 @@ void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadMana info->m_Reply->abort(); } break; case STATE_FETCHINGMODINFO: { - m_RequestIDs.insert(m_NexusInterface->requestDescription(info->m_FileInfo->modID, this, info->m_DownloadID, QString())); + m_RequestIDs.insert(m_NexusInterface->requestDescription(info->m_FileInfo->gameName, info->m_FileInfo->modID, this, info->m_DownloadID, QString())); } break; case STATE_FETCHINGFILEINFO: { - m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_FileInfo->modID, this, info->m_DownloadID, QString())); + m_RequestIDs.insert(m_NexusInterface->requestFiles(info->m_FileInfo->gameName, info->m_FileInfo->modID, this, info->m_DownloadID, QString())); } break; case STATE_READY: { createMetaFile(info); @@ -1080,6 +1090,7 @@ void DownloadManager::downloadReadyRead() void DownloadManager::createMetaFile(DownloadInfo *info) { 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); metaFile.setValue("fileID", info->m_FileInfo->fileID); metaFile.setValue("url", info->m_Urls.join(";")); @@ -1108,7 +1119,7 @@ void DownloadManager::createMetaFile(DownloadInfo *info) } -void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant resultData, int requestID) +void DownloadManager::nxmDescriptionAvailable(QString, int, QVariant userData, QVariant resultData, int requestID) { std::set::iterator idIter = m_RequestIDs.find(requestID); if (idIter == m_RequestIDs.end()) { @@ -1157,7 +1168,7 @@ static EFileCategory convertFileCategory(int id) } -void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultData, int requestID) +void DownloadManager::nxmFilesAvailable(QString, int, QVariant userData, QVariant resultData, int requestID) { std::set::iterator idIter = m_RequestIDs.find(requestID); if (idIter == m_RequestIDs.end()) { @@ -1237,7 +1248,7 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD } -void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID) +void DownloadManager::nxmFileInfoAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID) { std::set::iterator idIter = m_RequestIDs.find(requestID); if (idIter == m_RequestIDs.end()) { @@ -1260,11 +1271,12 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant userD info->description = BBCode::convertToHTML(result["description"].toString()); info->repository = "Nexus"; + info->gameName = gameName; info->modID = modID; info->fileID = fileID; QObject *test = info; - m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(test), QString())); + m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(gameName, modID, fileID, this, qVariantFromValue(test), QString())); } static int evaluateFileInfoMap(const QVariantMap &map, const std::map &preferredServers) @@ -1300,7 +1312,7 @@ bool DownloadManager::ServerByPreference(const std::map &preferred int DownloadManager::startDownloadURLs(const QStringList &urls) { ModRepositoryFileInfo info; - addDownload(urls, -1, -1, &info); + addDownload(urls, "", -1, -1, &info); return m_ActiveDownloads.size() - 1; } @@ -1326,7 +1338,7 @@ int DownloadManager::indexByName(const QString &fileName) const return -1; } -void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID) +void DownloadManager::nxmDownloadURLsAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID) { std::set::iterator idIter = m_RequestIDs.find(requestID); if (idIter == m_RequestIDs.end()) { @@ -1338,7 +1350,7 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u ModRepositoryFileInfo *info = qobject_cast(qvariant_cast(userData)); QVariantList resultList = resultData.toList(); if (resultList.length() == 0) { - removePending(modID, fileID); + removePending(gameName, modID, fileID); emit showMessage(tr("No download server available. Please try again later.")); return; } @@ -1352,11 +1364,11 @@ void DownloadManager::nxmDownloadURLsAvailable(int modID, int fileID, QVariant u foreach (const QVariant &server, resultList) { URLs.append(server.toMap()["URI"].toString()); } - addDownload(URLs, modID, fileID, info); + addDownload(URLs, gameName, modID, fileID, info); } -void DownloadManager::nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString) +void DownloadManager::nxmRequestFailed(QString gameName, int modID, int fileID, QVariant userData, int requestID, const QString &errorString) { std::set::iterator idIter = m_RequestIDs.find(requestID); if (idIter == m_RequestIDs.end()) { @@ -1381,7 +1393,7 @@ void DownloadManager::nxmRequestFailed(int modID, int fileID, QVariant userData, } } - removePending(modID, fileID); + removePending(gameName, modID, fileID); emit showMessage(tr("Failed to request file info from nexus: %1").arg(errorString)); } diff --git a/src/downloadmanager.h b/src/downloadmanager.h index a6d3b20c..780aeb5c 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -38,6 +38,7 @@ along with Mod Organizer. If not, see . namespace MOBase { class IPluginGame; } class NexusInterface; +class PluginContainer; /*! * \brief manages downloading of files and provides progress information for gui elements @@ -162,6 +163,8 @@ public: */ void setShowHidden(bool showHidden); + void setPluginContainer(PluginContainer *pluginContainer); + /** * @brief download from an already open network connection * @@ -179,7 +182,7 @@ public: * @param fileInfo information previously retrieved from the nexus network * @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again **/ - bool addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName, int modID, int fileID = 0, const MOBase::ModRepositoryFileInfo *fileInfo = new MOBase::ModRepositoryFileInfo()); + bool addDownload(QNetworkReply *reply, const QStringList &URLs, const QString &fileName, QString gameName, int modID, int fileID = 0, const MOBase::ModRepositoryFileInfo *fileInfo = new MOBase::ModRepositoryFileInfo()); /** * @brief start a download using a nxm-link @@ -209,7 +212,7 @@ public: * @param index index of the pending download (index in the range [0, numPendingDownloads()[) * @return pair of modid, fileid */ - std::pair getPendingDownload(int index); + std::tuple getPendingDownload(int index); /** * @brief retrieve the full path to the download specified by index @@ -411,15 +414,15 @@ public slots: void queryInfo(int index); - void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID); + void nxmDescriptionAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); - void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID); + void nxmFilesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); - void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmFileInfoAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmDownloadURLsAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString); + void nxmRequestFailed(QString gameName, int modID, int fileID, QVariant userData, int requestID, const QString &errorString); void managedGameChanged(MOBase::IPluginGame const *gamePlugin); @@ -462,7 +465,7 @@ private: * @param fileInfo information previously retrieved from the mod page * @return true if the download was started, false if it wasn't. The latter currently only happens if there is a duplicate and the user decides not to download again **/ - bool addDownload(const QStringList &URLs, int modID, int fileID, const MOBase::ModRepositoryFileInfo *fileInfo); + bool addDownload(const QStringList &URLs, QString gameName, int modID, int fileID, const MOBase::ModRepositoryFileInfo *fileInfo); // important: the caller has to lock the list-mutex, otherwise the DownloadInfo-pointer might get invalidated at any time DownloadInfo *findDownload(QObject *reply, int *index = nullptr) const; @@ -481,7 +484,7 @@ private: QDateTime matchDate(const QString &timeString); - void removePending(int modID, int fileID); + void removePending(QString gameName, int modID, int fileID); static QString getFileTypeString(int fileType); @@ -493,7 +496,7 @@ private: NexusInterface *m_NexusInterface; - QVector > m_PendingDownloads; + QVector> m_PendingDownloads; QVector m_ActiveDownloads; diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 9a70f8bd..6785bf67 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -537,7 +537,7 @@ bool InstallationManager::ensureValidModName(GuessedValue &name) const return true; } -bool InstallationManager::doInstall(GuessedValue &modName, int modID, +bool InstallationManager::doInstall(GuessedValue &modName, QString gameName, int modID, const QString &version, const QString &newestVersion, int categoryID, const QString &repository) { @@ -582,6 +582,9 @@ bool InstallationManager::doInstall(GuessedValue &modName, int modID, QSettings settingsFile(targetDirectory + "/meta.ini", QSettings::IniFormat); // overwrite settings only if they are actually are available or haven't been set before + if ((gameName != "") || !settingsFile.contains("gameName")) { + settingsFile.setValue("gameName", gameName); + } if ((modID != 0) || !settingsFile.contains("modid")) { settingsFile.setValue("modid", modID); } @@ -662,6 +665,7 @@ bool InstallationManager::install(const QString &fileName, modName.update(QFileInfo(fileName).completeBaseName(), GUESS_FALLBACK); // read out meta information from the download if available + QString gameName = ""; int modID = 0; QString version = ""; QString newestVersion = ""; @@ -671,6 +675,7 @@ bool InstallationManager::install(const QString &fileName, QString metaName = fileName + ".meta"; if (QFile(metaName).exists()) { QSettings metaFile(metaName, QSettings::IniFormat); + gameName = metaFile.value("gameName", "").toString(); modID = metaFile.value("modID", 0).toInt(); QTextDocument doc; doc.setHtml(metaFile.value("name", "").toString()); @@ -759,7 +764,7 @@ bool InstallationManager::install(const QString &fileName, mapToArchive(filesTree.data()); // the simple installer only prepares the installation, the rest // works the same for all installers - if (!doInstall(modName, modID, version, newestVersion, categoryID, + if (!doInstall(modName, gameName, modID, version, newestVersion, categoryID, repository)) { installResult = IPluginInstaller::RESULT_FAILED; } @@ -780,6 +785,15 @@ bool InstallationManager::install(const QString &fileName, if (installerExt.find(fileInfo.suffix()) != installerExt.end()) { installResult = installerCustom->install(modName, fileName, version, modID); + if (installResult == IPluginInstaller::RESULT_SUCCESS) { + mapToArchive(filesTree.data()); + // the simple installer only prepares the installation, the rest + // works the same for all installers + if (!doInstall(modName, gameName, modID, version, newestVersion, categoryID, + repository)) { + installResult = IPluginInstaller::RESULT_FAILED; + } + } unsigned int idx = ModInfo::getIndex(modName); if (idx != UINT_MAX) { ModInfo::Ptr info = ModInfo::getByIndex(idx); diff --git a/src/installationmanager.h b/src/installationmanager.h index e1ebb519..bdf71a8d 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -168,7 +168,7 @@ private: bool isSimpleArchiveTopLayer(const MOBase::DirectoryTree::Node *node, bool bainStyle); MOBase::DirectoryTree::Node *getSimpleArchiveBase(MOBase::DirectoryTree *dataTree); - bool doInstall(MOBase::GuessedValue &modName, + bool doInstall(MOBase::GuessedValue &modName, QString gameName, int modID, const QString &version, const QString &newestVersion, int categoryID, const QString &repository); QString generateBackupName(const QString &directoryName) const; diff --git a/src/main.cpp b/src/main.cpp index fc332fe7..d67c2673 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,6 +46,7 @@ along with Mod Organizer. If not, see . #include "nxmaccessmanager.h" #include "instancemanager.h" #include "moshortcut.h" +#include "organizercore.h" #include #include @@ -602,7 +603,7 @@ int runApplication(MOApplication &application, SingleInstance &instance, QSplashScreen splash(pixmap); splash.show(); - NexusInterface::instance()->getAccessManager()->startLoginCheck(); + NexusInterface::instance(&pluginContainer)->getAccessManager()->startLoginCheck(); qDebug("initializing tutorials"); TutorialManager::init( diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6cbe364e..171026cf 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -266,6 +266,7 @@ MainWindow::MainWindow(QSettings &initSettings // hide these columns by default ui->modList->header()->setSectionHidden(ModList::COL_CONTENT, true); ui->modList->header()->setSectionHidden(ModList::COL_MODID, true); + ui->modList->header()->setSectionHidden(ModList::COL_GAME, true); ui->modList->header()->setSectionHidden(ModList::COL_INSTALLTIME, true); } @@ -343,11 +344,11 @@ MainWindow::MainWindow(QSettings &initSettings connect(m_OrganizerCore.updater(), SIGNAL(updateAvailable()), this, SLOT(updateAvailable())); connect(m_OrganizerCore.updater(), SIGNAL(motdAvailable(QString)), this, SLOT(motdReceived(QString))); - connect(NexusInterface::instance(), SIGNAL(requestNXMDownload(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); - connect(NexusInterface::instance(), SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(int,int,QVariant,QVariant,int))); - connect(NexusInterface::instance(), SIGNAL(needLogin()), &m_OrganizerCore, SLOT(nexusLogin())); - connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); - connect(NexusInterface::instance()->getAccessManager(), SIGNAL(credentialsReceived(const QString&, bool)), + connect(NexusInterface::instance(&pluginContainer), SIGNAL(requestNXMDownload(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); + connect(NexusInterface::instance(&pluginContainer), SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(int,int,QVariant,QVariant,int))); + connect(NexusInterface::instance(&pluginContainer), SIGNAL(needLogin()), &m_OrganizerCore, SLOT(nexusLogin())); + connect(NexusInterface::instance(&pluginContainer)->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); + connect(NexusInterface::instance(&pluginContainer)->getAccessManager(), SIGNAL(credentialsReceived(const QString&, bool)), this, SLOT(updateWindowTitle(const QString&, bool))); connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString))); @@ -2347,7 +2348,7 @@ void MainWindow::reinstallMod_clicked() void MainWindow::resumeDownload(int downloadIndex) { - if (NexusInterface::instance()->getAccessManager()->loggedIn()) { + if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->loggedIn()) { m_OrganizerCore.downloadManager()->resumeDownload(downloadIndex); } else { QString username, password; @@ -2355,7 +2356,7 @@ void MainWindow::resumeDownload(int downloadIndex) m_OrganizerCore.doAfterLogin([this, downloadIndex] () { this->resumeDownload(downloadIndex); }); - NexusInterface::instance()->getAccessManager()->login(username, password); + NexusInterface::instance(&m_PluginContainer)->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to resume a download"), this); } @@ -2365,13 +2366,13 @@ void MainWindow::resumeDownload(int downloadIndex) void MainWindow::endorseMod(ModInfo::Ptr mod) { - if (NexusInterface::instance()->getAccessManager()->loggedIn()) { + if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->loggedIn()) { mod->endorse(true); } else { QString username, password; if (m_OrganizerCore.settings().getNexusLogin(username, password)) { m_OrganizerCore.doAfterLogin(boost::bind(&MainWindow::endorseMod, this, mod)); - NexusInterface::instance()->getAccessManager()->login(username, password); + NexusInterface::instance(&m_PluginContainer)->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); } @@ -2393,12 +2394,12 @@ void MainWindow::dontendorse_clicked() void MainWindow::unendorse_clicked() { QString username, password; - if (NexusInterface::instance()->getAccessManager()->loggedIn()) { + if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->loggedIn()) { ModInfo::getByIndex(m_ContextRow)->endorse(false); } else { if (m_OrganizerCore.settings().getNexusLogin(username, password)) { m_OrganizerCore.doAfterLogin([this] () { this->unendorse_clicked(); }); - NexusInterface::instance()->getAccessManager()->login(username, password); + NexusInterface::instance(&m_PluginContainer)->getAccessManager()->login(username, password); } else { MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); } @@ -2574,8 +2575,9 @@ void MainWindow::ignoreMissingData_clicked() void MainWindow::visitOnNexus_clicked() { int modID = m_OrganizerCore.modList()->data(m_OrganizerCore.modList()->index(m_ContextRow, 0), Qt::UserRole).toInt(); + QString gameName = m_OrganizerCore.modList()->data(m_OrganizerCore.modList()->index(m_ContextRow, 0), Qt::UserRole + 4).toString(); if (modID > 0) { - nexusLinkActivated(NexusInterface::instance()->getModURL(modID)); + nexusLinkActivated(NexusInterface::instance(&m_PluginContainer)->getModURL(modID, gameName)); } else { MessageDialog::showMessage(tr("Nexus ID for this Mod is unknown"), this); } @@ -2921,18 +2923,18 @@ void MainWindow::saveArchiveList() void MainWindow::checkModsForUpdates() { statusBar()->show(); - if (NexusInterface::instance()->getAccessManager()->loggedIn()) { - m_ModsToUpdate = ModInfo::checkAllForUpdate(this); + if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->loggedIn()) { + m_ModsToUpdate = ModInfo::checkAllForUpdate(&m_PluginContainer, this); m_RefreshProgress->setRange(0, m_ModsToUpdate); } else { QString username, password; if (m_OrganizerCore.settings().getNexusLogin(username, password)) { m_OrganizerCore.doAfterLogin([this] () { this->checkModsForUpdates(); }); - NexusInterface::instance()->getAccessManager()->login(username, password); + NexusInterface::instance(&m_PluginContainer)->getAccessManager()->login(username, password); } else { // otherwise there will be no endorsement info MessageDialog::showMessage(tr("Not logged in, endorsement information will be wrong"), this, true); - m_ModsToUpdate = ModInfo::checkAllForUpdate(this); + m_ModsToUpdate = ModInfo::checkAllForUpdate(&m_PluginContainer, this); } } } @@ -3207,7 +3209,7 @@ void MainWindow::exportModListCSV() if (nexus_ID->isChecked()) builder.setRowField("#Nexus_ID", info->getNexusID()); if (mod_Nexus_URL->isChecked()) - builder.setRowField("#Mod_Nexus_URL",(info->getNexusID()>0)? NexusInterface::instance()->getModURL(info->getNexusID()) : ""); + builder.setRowField("#Mod_Nexus_URL",(info->getNexusID()>0)? NexusInterface::instance(&m_PluginContainer)->getModURL(info->getNexusID(), info->getGameName()) : ""); if (mod_Version->isChecked()) builder.setRowField("#Mod_Version", info->getVersion().canonicalString()); if (install_Date->isChecked()) @@ -3629,7 +3631,7 @@ void MainWindow::on_actionSettings_triggered() bool oldDisplayForeign(settings.displayForeign()); bool proxy = settings.useProxy(); - settings.query(this); + settings.query(&m_PluginContainer, this); InstallationManager *instManager = m_OrganizerCore.installationManager(); instManager->setModsDirectory(settings.getModDirectory()); @@ -3661,14 +3663,14 @@ void MainWindow::on_actionSettings_triggered() } if (settings.getCacheDirectory() != oldCacheDirectory) { - NexusInterface::instance()->setCacheDirectory(settings.getCacheDirectory()); + NexusInterface::instance(&m_PluginContainer)->setCacheDirectory(settings.getCacheDirectory()); } if (proxy != settings.useProxy()) { activateProxy(settings.useProxy()); } - NexusInterface::instance()->setNMMVersion(settings.getNMMVersion()); + NexusInterface::instance(&m_PluginContainer)->setNMMVersion(settings.getNMMVersion()); updateDownloadListDelegate(); @@ -3680,7 +3682,7 @@ void MainWindow::on_actionSettings_triggered() void MainWindow::on_actionNexus_triggered() { ::ShellExecuteW(nullptr, L"open", - NexusInterface::instance()->getGameURL().toStdWString().c_str(), + NexusInterface::instance(&m_PluginContainer)->getGameURL(m_OrganizerCore.managedGame()->gameShortName()).toStdWString().c_str(), nullptr, nullptr, SW_SHOWNORMAL); } @@ -4099,10 +4101,10 @@ void MainWindow::on_actionEndorseMO_triggered() { if (QMessageBox::question(this, tr("Endorse Mod Organizer"), tr("Do you want to endorse Mod Organizer on %1 now?").arg( - NexusInterface::instance()->getGameURL()), + NexusInterface::instance(&m_PluginContainer)->getGameURL(m_OrganizerCore.managedGame()->gameShortName())), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - NexusInterface::instance()->requestToggleEndorsement( - m_OrganizerCore.managedGame()->nexusModOrganizerID(), true, this, QVariant(), QString()); + NexusInterface::instance(&m_PluginContainer)->requestToggleEndorsement( + m_OrganizerCore.managedGame()->gameShortName(), m_OrganizerCore.managedGame()->nexusModOrganizerID(), true, this, QVariant(), QString()); } } @@ -4174,7 +4176,7 @@ void MainWindow::nxmUpdatesAvailable(const std::vector &modIDs, QVariant us for (auto iter = info.begin(); iter != info.end(); ++iter) { (*iter)->setNewestVersion(result["version"].toString()); (*iter)->setNexusDescription(result["description"].toString()); - if (NexusInterface::instance()->getAccessManager()->loggedIn() && + if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->loggedIn() && result.contains("voted_by_user")) { // don't use endorsement info if we're not logged in or if the response doesn't contain it (*iter)->setIsEndorsed(result["voted_by_user"].toBool()); @@ -4197,20 +4199,20 @@ void MainWindow::nxmUpdatesAvailable(const std::vector &modIDs, QVariant us } } -void MainWindow::nxmEndorsementToggled(int, QVariant, QVariant resultData, int) +void MainWindow::nxmEndorsementToggled(QString, int, QVariant, QVariant resultData, int) { if (resultData.toBool()) { ui->actionEndorseMO->setVisible(false); QMessageBox::question(this, tr("Thank you!"), tr("Thank you for your endorsement!")); } - if (!disconnect(sender(), SIGNAL(nxmEndorsementToggled(int, QVariant, QVariant, int)), - this, SLOT(nxmEndorsementToggled(int, QVariant, QVariant, int)))) { + if (!disconnect(sender(), SIGNAL(nxmEndorsementToggled(QString, int, QVariant, QVariant, int)), + this, SLOT(nxmEndorsementToggled(QString, int, QVariant, QVariant, int)))) { qCritical("failed to disconnect endorsement slot"); } } -void MainWindow::nxmDownloadURLs(int, int, QVariant, QVariant resultData, int) +void MainWindow::nxmDownloadURLs(QString, int, int, QVariant, QVariant resultData, int) { QVariantList serverList = resultData.toList(); @@ -4229,7 +4231,7 @@ void MainWindow::nxmDownloadURLs(int, int, QVariant, QVariant resultData, int) } -void MainWindow::nxmRequestFailed(int modID, int, QVariant, int, const QString &errorString) +void MainWindow::nxmRequestFailed(QString, int modID, int, QVariant, int, const QString &errorString) { if (modID == -1) { // must be the update-check that failed diff --git a/src/mainwindow.h b/src/mainwindow.h index ef6eb262..d684dbc0 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -461,9 +461,9 @@ private slots: void modInstalled(const QString &modName); void nxmUpdatesAvailable(const std::vector &modIDs, QVariant userData, QVariant resultData, int requestID); - void nxmEndorsementToggled(int, QVariant, QVariant resultData, int); - void nxmDownloadURLs(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString); + void nxmEndorsementToggled(QString, int, QVariant, QVariant resultData, int); + void nxmDownloadURLs(QString, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmRequestFailed(QString, int modID, int fileID, QVariant userData, int requestID, const QString &errorString); void editCategories(); void deselectFilters(); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index d80c636b..2cd3aec7 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -61,16 +61,16 @@ bool ModInfo::ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS) } -ModInfo::Ptr ModInfo::createFrom(const QDir &dir, DirectoryEntry **directoryStructure) +ModInfo::Ptr ModInfo::createFrom(PluginContainer *pluginContainer, QString gameName, const QDir &dir, DirectoryEntry **directoryStructure) { QMutexLocker locker(&s_Mutex); // int id = s_NextID++; static QRegExp backupExp(".*backup[0-9]*"); ModInfo::Ptr result; if (backupExp.exactMatch(dir.dirName())) { - result = ModInfo::Ptr(new ModInfoBackup(dir, directoryStructure)); + result = ModInfo::Ptr(new ModInfoBackup(pluginContainer, gameName, dir, directoryStructure)); } else { - result = ModInfo::Ptr(new ModInfoRegular(dir, directoryStructure)); + result = ModInfo::Ptr(new ModInfoRegular(pluginContainer, gameName, dir, directoryStructure)); } s_Collection.push_back(result); return result; @@ -80,10 +80,11 @@ ModInfo::Ptr ModInfo::createFromPlugin(const QString &modName, const QString &espName, const QStringList &bsaNames, ModInfo::EModType modType, - DirectoryEntry **directoryStructure) { + DirectoryEntry **directoryStructure, + PluginContainer *pluginContainer) { QMutexLocker locker(&s_Mutex); ModInfo::Ptr result = ModInfo::Ptr( - new ModInfoForeign(modName, espName, bsaNames, modType, directoryStructure)); + new ModInfoForeign(modName, espName, bsaNames, modType, directoryStructure, pluginContainer)); s_Collection.push_back(result); return result; } @@ -105,11 +106,11 @@ QString ModInfo::getContentTypeName(int contentType) } } -void ModInfo::createFromOverwrite() +void ModInfo::createFromOverwrite(PluginContainer *pluginContainer) { QMutexLocker locker(&s_Mutex); - s_Collection.push_back(ModInfo::Ptr(new ModInfoOverwrite)); + s_Collection.push_back(ModInfo::Ptr(new ModInfoOverwrite(pluginContainer))); } unsigned int ModInfo::getNumMods() @@ -207,6 +208,7 @@ unsigned int ModInfo::findMod(const boost::function &filter void ModInfo::updateFromDisc(const QString &modDirectory, DirectoryEntry **directoryStructure, + PluginContainer *pluginContainer, bool displayForeign, MOBase::IPluginGame const *game) { @@ -219,7 +221,7 @@ void ModInfo::updateFromDisc(const QString &modDirectory, mods.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QDirIterator modIter(mods); while (modIter.hasNext()) { - createFrom(QDir(modIter.next()), directoryStructure); + createFrom(pluginContainer, game->gameShortName(), QDir(modIter.next()), directoryStructure); } } @@ -233,11 +235,12 @@ void ModInfo::updateFromDisc(const QString &modDirectory, unmanaged->referenceFile(modName).absoluteFilePath(), unmanaged->secondaryFiles(modName), modType, - directoryStructure); + directoryStructure, + pluginContainer); } } - createFromOverwrite(); + createFromOverwrite(pluginContainer); std::sort(s_Collection.begin(), s_Collection.end(), ModInfo::ByName); @@ -259,21 +262,21 @@ void ModInfo::updateIndices() } -ModInfo::ModInfo() +ModInfo::ModInfo(PluginContainer *pluginContainer) : m_Valid(false), m_PrimaryCategory(-1) { } -void ModInfo::checkChunkForUpdate(const std::vector &modIDs, QObject *receiver) +void ModInfo::checkChunkForUpdate(PluginContainer *pluginContainer, const std::vector &modIDs, QObject *receiver, QString gameName) { if (modIDs.size() != 0) { - NexusInterface::instance()->requestUpdates(modIDs, receiver, QVariant(), QString()); + NexusInterface::instance(pluginContainer)->requestUpdates(modIDs, receiver, QVariant(), gameName, QString()); } } -int ModInfo::checkAllForUpdate(QObject *receiver) +int ModInfo::checkAllForUpdate(PluginContainer *pluginContainer, QObject *receiver) { // technically this should be 255 but those requests can take nexus fairly long, produce // large output and may have been the cause of issue #1166 @@ -284,20 +287,33 @@ int ModInfo::checkAllForUpdate(QObject *receiver) //I ought to store this, it's used elsewhere IPluginGame const *game = qApp->property("managed_game").value(); - modIDs.push_back(game->nexusModOrganizerID()); + checkChunkForUpdate(pluginContainer, modIDs, receiver, game->gameShortName()); - for (const ModInfo::Ptr &mod : s_Collection) { + std::multimap> organizedGames; + for (auto mod : s_Collection) { if (mod->canBeUpdated()) { - modIDs.push_back(mod->getNexusID()); - if (modIDs.size() >= chunkSize) { - checkChunkForUpdate(modIDs, receiver); + organizedGames.insert(std::pair>(mod->getGameName(), mod)); + } + } + + QString currentGame = ""; + for (auto game : organizedGames) { + if (currentGame != game.first) { + if (currentGame != "") { + checkChunkForUpdate(pluginContainer, modIDs, receiver, currentGame); modIDs.clear(); } + currentGame = game.first; + } + modIDs.push_back(game.second->getNexusID()); + if (modIDs.size() >= chunkSize) { + checkChunkForUpdate(pluginContainer, modIDs, receiver, currentGame); + modIDs.clear(); } } - checkChunkForUpdate(modIDs, receiver); + checkChunkForUpdate(pluginContainer, modIDs, receiver, currentGame); return result; } diff --git a/src/modinfo.h b/src/modinfo.h index 1ceb8e1f..f321f068 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -23,6 +23,8 @@ along with Mod Organizer. If not, see . #include "imodinterface.h" #include "versioninfo.h" +class PluginContainer; + class QDateTime; class QDir; #include @@ -115,6 +117,7 @@ public: **/ static void updateFromDisc(const QString &modDirectory, MOShared::DirectoryEntry **directoryStructure, + PluginContainer *pluginContainer, bool displayForeign, MOBase::IPluginGame const *game); @@ -176,19 +179,19 @@ public: * @param modIDs list of mods (Nexus Mod IDs) to check for updates * @return */ - static void checkChunkForUpdate(const std::vector &modIDs, QObject *receiver); + static void checkChunkForUpdate(PluginContainer *pluginContainer, const std::vector &modIDs, QObject *receiver, QString gameName); /** * @brief query nexus information for every mod and update the "newest version" information **/ - static int checkAllForUpdate(QObject *receiver); + static int checkAllForUpdate(PluginContainer *pluginContainer, QObject *receiver); /** * @brief create a new mod from the specified directory and add it to the collection * @param dir directory to create from * @return pointer to the info-structure of the newly created/added mod */ - static ModInfo::Ptr createFrom(const QDir &dir, MOShared::DirectoryEntry **directoryStructure); + static ModInfo::Ptr createFrom(PluginContainer *pluginContainer, QString gameName, const QDir &dir, MOShared::DirectoryEntry **directoryStructure); /** * @brief create a new "foreign-managed" mod from a tuple of plugin and archives @@ -196,7 +199,7 @@ public: * @param bsaNames names of archives * @return a new mod */ - static ModInfo::Ptr createFromPlugin(const QString &modName, const QString &espName, const QStringList &bsaNames, ModInfo::EModType modType, MOShared::DirectoryEntry **directoryStructure); + static ModInfo::Ptr createFromPlugin(const QString &modName, const QString &espName, const QStringList &bsaNames, ModInfo::EModType modType, MOShared::DirectoryEntry **directoryStructure, PluginContainer *pluginContainer); /** * @brief retieve a name for one of the CONTENT_ enums @@ -408,7 +411,14 @@ public: * * @return the nexus mod id. may be 0 if the mod id isn't known or doesn't exist **/ - virtual int getNexusID() const = 0; + virtual int getNexusID() const = 0; + + /** + * @brief getter for the source game repository + * + * @return the source game repository. should default to the active game. + **/ + virtual QString getGameName() const = 0; /** * @return the fixed priority of mods of this type or INT_MIN if the priority of mods @@ -596,14 +606,14 @@ signals: protected: - ModInfo(); + ModInfo(PluginContainer *pluginContainer); static void updateIndices(); static bool ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS); private: - static void createFromOverwrite(); + static void createFromOverwrite(PluginContainer *pluginContainer); protected: diff --git a/src/modinfobackup.cpp b/src/modinfobackup.cpp index a57b84fe..b779dca1 100644 --- a/src/modinfobackup.cpp +++ b/src/modinfobackup.cpp @@ -14,7 +14,7 @@ QString ModInfoBackup::getDescription() const } -ModInfoBackup::ModInfoBackup(const QDir &path, MOShared::DirectoryEntry **directoryStructure) - : ModInfoRegular(path, directoryStructure) +ModInfoBackup::ModInfoBackup(PluginContainer *pluginContainer, QString gameName, const QDir &path, MOShared::DirectoryEntry **directoryStructure) + : ModInfoRegular(pluginContainer, gameName, path, directoryStructure) { } diff --git a/src/modinfobackup.h b/src/modinfobackup.h index adbac783..aa59dda4 100644 --- a/src/modinfobackup.h +++ b/src/modinfobackup.h @@ -32,7 +32,7 @@ public: private: - ModInfoBackup(const QDir &path, MOShared::DirectoryEntry **directoryStructure); + ModInfoBackup(PluginContainer *pluginContainer, QString gameName, const QDir &path, MOShared::DirectoryEntry **directoryStructure); }; diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 6a53d915..31ffe82d 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -714,7 +714,7 @@ void ModInfoDialog::linkClicked(const QUrl &url) { //Ideally we'd ask the mod for the game and the web service then pass the game //and URL to the web service - if (NexusInterface::instance()->isURLGameRelated(url)) { + if (NexusInterface::instance(m_PluginContainer)->isURLGameRelated(url)) { this->close(); emit nexusLinkActivated(url.toString()); } else { @@ -860,7 +860,7 @@ void ModInfoDialog::activateNexusTab() QLineEdit *modIDEdit = findChild("modIDEdit"); int modID = modIDEdit->text().toInt(); if (modID != 0) { - QString nexusLink = NexusInterface::instance()->getModURL(modID); + QString nexusLink = NexusInterface::instance(m_PluginContainer)->getModURL(modID, m_ModInfo->getGameName()); QLabel *visitNexusLabel = findChild("visitNexusLabel"); visitNexusLabel->setText(tr("Visit on Nexus").arg(nexusLink)); visitNexusLabel->setToolTip(nexusLink); diff --git a/src/modinfoforeign.cpp b/src/modinfoforeign.cpp index 6ac66c67..b599d4eb 100644 --- a/src/modinfoforeign.cpp +++ b/src/modinfoforeign.cpp @@ -51,8 +51,9 @@ ModInfoForeign::ModInfoForeign(const QString &modName, const QString &referenceFile, const QStringList &archives, ModInfo::EModType modType, - DirectoryEntry **directoryStructure) - : ModInfoWithConflictInfo(directoryStructure), + DirectoryEntry **directoryStructure, + PluginContainer *pluginContainer) + : ModInfoWithConflictInfo(pluginContainer, directoryStructure), m_ReferenceFile(referenceFile), m_Archives(archives) { m_CreationTime = QFileInfo(referenceFile).created(); switch (modType) { diff --git a/src/modinfoforeign.h b/src/modinfoforeign.h index d60064f0..23500df8 100644 --- a/src/modinfoforeign.h +++ b/src/modinfoforeign.h @@ -37,6 +37,7 @@ public: virtual QString absolutePath() const; virtual MOBase::VersionInfo getNewestVersion() const { return QString(); } virtual QString getInstallationFile() const { return ""; } + virtual QString getGameName() const { return ""; } virtual int getNexusID() const { return -1; } virtual std::vector getIniTweaks() const { return std::vector(); } virtual std::vector getFlags() const; @@ -53,7 +54,7 @@ public: protected: ModInfoForeign(const QString &modName, const QString &referenceFile, const QStringList &archives, ModInfo::EModType modType, - MOShared::DirectoryEntry **directoryStructure); + MOShared::DirectoryEntry **directoryStructure, PluginContainer *pluginContainer); private: QString m_Name; diff --git a/src/modinfooverwrite.cpp b/src/modinfooverwrite.cpp index 360212c0..742ae384 100644 --- a/src/modinfooverwrite.cpp +++ b/src/modinfooverwrite.cpp @@ -6,7 +6,7 @@ #include #include -ModInfoOverwrite::ModInfoOverwrite() +ModInfoOverwrite::ModInfoOverwrite(PluginContainer *pluginContainer) : ModInfo(pluginContainer) { testValid(); } diff --git a/src/modinfooverwrite.h b/src/modinfooverwrite.h index b6cdfb43..360c7335 100644 --- a/src/modinfooverwrite.h +++ b/src/modinfooverwrite.h @@ -40,6 +40,7 @@ public: virtual MOBase::VersionInfo getNewestVersion() const { return QString(); } virtual QString getInstallationFile() const { return ""; } virtual int getFixedPriority() const { return INT_MAX; } + virtual QString getGameName() const { return ""; } virtual int getNexusID() const { return -1; } virtual std::vector getIniTweaks() const { return std::vector(); } virtual std::vector getFlags() const; @@ -52,7 +53,7 @@ public: private: - ModInfoOverwrite(); + ModInfoOverwrite(PluginContainer *pluginContainer); }; diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index 4572f5bf..69718705 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -24,26 +24,27 @@ namespace { } } -ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStructure) - : ModInfoWithConflictInfo(directoryStructure) +ModInfoRegular::ModInfoRegular(PluginContainer *pluginContainer, QString gameName, const QDir &path, DirectoryEntry **directoryStructure) + : ModInfoWithConflictInfo(pluginContainer, directoryStructure) + , m_GameName(gameName) , m_Name(path.dirName()) , m_Path(path.absolutePath()) , m_Repository() , m_MetaInfoChanged(false) , m_EndorsedState(ENDORSED_UNKNOWN) - , m_NexusBridge() + , m_NexusBridge(pluginContainer) { testValid(); m_CreationTime = QFileInfo(path.absolutePath()).created(); // read out the meta-file for information readMeta(); - connect(&m_NexusBridge, SIGNAL(descriptionAvailable(int,QVariant,QVariant)) - , this, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant))); - connect(&m_NexusBridge, SIGNAL(endorsementToggled(int,QVariant,QVariant)) - , this, SLOT(nxmEndorsementToggled(int,QVariant,QVariant))); - connect(&m_NexusBridge, SIGNAL(requestFailed(int,int,QVariant,QString)) - , this, SLOT(nxmRequestFailed(int,int,QVariant,QString))); + connect(&m_NexusBridge, SIGNAL(descriptionAvailable(QString,int,QVariant,QVariant)) + , this, SLOT(nxmDescriptionAvailable(QString,int,QVariant,QVariant))); + connect(&m_NexusBridge, SIGNAL(endorsementToggled(QString,int,QVariant,QVariant)) + , this, SLOT(nxmEndorsementToggled(QString,int,QVariant,QVariant))); + connect(&m_NexusBridge, SIGNAL(requestFailed(QString,int,int,QVariant,QString)) + , this, SLOT(nxmRequestFailed(QString,int,int,QVariant,QString))); } @@ -71,6 +72,8 @@ void ModInfoRegular::readMeta() { QSettings metaFile(m_Path + "/meta.ini", QSettings::IniFormat); m_Notes = metaFile.value("notes", "").toString(); + QString tempGameName = metaFile.value("gameName", m_GameName).toString(); + if (tempGameName != "") m_GameName = tempGameName; m_NexusID = metaFile.value("modid", -1).toInt(); m_Version.parse(metaFile.value("version", "").toString()); m_NewestVersion = metaFile.value("newestVersion", "").toString(); @@ -135,6 +138,7 @@ void ModInfoRegular::saveMeta() metaFile.setValue("version", m_Version.canonicalString()); metaFile.setValue("installationFile", m_InstallationFile); metaFile.setValue("repository", m_Repository); + metaFile.setValue("gameName", m_GameName); metaFile.setValue("modid", m_NexusID); metaFile.setValue("notes", m_Notes); metaFile.setValue("nexusDescription", m_NexusDescription); @@ -185,7 +189,7 @@ bool ModInfoRegular::downgradeAvailable() const } -void ModInfoRegular::nxmDescriptionAvailable(int, QVariant, QVariant resultData) +void ModInfoRegular::nxmDescriptionAvailable(QString, int, QVariant, QVariant resultData) { QVariantMap result = resultData.toMap(); setNewestVersion(VersionInfo(result["version"].toString())); @@ -201,7 +205,7 @@ void ModInfoRegular::nxmDescriptionAvailable(int, QVariant, QVariant resultData) } -void ModInfoRegular::nxmEndorsementToggled(int, QVariant, QVariant resultData) +void ModInfoRegular::nxmEndorsementToggled(QString, int, QVariant, QVariant resultData) { m_EndorsedState = resultData.toBool() ? ENDORSED_TRUE : ENDORSED_FALSE; m_MetaInfoChanged = true; @@ -210,7 +214,7 @@ void ModInfoRegular::nxmEndorsementToggled(int, QVariant, QVariant resultData) } -void ModInfoRegular::nxmRequestFailed(int, int, QVariant userData, const QString &errorMessage) +void ModInfoRegular::nxmRequestFailed(QString, int, int, QVariant userData, const QString &errorMessage) { QString fullMessage = errorMessage; if (userData.canConvert() && (userData.toInt() == 1)) { @@ -226,7 +230,7 @@ void ModInfoRegular::nxmRequestFailed(int, int, QVariant userData, const QString bool ModInfoRegular::updateNXMInfo() { if (m_NexusID > 0) { - m_NexusBridge.requestDescription(m_NexusID, QVariant()); + m_NexusBridge.requestDescription(m_GameName, m_NexusID, QVariant()); return true; } return false; @@ -387,7 +391,7 @@ bool ModInfoRegular::remove() void ModInfoRegular::endorse(bool doEndorse) { if (doEndorse != (m_EndorsedState == ENDORSED_TRUE)) { - m_NexusBridge.requestToggleEndorsement(getNexusID(), doEndorse, QVariant(1)); + m_NexusBridge.requestToggleEndorsement(m_GameName, getNexusID(), doEndorse, QVariant(1)); } } diff --git a/src/modinforegular.h b/src/modinforegular.h index a94d0363..7a6f4827 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -206,6 +206,14 @@ public: * @return file used to install this mod from */ virtual QString getInstallationFile() const { return m_InstallationFile; } + + /** + * @brief getter for the source game repository + * + * @return the source game repository. should default to the active game. + **/ + QString getGameName() const { return m_GameName; } + /** * @brief getter for the nexus mod id * @@ -308,13 +316,13 @@ private: private slots: - void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData); - void nxmEndorsementToggled(int, QVariant userData, QVariant resultData); - void nxmRequestFailed(int modID, int fileID, QVariant userData, const QString &errorMessage); + void nxmDescriptionAvailable(QString, int modID, QVariant userData, QVariant resultData); + void nxmEndorsementToggled(QString, int, QVariant userData, QVariant resultData); + void nxmRequestFailed(QString, int modID, int fileID, QVariant userData, const QString &errorMessage); protected: - ModInfoRegular(const QDir &path, MOShared::DirectoryEntry **directoryStructure); + ModInfoRegular(PluginContainer *pluginContainer, QString gameName, const QDir &path, MOShared::DirectoryEntry **directoryStructure); private: @@ -325,6 +333,7 @@ private: QString m_NexusDescription; QString m_Repository; QString m_URL; + QString m_GameName; QDateTime m_CreationTime; QDateTime m_LastNexusQuery; diff --git a/src/modinfowithconflictinfo.cpp b/src/modinfowithconflictinfo.cpp index b8ece783..e88ba29d 100644 --- a/src/modinfowithconflictinfo.cpp +++ b/src/modinfowithconflictinfo.cpp @@ -6,8 +6,8 @@ using namespace MOBase; using namespace MOShared; -ModInfoWithConflictInfo::ModInfoWithConflictInfo(DirectoryEntry **directoryStructure) - : m_DirectoryStructure(directoryStructure) {} +ModInfoWithConflictInfo::ModInfoWithConflictInfo(PluginContainer *pluginContainer, DirectoryEntry **directoryStructure) + : ModInfo(pluginContainer), m_DirectoryStructure(directoryStructure) {} void ModInfoWithConflictInfo::clearCaches() { diff --git a/src/modinfowithconflictinfo.h b/src/modinfowithconflictinfo.h index be31f20f..6c1591c5 100644 --- a/src/modinfowithconflictinfo.h +++ b/src/modinfowithconflictinfo.h @@ -10,7 +10,7 @@ class ModInfoWithConflictInfo : public ModInfo public: - ModInfoWithConflictInfo(MOShared::DirectoryEntry **directoryStructure); + ModInfoWithConflictInfo(PluginContainer *pluginContainer, MOShared::DirectoryEntry **directoryStructure); std::vector getFlags() const; diff --git a/src/modlist.cpp b/src/modlist.cpp index 7b2ad1f9..c8fd4b3e 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -53,13 +53,14 @@ along with Mod Organizer. If not, see . using namespace MOBase; -ModList::ModList(QObject *parent) +ModList::ModList(PluginContainer *pluginContainer, QObject *parent) : QAbstractItemModel(parent) , m_Profile(nullptr) , m_NexusInterface(nullptr) , m_Modified(false) , m_FontMetrics(QFont()) , m_DropOnItems(false) + , m_PluginContainer(pluginContainer) { m_ContentIcons[ModInfo::CONTENT_PLUGIN] = std::make_tuple(":/MO/gui/content/plugin", tr("Game Plugins (ESP/ESM/ESL)")); m_ContentIcons[ModInfo::CONTENT_INTERFACE] = std::make_tuple(":/MO/gui/content/interface", tr("Interface")); @@ -219,9 +220,18 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const int modID = modInfo->getNexusID(); if (modID >= 0) { return modID; - } else { + } + else { return QVariant(); } + } else if (column == COL_GAME) { + if (m_PluginContainer != nullptr) { + for (auto game : m_PluginContainer->plugins()) { + if (game->gameShortName() == modInfo->getGameName()) + return game->gameName(); + } + } + return modInfo->getGameName(); } else if (column == COL_CATEGORY) { if (modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { return tr("Non-MO"); @@ -309,6 +319,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const } } else if (role == Qt::UserRole + 3) { return contentsToIcons(modInfo->getContents()); + } else if (role == Qt::UserRole + 4) { + return modInfo->getGameName(); } else if (role == Qt::FontRole) { QFont result; if (column == COL_NAME) { @@ -659,6 +671,11 @@ void ModList::setOverwriteMarkers(const std::set &overwrite, const notifyChange(0, rowCount() - 1); } +void ModList::setPluginContainer(PluginContainer *pluginContianer) +{ + m_PluginContainer = pluginContianer; +} + bool ModList::modInfoAboutToChange(ModInfo::Ptr info) { if (m_ChangeInfo.name.isEmpty()) { @@ -1051,6 +1068,7 @@ QString ModList::getColumnName(int column) case COL_VERSION: return tr("Version"); case COL_PRIORITY: return tr("Priority"); case COL_CATEGORY: return tr("Category"); + case COL_GAME: return tr("Source Game"); case COL_MODID: return tr("Nexus ID"); case COL_INSTALLTIME: return tr("Installation"); default: return tr("unknown"); @@ -1066,6 +1084,7 @@ QString ModList::getColumnToolTip(int column) case COL_PRIORITY: return tr("Installation priority of your mod. The higher, the more \"important\" it is and thus " "overwrites files from mods with lower priority."); case COL_CATEGORY: return tr("Category of the mod."); + case COL_GAME: return tr("The source game which was the origin of this mod."); case COL_MODID: return tr("Id of the mod as used on Nexus."); case COL_FLAGS: return tr("Emblemes to highlight things that might require attention."); case COL_CONTENT: return tr("Depicts the content of the mod:
" diff --git a/src/modlist.h b/src/modlist.h index bd715107..b5f18e98 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -42,6 +42,7 @@ along with Mod Organizer. If not, see . class QSortFilterProxyModel; +class PluginContainer; /** * Model presenting an overview of the installed mod @@ -60,6 +61,7 @@ public: COL_CONTENT, COL_CATEGORY, COL_MODID, + COL_GAME, COL_VERSION, COL_INSTALLTIME, COL_PRIORITY, @@ -76,7 +78,7 @@ public: * @brief constructor * @todo ensure this view works without a profile set, otherwise there are intransparent dependencies on the initialisation order **/ - ModList(QObject *parent = nullptr); + ModList(PluginContainer *pluginContainer, QObject *parent = nullptr); ~ModList(); @@ -106,6 +108,7 @@ public: void changeModPriority(int sourceIndex, int newPriority); void setOverwriteMarkers(const std::set &overwrite, const std::set &overwritten); + void setPluginContainer(PluginContainer *pluginContainer); bool modInfoAboutToChange(ModInfo::Ptr info); void modInfoChanged(ModInfo::Ptr info); @@ -329,6 +332,8 @@ private: QTime m_LastCheck; + PluginContainer *m_PluginContainer; + }; #endif // MODLIST_H diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 97186e8b..434c0334 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -36,48 +36,48 @@ using namespace MOBase; using namespace MOShared; -NexusBridge::NexusBridge(const QString &subModule) - : m_Interface(NexusInterface::instance()) +NexusBridge::NexusBridge(PluginContainer *pluginContainer, const QString &subModule) + : m_Interface(NexusInterface::instance(pluginContainer)) , m_SubModule(subModule) { } -void NexusBridge::requestDescription(int modID, QVariant userData) +void NexusBridge::requestDescription(QString gameName, int modID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestDescription(modID, this, userData, m_SubModule)); + m_RequestIDs.insert(m_Interface->requestDescription(gameName, modID, this, userData, m_SubModule)); } -void NexusBridge::requestFiles(int modID, QVariant userData) +void NexusBridge::requestFiles(QString gameName, int modID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestFiles(modID, this, userData, m_SubModule)); + m_RequestIDs.insert(m_Interface->requestFiles(gameName, modID, this, userData, m_SubModule)); } -void NexusBridge::requestFileInfo(int modID, int fileID, QVariant userData) +void NexusBridge::requestFileInfo(QString gameName, int modID, int fileID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestFileInfo(modID, fileID, this, userData, m_SubModule)); + m_RequestIDs.insert(m_Interface->requestFileInfo(gameName, modID, fileID, this, userData, m_SubModule)); } -void NexusBridge::requestDownloadURL(int modID, int fileID, QVariant userData) +void NexusBridge::requestDownloadURL(QString gameName, int modID, int fileID, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestDownloadURL(modID, fileID, this, userData, m_SubModule)); + m_RequestIDs.insert(m_Interface->requestDownloadURL(gameName, modID, fileID, this, userData, m_SubModule)); } -void NexusBridge::requestToggleEndorsement(int modID, bool endorse, QVariant userData) +void NexusBridge::requestToggleEndorsement(QString gameName, int modID, bool endorse, QVariant userData) { - m_RequestIDs.insert(m_Interface->requestToggleEndorsement(modID, endorse, this, userData, m_SubModule)); + m_RequestIDs.insert(m_Interface->requestToggleEndorsement(gameName, modID, endorse, this, userData, m_SubModule)); } -void NexusBridge::nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID) +void NexusBridge::nxmDescriptionAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID) { std::set::iterator iter = m_RequestIDs.find(requestID); if (iter != m_RequestIDs.end()) { m_RequestIDs.erase(iter); - emit descriptionAvailable(modID, userData, resultData); + emit descriptionAvailable(gameName, modID, userData, resultData); } } -void NexusBridge::nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID) +void NexusBridge::nxmFilesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID) { std::set::iterator iter = m_RequestIDs.find(requestID); if (iter != m_RequestIDs.end()) { @@ -100,43 +100,43 @@ void NexusBridge::nxmFilesAvailable(int modID, QVariant userData, QVariant resul fileInfoList.append(temp); } - emit filesAvailable(modID, userData, fileInfoList); + emit filesAvailable(gameName, modID, userData, fileInfoList); } } -void NexusBridge::nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID) +void NexusBridge::nxmFileInfoAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID) { std::set::iterator iter = m_RequestIDs.find(requestID); if (iter != m_RequestIDs.end()) { m_RequestIDs.erase(iter); - emit fileInfoAvailable(modID, fileID, userData, resultData); + emit fileInfoAvailable(gameName, modID, fileID, userData, resultData); } } -void NexusBridge::nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID) +void NexusBridge::nxmDownloadURLsAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID) { std::set::iterator iter = m_RequestIDs.find(requestID); if (iter != m_RequestIDs.end()) { m_RequestIDs.erase(iter); - emit downloadURLsAvailable(modID, fileID, userData, resultData); + emit downloadURLsAvailable(gameName, modID, fileID, userData, resultData); } } -void NexusBridge::nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID) +void NexusBridge::nxmEndorsementToggled(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID) { std::set::iterator iter = m_RequestIDs.find(requestID); if (iter != m_RequestIDs.end()) { m_RequestIDs.erase(iter); - emit endorsementToggled(modID, userData, resultData); + emit endorsementToggled(gameName, modID, userData, resultData); } } -void NexusBridge::nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage) +void NexusBridge::nxmRequestFailed(QString gameName, int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage) { std::set::iterator iter = m_RequestIDs.find(requestID); if (iter != m_RequestIDs.end()) { m_RequestIDs.erase(iter); - emit requestFailed(modID, fileID, userData, errorMessage); + emit requestFailed(gameName, modID, fileID, userData, errorMessage); } } @@ -144,8 +144,8 @@ void NexusBridge::nxmRequestFailed(int modID, int fileID, QVariant userData, int QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0); -NexusInterface::NexusInterface() - : m_NMMVersion() +NexusInterface::NexusInterface(PluginContainer *pluginContainer) + : m_NMMVersion(), m_PluginContainer(pluginContainer) { VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath())); m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16, @@ -167,9 +167,9 @@ NexusInterface::~NexusInterface() cleanup(); } -NexusInterface *NexusInterface::instance() +NexusInterface *NexusInterface::instance(PluginContainer *pluginContainer) { - static NexusInterface s_Instance; + static NexusInterface s_Instance(pluginContainer); return &s_Instance; } @@ -246,12 +246,16 @@ void NexusInterface::interpretNexusFileName(const QString &fileName, QString &mo bool NexusInterface::isURLGameRelated(const QUrl &url) const { QString const name(url.toString()); - return name.startsWith(getGameURL() + "/") || + return name.startsWith(getGameURL("") + "/") || name.startsWith(getOldModsURL() + "/"); } -QString NexusInterface::getGameURL() const +QString NexusInterface::getGameURL(QString gameName) const { + IPluginGame *game = getGame(gameName); + if (game != nullptr) { + return "http://www.nexusmods.com/" + game->gameNexusName().toLower(); + } return "http://www.nexusmods.com/" + m_Game->gameNexusName().toLower(); } @@ -261,9 +265,9 @@ QString NexusInterface::getOldModsURL() const } -QString NexusInterface::getModURL(int modID) const +QString NexusInterface::getModURL(int modID, QString gameName = "") const { - return QString("%1/mods/%2").arg(getGameURL()).arg(modID); + return QString("%1/mods/%2").arg(getGameURL(gameName)).arg(modID); } bool NexusInterface::isModURL(int modID, const QString &url) const @@ -276,17 +280,22 @@ bool NexusInterface::isModURL(int modID, const QString &url) const return QUrl(alt) == QUrl(url); } -int NexusInterface::requestDescription(int modID, QObject *receiver, QVariant userData, +void NexusInterface::setPluginContainer(PluginContainer *pluginContainer) +{ + m_PluginContainer = pluginContainer; +} + +int NexusInterface::requestDescription(QString gameName, int modID, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game) { NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_DESCRIPTION, userData, subModule, game); m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmDescriptionAvailable(int,QVariant,QVariant,int)), - receiver, SLOT(nxmDescriptionAvailable(int,QVariant,QVariant,int)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmDescriptionAvailable(QString,int,QVariant,QVariant,int)), + receiver, SLOT(nxmDescriptionAvailable(QString,int,QVariant,QVariant,int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(QString,int,int,QVariant,int,QString)), + receiver, SLOT(nxmRequestFailed(QString,int,int,QVariant,int,QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; @@ -294,19 +303,25 @@ int NexusInterface::requestDescription(int modID, QObject *receiver, QVariant us int NexusInterface::requestUpdates(const std::vector &modIDs, QObject *receiver, QVariant userData, - const QString &subModule, MOBase::IPluginGame const *game) + QString gameName, const QString &subModule) { - NXMRequestInfo requestInfo(modIDs, NXMRequestInfo::TYPE_GETUPDATES, userData, subModule, game); - m_RequestQueue.enqueue(requestInfo); + IPluginGame *game = getGame(gameName); - connect(this, SIGNAL(nxmUpdatesAvailable(std::vector,QVariant,QVariant,int)), - receiver, SLOT(nxmUpdatesAvailable(std::vector,QVariant,QVariant,int)), Qt::UniqueConnection); + if (game != nullptr) { + NXMRequestInfo requestInfo(modIDs, NXMRequestInfo::TYPE_GETUPDATES, userData, subModule, game); + m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmUpdatesAvailable(std::vector, QVariant, QVariant, int)), + receiver, SLOT(nxmUpdatesAvailable(std::vector, QVariant, QVariant, int)), Qt::UniqueConnection); - nextRequest(); - return requestInfo.m_ID; + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QString)), Qt::UniqueConnection); + + nextRequest(); + return requestInfo.m_ID; + } + + return -1; } @@ -325,72 +340,75 @@ void NexusInterface::fakeFiles() fileMap["size"] = "512"; result.append(fileMap); - emit nxmFilesAvailable(1234, "fake", result, id++); + emit nxmFilesAvailable("fakeGame", 1234, "fake", result, id++); } -int NexusInterface::requestFiles(int modID, QObject *receiver, QVariant userData, +int NexusInterface::requestFiles(QString gameName, int modID, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game) { NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_FILES, userData, subModule, game); m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmFilesAvailable(int,QVariant,QVariant,int)), - receiver, SLOT(nxmFilesAvailable(int,QVariant,QVariant,int)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmFilesAvailable(QString,int,QVariant,QVariant,int)), + receiver, SLOT(nxmFilesAvailable(QString,int,QVariant,QVariant,int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(QString,int,int,QVariant,int,QString)), + receiver, SLOT(nxmRequestFailed(QString,int,int,QVariant,int,QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; } -int NexusInterface::requestFileInfo(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule, - MOBase::IPluginGame const *game) +int NexusInterface::requestFileInfo(QString gameName, int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule) { - NXMRequestInfo requestInfo(modID, fileID, NXMRequestInfo::TYPE_FILEINFO, userData, subModule, game); - m_RequestQueue.enqueue(requestInfo); + IPluginGame *gamePlugin = getGame(gameName); + if (gamePlugin != nullptr) { + NXMRequestInfo requestInfo(modID, fileID, NXMRequestInfo::TYPE_FILEINFO, userData, subModule, gamePlugin); + m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmFileInfoAvailable(int,int,QVariant,QVariant,int)), - receiver, SLOT(nxmFileInfoAvailable(int,int,QVariant,QVariant,int)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmFileInfoAvailable(QString, int, int, QVariant, QVariant, int)), + receiver, SLOT(nxmFileInfoAvailable(QString, int, int, QVariant, QVariant, int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QString)), Qt::UniqueConnection); - nextRequest(); - return requestInfo.m_ID; + nextRequest(); + return requestInfo.m_ID; + } + return -1; } -int NexusInterface::requestDownloadURL(int modID, int fileID, QObject *receiver, QVariant userData, +int NexusInterface::requestDownloadURL(QString gameName, int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game) { NXMRequestInfo requestInfo(modID, fileID, NXMRequestInfo::TYPE_DOWNLOADURL, userData, subModule, game); m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), - receiver, SLOT(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmDownloadURLsAvailable(QString,int,int,QVariant,QVariant,int)), + receiver, SLOT(nxmDownloadURLsAvailable(QString,int,int,QVariant,QVariant,int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(QString,int,int,QVariant,int,QString)), + receiver, SLOT(nxmRequestFailed(QString,int,int,QVariant,int,QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; } -int NexusInterface::requestToggleEndorsement(int modID, bool endorse, QObject *receiver, QVariant userData, +int NexusInterface::requestToggleEndorsement(QString gameName, int modID, bool endorse, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game) { NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_TOGGLEENDORSEMENT, userData, subModule, game); requestInfo.m_Endorse = endorse; m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmEndorsementToggled(int,QVariant,QVariant,int)), - receiver, SLOT(nxmEndorsementToggled(int,QVariant,QVariant,int)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmEndorsementToggled(QString,int,QVariant,QVariant,int)), + receiver, SLOT(nxmEndorsementToggled(QString,int,QVariant,QVariant,int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(int,int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(int,int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(QString,int,int,QVariant,int,QString)), + receiver, SLOT(nxmRequestFailed(QString,int,int,QVariant,int,QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; @@ -402,6 +420,19 @@ bool NexusInterface::requiresLogin(const NXMRequestInfo &info) || (info.m_Type == NXMRequestInfo::TYPE_DOWNLOADURL); } +IPluginGame* NexusInterface::getGame(QString gameName) const +{ + auto gamePlugins = m_PluginContainer->plugins(); + IPluginGame *gamePlugin = nullptr; + for (auto plugin : gamePlugins) { + if (plugin->gameShortName().compare(gameName, Qt::CaseInsensitive) == 0) { + gamePlugin = plugin; + break; + } + } + return gamePlugin; +} + void NexusInterface::cleanup() { // delete m_AccessManager; @@ -492,7 +523,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) if (reply->error() != QNetworkReply::NoError) { qWarning("request failed: %s", reply->errorString().toUtf8().constData()); - emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, reply->errorString()); + emit nxmRequestFailed(iter->m_GameName, iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, reply->errorString()); } else { int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (statusCode == 301) { @@ -510,33 +541,33 @@ void NexusInterface::requestFinished(std::list::iterator iter) nexusError = tr("empty response"); } qDebug("nexus error: %s", qPrintable(nexusError)); - emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, nexusError); + emit nxmRequestFailed(iter->m_GameName, iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, nexusError); } else { bool ok; QVariant result = QtJson::parse(data, ok); if (result.isValid() && ok) { switch (iter->m_Type) { case NXMRequestInfo::TYPE_DESCRIPTION: { - emit nxmDescriptionAvailable(iter->m_ModID, iter->m_UserData, result, iter->m_ID); + emit nxmDescriptionAvailable(iter->m_GameName, iter->m_ModID, iter->m_UserData, result, iter->m_ID); } break; case NXMRequestInfo::TYPE_FILES: { - emit nxmFilesAvailable(iter->m_ModID, iter->m_UserData, result, iter->m_ID); + emit nxmFilesAvailable(iter->m_GameName, iter->m_ModID, iter->m_UserData, result, iter->m_ID); } break; case NXMRequestInfo::TYPE_FILEINFO: { - emit nxmFileInfoAvailable(iter->m_ModID, iter->m_FileID, iter->m_UserData, result, iter->m_ID); + emit nxmFileInfoAvailable(iter->m_GameName, iter->m_ModID, iter->m_FileID, iter->m_UserData, result, iter->m_ID); } break; case NXMRequestInfo::TYPE_DOWNLOADURL: { - emit nxmDownloadURLsAvailable(iter->m_ModID, iter->m_FileID, iter->m_UserData, result, iter->m_ID); + emit nxmDownloadURLsAvailable(iter->m_GameName, iter->m_ModID, iter->m_FileID, iter->m_UserData, result, iter->m_ID); } break; case NXMRequestInfo::TYPE_GETUPDATES: { emit nxmUpdatesAvailable(iter->m_ModIDList, iter->m_UserData, result, iter->m_ID); } break; case NXMRequestInfo::TYPE_TOGGLEENDORSEMENT: { - emit nxmEndorsementToggled(iter->m_ModID, iter->m_UserData, result, iter->m_ID); + emit nxmEndorsementToggled(iter->m_GameName, iter->m_ModID, iter->m_UserData, result, iter->m_ID); } break; } } else { - emit nxmRequestFailed(iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, tr("invalid response")); + emit nxmRequestFailed(iter->m_GameName, iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, tr("invalid response")); } } } @@ -620,6 +651,7 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_URL(get_management_url(game)) , m_SubModule(subModule) , m_NexusGameID(game->nexusGameID()) + , m_GameName(game->gameShortName()) , m_Endorse(false) {} @@ -641,6 +673,7 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(std::vector modIDList , m_URL(get_management_url(game)) , m_SubModule(subModule) , m_NexusGameID(game->nexusGameID()) + , m_GameName(game->gameShortName()) , m_Endorse(false) {} @@ -662,5 +695,6 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_URL(get_management_url(game)) , m_SubModule(subModule) , m_NexusGameID(game->nexusGameID()) + , m_GameName(game->gameShortName()) , m_Endorse(false) {} diff --git a/src/nexusinterface.h b/src/nexusinterface.h index 9e7f3642..9a01c5c5 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include @@ -53,7 +54,7 @@ class NexusBridge : public MOBase::IModRepositoryBridge public: - NexusBridge(const QString &subModule = ""); + NexusBridge(PluginContainer *pluginContainer, const QString &subModule = ""); /** * @brief request description for a mod @@ -62,7 +63,7 @@ public: * @param userData user data to be returned with the result * @param url the url to request from **/ - virtual void requestDescription(int modID, QVariant userData); + virtual void requestDescription(QString gameName, int modID, QVariant userData); /** * @brief request a list of the files belonging to a mod @@ -70,7 +71,7 @@ public: * @param modID id of the mod caller is interested in * @param userData user data to be returned with the result **/ - virtual void requestFiles(int modID, QVariant userData); + virtual void requestFiles(QString gameName, int modID, QVariant userData); /** * @brief request info about a single file of a mod @@ -79,7 +80,7 @@ public: * @param fileID id of the file the caller is interested in * @param userData user data to be returned with the result **/ - virtual void requestFileInfo(int modID, int fileID, QVariant userData); + virtual void requestFileInfo(QString gameName, int modID, int fileID, QVariant userData); /** * @brief request the download url of a file @@ -88,23 +89,23 @@ public: * @param fileID id of the file the caller is interested in * @param userData user data to be returned with the result **/ - virtual void requestDownloadURL(int modID, int fileID, QVariant userData); + virtual void requestDownloadURL(QString gameName, int modID, int fileID, QVariant userData); /** * @brief requestToggleEndorsement * @param modID id of the mod caller is interested in * @param userData user data to be returned with the result */ - virtual void requestToggleEndorsement(int modID, bool endorse, QVariant userData); + virtual void requestToggleEndorsement(QString gameName, int modID, bool endorse, QVariant userData); public slots: - void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage); + void nxmDescriptionAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); + void nxmFilesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); + void nxmFileInfoAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmDownloadURLsAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmEndorsementToggled(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); + void nxmRequestFailed(QString gameName, int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage); private: @@ -130,7 +131,7 @@ public: ~NexusInterface(); - static NexusInterface *instance(); + static NexusInterface *instance(PluginContainer *pluginContainer); /** * @return the access manager object used to connect to nexus @@ -155,9 +156,9 @@ public: * @param userData user data to be returned with the result * @return int an id to identify the request **/ - int requestDescription(int modID, QObject *receiver, QVariant userData, const QString &subModule) + int requestDescription(QString gameName, int modID, QObject *receiver, QVariant userData, const QString &subModule) { - return requestDescription(modID, receiver, userData, subModule, m_Game); + return requestDescription(gameName, modID, receiver, userData, subModule, getGame(gameName)); } /** @@ -169,21 +170,9 @@ public: * @param game Game with which the mod is associated * @return int an id to identify the request **/ - int requestDescription(int modID, QObject *receiver, QVariant userData, const QString &subModule, + int requestDescription(QString gameName, int modID, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); - /** - * @brief request nexus descriptions for multiple mods at once - * @param modIDs a list of ids of mods the caller is interested in (assumed to be for the current game) - * @param receiver the object to receive the result asynchronously via a signal (nxmDescriptionAvailable) - * @param userData user data to be returned with the result - * @return int an id to identify the request - */ - int requestUpdates(const std::vector &modIDs, QObject *receiver, QVariant userData, const QString &subModule) - { - return requestUpdates(modIDs, receiver, userData, subModule, m_Game); - } - /** * @brief request nexus descriptions for multiple mods at once * @param modIDs a list of ids of mods the caller is interested in @@ -192,8 +181,7 @@ public: * @param game the game with which the mods are associated * @return int an id to identify the request */ - int requestUpdates(const std::vector &modIDs, QObject *receiver, QVariant userData, const QString &subModule, - MOBase::IPluginGame const *game); + int requestUpdates(const std::vector &modIDs, QObject *receiver, QVariant userData, QString gameName, const QString &subModule); /** * @brief request a list of the files belonging to a mod @@ -203,9 +191,9 @@ public: * @param userData user data to be returned with the result * @return int an id to identify the request **/ - int requestFiles(int modID, QObject *receiver, QVariant userData, const QString &subModule) + int requestFiles(QString gameName, int modID, QObject *receiver, QVariant userData, const QString &subModule) { - return requestFiles(modID, receiver, userData, subModule, m_Game); + return requestFiles(gameName, modID, receiver, userData, subModule, getGame(gameName)); } @@ -218,35 +206,20 @@ public: * @param game the game with which the mods are associated * @return int an id to identify the request **/ - int requestFiles(int modID, QObject *receiver, QVariant userData, const QString &subModule, + int requestFiles(QString gameName, int modID, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); /** * @brief request info about a single file of a mod * + * @param game name of the game short name to request the download from * @param modID id of the mod caller is interested in (assumed to be for the current game) * @param fileID id of the file the caller is interested in * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable) * @param userData user data to be returned with the result * @return int an id to identify the request **/ - int requestFileInfo(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule) - { - return requestFileInfo(modID, fileID, receiver, userData, subModule, m_Game); - } - - /** - * @brief request info about a single file of a mod - * - * @param modID id of the mod caller is interested in (assumed to be for the current game) - * @param fileID id of the file the caller is interested in - * @param receiver the object to receive the result asynchronously via a signal (nxmFilesAvailable) - * @param userData user data to be returned with the result - * @param game the game with which the mods are associated - * @return int an id to identify the request - **/ - int requestFileInfo(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule, - MOBase::IPluginGame const *game); + int requestFileInfo(QString gameName, int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule); /** * @brief request the download url of a file @@ -257,9 +230,9 @@ public: * @param userData user data to be returned with the result * @return int an id to identify the request **/ - int requestDownloadURL(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule) + int requestDownloadURL(QString gameName, int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule) { - return requestDownloadURL(modID, fileID, receiver, userData, subModule, m_Game); + return requestDownloadURL(gameName, modID, fileID, receiver, userData, subModule, getGame(gameName)); } /** @@ -272,7 +245,7 @@ public: * @param game the game with which the mods are associated * @return int an id to identify the request **/ - int requestDownloadURL(int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); + int requestDownloadURL(QString gameName, int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); /** * @brief toggle endorsement state of the mod @@ -282,9 +255,9 @@ public: * @param userData user data to be returned with the result * @return int an id to identify the request */ - int requestToggleEndorsement(int modID, bool endorse, QObject *receiver, QVariant userData, const QString &subModule) + int requestToggleEndorsement(QString gameName, int modID, bool endorse, QObject *receiver, QVariant userData, const QString &subModule) { - return requestToggleEndorsement(modID, endorse, receiver, userData, subModule, m_Game); + return requestToggleEndorsement(gameName, modID, endorse, receiver, userData, subModule, getGame(gameName)); } /** @@ -296,7 +269,7 @@ public: * @param game the game with which the mods are associated * @return int an id to identify the request */ - int requestToggleEndorsement(int modID, bool endorse, QObject *receiver, QVariant userData, const QString &subModule, + int requestToggleEndorsement(QString gameName, int modID, bool endorse, QObject *receiver, QVariant userData, const QString &subModule, MOBase::IPluginGame const *game); /** @@ -342,13 +315,13 @@ public: * * Arguably, this should optionally take a gameplugin pointer */ - QString getGameURL() const; + QString getGameURL(QString gameName) const; /** * @brief Get the URL for the mod web page * @param modID */ - QString getModURL(int modID) const; + QString getModURL(int modID, QString gameName) const; /** * @brief Checks if the specified URL might correspond to a nexus mod @@ -358,19 +331,21 @@ public: */ bool isModURL(int modID, QString const &url) const; + void setPluginContainer(PluginContainer *pluginContainer); + signals: void requestNXMDownload(const QString &url); void needLogin(); - void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID); + void nxmDescriptionAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); void nxmUpdatesAvailable(const std::vector &modIDs, QVariant userData, QVariant resultData, int requestID); - void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - void nxmEndorsementToggled(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorString); + void nxmFilesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); + void nxmFileInfoAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmDownloadURLsAvailable(QString gameName, int modID, int fileID, QVariant userData, QVariant resultData, int requestID); + void nxmEndorsementToggled(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); + void nxmRequestFailed(QString gameName, int modID, int fileID, QVariant userData, int requestID, const QString &errorString); public slots: void managedGameChanged(MOBase::IPluginGame const *game); @@ -404,6 +379,7 @@ private: QTimer *m_Timeout; QString m_URL; QString m_SubModule; + QString m_GameName; int m_NexusGameID; bool m_Reroute; int m_ID; @@ -421,10 +397,11 @@ private: private: - NexusInterface(); + NexusInterface(PluginContainer *pluginContainer); void nextRequest(); void requestFinished(std::list::iterator iter); bool requiresLogin(const NXMRequestInfo &info); + MOBase::IPluginGame *getGame(QString gameName) const; QString getOldModsURL() const; private: @@ -440,6 +417,7 @@ private: QString m_NMMVersion; MOBase::IPluginGame const *m_Game; + PluginContainer *m_PluginContainer; }; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 999c7732..3248a8e0 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -192,15 +192,15 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) , m_GameName() , m_CurrentProfile(nullptr) , m_Settings(initSettings) - , m_Updater(NexusInterface::instance()) + , m_Updater(NexusInterface::instance(m_PluginContainer)) , m_AboutToRun() , m_FinishedRun() , m_ModInstalled() - , m_ModList(this) + , m_ModList(m_PluginContainer, this) , m_PluginList(this) , m_DirectoryRefresher() , m_DirectoryStructure(new DirectoryEntry(L"data", nullptr, 0)) - , m_DownloadManager(NexusInterface::instance(), this) + , m_DownloadManager(NexusInterface::instance(m_PluginContainer), this) , m_InstallationManager() , m_RefresherThread() , m_AskForNexusPW(false) @@ -211,8 +211,8 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) m_DownloadManager.setOutputDirectory(m_Settings.getDownloadDirectory()); m_DownloadManager.setPreferredServers(m_Settings.getPreferredServers()); - NexusInterface::instance()->setCacheDirectory(m_Settings.getCacheDirectory()); - NexusInterface::instance()->setNMMVersion(m_Settings.getNMMVersion()); + NexusInterface::instance(m_PluginContainer)->setCacheDirectory(m_Settings.getCacheDirectory()); + NexusInterface::instance(m_PluginContainer)->setNMMVersion(m_Settings.getNMMVersion()); MOBase::QuestionBoxMemory::init(initSettings.fileName()); @@ -227,9 +227,9 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) connect(&m_ModList, SIGNAL(removeOrigin(QString)), this, SLOT(removeOrigin(QString))); - connect(NexusInterface::instance()->getAccessManager(), + connect(NexusInterface::instance(m_PluginContainer)->getAccessManager(), SIGNAL(loginSuccessful(bool)), this, SLOT(loginSuccessful(bool))); - connect(NexusInterface::instance()->getAccessManager(), + connect(NexusInterface::instance(m_PluginContainer)->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); // This seems awfully imperative @@ -241,7 +241,7 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const *)), &m_PluginList, SLOT(managedGameChanged(MOBase::IPluginGame const *))); connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const *)), - NexusInterface::instance(), + NexusInterface::instance(m_PluginContainer), SLOT(managedGameChanged(MOBase::IPluginGame const *))); connect(&m_PluginList, &PluginList::writePluginsList, &m_PluginListsWriter, @@ -454,7 +454,7 @@ void OrganizerCore::updateExecutablesList(QSettings &settings) // TODO this has nothing to do with executables list move to an appropriate // function! ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, - m_Settings.displayForeign(), managedGame()); + m_PluginContainer, m_Settings.displayForeign(), managedGame()); } void OrganizerCore::setUserInterface(IUserInterface *userInterface, @@ -504,6 +504,9 @@ void OrganizerCore::connectPlugins(PluginContainer *container) m_DownloadManager.setSupportedExtensions( m_InstallationManager.getSupportedExtensions()); m_PluginContainer = container; + m_Updater.setPluginContainer(m_PluginContainer); + m_DownloadManager.setPluginContainer(m_PluginContainer); + m_ModList.setPluginContainer(m_PluginContainer); if (!m_GameName.isEmpty()) { m_GamePlugin = m_PluginContainer->managedGame(m_GameName); @@ -518,6 +521,9 @@ void OrganizerCore::disconnectPlugins() m_ModInstalled.disconnect_all_slots(); m_ModList.disconnectSlots(); m_PluginList.disconnectSlots(); + m_Updater.setPluginContainer(nullptr); + m_DownloadManager.setPluginContainer(nullptr); + m_ModList.setPluginContainer(nullptr); m_Settings.clearPlugins(); m_GamePlugin = nullptr; @@ -540,7 +546,7 @@ Settings &OrganizerCore::settings() bool OrganizerCore::nexusLogin(bool retry) { NXMAccessManager *accessManager - = NexusInterface::instance()->getAccessManager(); + = NexusInterface::instance(m_PluginContainer)->getAccessManager(); if ((accessManager->loginAttempted() || accessManager->loggedIn()) && !retry) { @@ -612,12 +618,12 @@ void OrganizerCore::externalMessage(const QString &message) } } -void OrganizerCore::downloadRequested(QNetworkReply *reply, int modID, +void OrganizerCore::downloadRequested(QNetworkReply *reply, QString gameName, int modID, const QString &fileName) { try { - if (m_DownloadManager.addDownload(reply, QStringList(), fileName, modID, 0, - new ModRepositoryFileInfo(modID))) { + if (m_DownloadManager.addDownload(reply, QStringList(), fileName, gameName, modID, 0, + new ModRepositoryFileInfo(gameName, modID))) { MessageDialog::showMessage(tr("Download started"), qApp->activeWindow()); } } catch (const std::exception &e) { @@ -739,7 +745,7 @@ void OrganizerCore::setCurrentProfile(const QString &profileName) MOBase::IModRepositoryBridge *OrganizerCore::createNexusBridge() const { - return new NexusBridge(); + return new NexusBridge(m_PluginContainer); } QString OrganizerCore::profileName() const @@ -813,7 +819,7 @@ MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) settingsFile.endArray(); } - return ModInfo::createFrom(QDir(targetDirectory), &m_DirectoryStructure) + return ModInfo::createFrom(m_PluginContainer, m_GamePlugin->gameShortName(), QDir(targetDirectory), &m_DirectoryStructure) .data(); } @@ -1552,7 +1558,7 @@ void OrganizerCore::refreshModList(bool saveChanges) m_CurrentProfile->writeModlistNow(true); } ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, - m_Settings.displayForeign(), managedGame()); + m_PluginContainer, m_Settings.displayForeign(), managedGame()); m_CurrentProfile->refreshModStatus(); @@ -1719,8 +1725,13 @@ void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply) // no mod found that could handle the download. Is it a nexus mod? if (url.host() == "www.nexusmods.com") { + QString gameName = ""; int modID = 0; int fileID = 0; + QRegExp nameExp("www\.nexusmods\.com/(\\a+)/"); + if (nameExp.indexIn(url.toString()) != -1) { + gameName = nameExp.cap(1); + } QRegExp modExp("mods/(\\d+)"); if (modExp.indexIn(url.toString()) != -1) { modID = modExp.cap(1).toInt(); @@ -1730,7 +1741,7 @@ void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply) fileID = fileExp.cap(1).toInt(); } m_DownloadManager.addDownload(reply, - new ModRepositoryFileInfo(modID, fileID)); + new ModRepositoryFileInfo(gameName, modID, fileID)); } else { if (QMessageBox::question(qApp->activeWindow(), tr("Download?"), tr("A download has been started but no installed " @@ -1827,7 +1838,7 @@ void OrganizerCore::profileRefresh() // have to refresh mods twice (again in refreshModList), otherwise the refresh // isn't complete. Not sure why ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure, - m_Settings.displayForeign(), managedGame()); + m_PluginContainer, m_Settings.displayForeign(), managedGame()); m_CurrentProfile->refreshModStatus(); refreshModList(); @@ -1885,7 +1896,7 @@ void OrganizerCore::loginSuccessful(bool necessary) } m_PostLoginTasks.clear(); - NexusInterface::instance()->loginCompleted(); + NexusInterface::instance(m_PluginContainer)->loginCompleted(); } void OrganizerCore::loginSuccessfulUpdate(bool necessary) @@ -1920,7 +1931,7 @@ void OrganizerCore::loginFailed(const QString &message) qApp->activeWindow()); m_PostLoginTasks.clear(); } - NexusInterface::instance()->loginCompleted(); + NexusInterface::instance(m_PluginContainer)->loginCompleted(); } void OrganizerCore::loginFailedUpdate(const QString &message) diff --git a/src/organizercore.h b/src/organizercore.h index 54d9dd6d..76c286be 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -285,7 +285,7 @@ private: private slots: void directory_refreshed(); - void downloadRequested(QNetworkReply *reply, int modID, const QString &fileName); + void downloadRequested(QNetworkReply *reply, QString gameName, int modID, const QString &fileName); void removeOrigin(const QString &name); void downloadSpeed(const QString &serverName, int bytesPerSecond); void loginSuccessful(bool necessary); diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index cf2e29e3..77d61a52 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -2,6 +2,7 @@ #include "appconfig.h" #include "organizercore.h" +#include "plugincontainer.h" #include @@ -9,15 +10,16 @@ using namespace MOBase; using namespace MOShared; -OrganizerProxy::OrganizerProxy(OrganizerCore *organizer, const QString &pluginName) +OrganizerProxy::OrganizerProxy(OrganizerCore *organizer, PluginContainer *pluginContainer, const QString &pluginName) : m_Proxied(organizer) + , m_PluginContainer(pluginContainer) , m_PluginName(pluginName) { } IModRepositoryBridge *OrganizerProxy::createNexusBridge() const { - return new NexusBridge(m_PluginName); + return new NexusBridge(m_PluginContainer, m_PluginName); } QString OrganizerProxy::profileName() const diff --git a/src/organizerproxy.h b/src/organizerproxy.h index 7004db14..cebb98ac 100644 --- a/src/organizerproxy.h +++ b/src/organizerproxy.h @@ -5,13 +5,14 @@ #include class OrganizerCore; +class PluginContainer; class OrganizerProxy : public MOBase::IOrganizer { public: - OrganizerProxy(OrganizerCore *organizer, const QString &pluginName); + OrganizerProxy(OrganizerCore *organizer, PluginContainer *pluginContainer, const QString &pluginName); virtual MOBase::IModRepositoryBridge *createNexusBridge() const; virtual QString profileName() const; @@ -55,6 +56,7 @@ public: private: OrganizerCore *m_Proxied; + PluginContainer *m_PluginContainer; const QString &m_PluginName; diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index 60d1130c..f6376496 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -1,4 +1,5 @@ #include "plugincontainer.h" +#include "organizercore.h" #include "organizerproxy.h" #include "report.h" #include @@ -67,7 +68,7 @@ bool PluginContainer::verifyPlugin(IPlugin *plugin) { if (plugin == nullptr) { return false; - } else if (!plugin->init(new OrganizerProxy(m_Organizer, plugin->name()))) { + } else if (!plugin->init(new OrganizerProxy(m_Organizer, this, plugin->name()))) { qWarning("plugin failed to initialize"); return false; } diff --git a/src/plugincontainer.h b/src/plugincontainer.h index 70392d0b..172dbd09 100644 --- a/src/plugincontainer.h +++ b/src/plugincontainer.h @@ -1,9 +1,11 @@ #ifndef PLUGINCONTAINER_H #define PLUGINCONTAINER_H - -#include "organizercore.h" #include "previewgenerator.h" + +class OrganizerCore; +class IUserInterface; + #include #include #include diff --git a/src/profile.cpp b/src/profile.cpp index bbb5b814..7f10bd02 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -753,6 +753,7 @@ bool Profile::enableLocalSettings(bool enable) QString backupFile = getIniFileName() + "_"; if (enable) { if (m_Directory.exists(backupFile)) { + shellRename(backupFile, getIniFileName()); } else { IPluginGame *game = qApp->property("managed_game").value(); diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 8f962ba4..debf9106 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -30,6 +30,7 @@ along with Mod Organizer. If not, see . #include "nxmaccessmanager.h" #include "settings.h" #include "bbcode.h" +#include "plugincontainer.h" #include #include #include @@ -120,6 +121,11 @@ void SelfUpdater::setUserInterface(QWidget *widget) m_Parent = widget; } +void SelfUpdater::setPluginContainer(PluginContainer *pluginContainer) +{ + m_Interface->setPluginContainer(pluginContainer); +} + void SelfUpdater::testForUpdate() { // TODO: if prereleases are disabled we could just request the latest release diff --git a/src/selfupdater.h b/src/selfupdater.h index 4743f6a4..bce49495 100644 --- a/src/selfupdater.h +++ b/src/selfupdater.h @@ -26,6 +26,7 @@ along with Mod Organizer. If not, see . class Archive; class NexusInterface; +class PluginContainer; namespace MOBase { class IPluginGame; } #include @@ -77,6 +78,8 @@ public: void setUserInterface(QWidget *widget); + void setPluginContainer(PluginContainer *pluginContainer); + /** * @brief start the update process * @note this should not be called if there is no update available diff --git a/src/settings.cpp b/src/settings.cpp index d1130e05..9510999d 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -584,9 +584,9 @@ void Settings::resetDialogs() } -void Settings::query(QWidget *parent) +void Settings::query(PluginContainer *pluginContainer, QWidget *parent) { - SettingsDialog dialog(parent); + SettingsDialog dialog(pluginContainer, parent); connect(&dialog, SIGNAL(resetDialogs()), this, SLOT(resetDialogs())); diff --git a/src/settings.h b/src/settings.h index ae38223f..76ab55da 100644 --- a/src/settings.h +++ b/src/settings.h @@ -51,6 +51,7 @@ namespace MOBase { } class SettingsDialog; +class PluginContainer; /** * manages the settings for Mod Organizer. The settings are not cached @@ -88,7 +89,7 @@ public: * displays a SettingsDialog that allows the user to change settings. If the * user accepts the changes, the settings are immediately written **/ - void query(QWidget *parent); + void query(PluginContainer *pluginContainer, QWidget *parent); /** * set up the settings for the specified plugins diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index fb9433a6..a4071c26 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -27,6 +27,7 @@ along with Mod Organizer. If not, see . #include "settings.h" #include "instancemanager.h" #include "nexusinterface.h" +#include "plugincontainer.h" #include #include @@ -40,9 +41,10 @@ along with Mod Organizer. If not, see . using namespace MOBase; -SettingsDialog::SettingsDialog(QWidget *parent) +SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, QWidget *parent) : TutorableDialog("SettingsDialog", parent) , ui(new Ui::SettingsDialog) + , m_PluginContainer(pluginContainer) { ui->setupUi(this); @@ -252,6 +254,6 @@ void SettingsDialog::on_associateButton_clicked() void SettingsDialog::on_clearCacheButton_clicked() { QDir(Settings::instance().getCacheDirectory()).removeRecursively(); - NexusInterface::instance()->clearCache(); + NexusInterface::instance(m_PluginContainer)->clearCache(); } diff --git a/src/settingsdialog.h b/src/settingsdialog.h index e9d995d3..32a8d0c5 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -25,6 +25,8 @@ along with Mod Organizer. If not, see . #include #include +class PluginContainer; + namespace Ui { class SettingsDialog; } @@ -39,7 +41,7 @@ class SettingsDialog : public MOBase::TutorableDialog Q_OBJECT public: - explicit SettingsDialog(QWidget *parent = 0); + explicit SettingsDialog(PluginContainer *pluginContainer, QWidget *parent = 0); ~SettingsDialog(); void addPlugins(const std::vector &plugins); @@ -87,7 +89,7 @@ private slots: private: Ui::SettingsDialog *ui; - + PluginContainer *m_PluginContainer; }; -- cgit v1.3.1 From b1c477f8ec6266b5987b6c73a1d1f79df83b18ed Mon Sep 17 00:00:00 2001 From: Silarn Date: Mon, 16 Apr 2018 15:31:45 -0500 Subject: Fix for virtualized executable path --- src/organizercore.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 1f6d665f..15596f75 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1238,17 +1238,19 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, if (virtualizedCwd || virtualizedBin) { if (virtualizedCwd) { int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1); - cwdPath = m_GamePlugin->dataDirectory().absolutePath(); - if (cwdOffset >= 0) - cwdPath += cwdPath.mid(cwdOffset, -1); - + QString adjustedCwd = cwdPath.mid(cwdOffset, -1); + cwdPath = m_GamePlugin->dataDirectory().absolutePath(); + if (cwdOffset >= 0) + cwdPath += adjustedCwd; + } if (virtualizedBin) { int binOffset = binPath.indexOf('/', modsPath.length() + 1); + QString adjustedBin = binPath.mid(binOffset, -1); binPath = m_GamePlugin->dataDirectory().absolutePath(); - if (binOffset>= 0) - binPath += binPath.mid(binOffset, -1); + if (binOffset >= 0) + binPath += adjustedBin; } QString cmdline -- cgit v1.3.1 From b4eae6e5d1cbd92140e9a1058d623a4338a35c43 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 17 Apr 2018 00:33:15 -0500 Subject: Fix signal/slot connector for available server parser --- 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 651afba9..802adc24 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -345,7 +345,7 @@ MainWindow::MainWindow(QSettings &initSettings connect(m_OrganizerCore.updater(), SIGNAL(motdAvailable(QString)), this, SLOT(motdReceived(QString))); connect(NexusInterface::instance(&pluginContainer), SIGNAL(requestNXMDownload(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); - connect(NexusInterface::instance(&pluginContainer), SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(int,int,QVariant,QVariant,int))); + connect(NexusInterface::instance(&pluginContainer), SIGNAL(nxmDownloadURLsAvailable(QString,int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(QString,int,int,QVariant,QVariant,int))); connect(NexusInterface::instance(&pluginContainer), SIGNAL(needLogin()), &m_OrganizerCore, SLOT(nexusLogin())); connect(NexusInterface::instance(&pluginContainer)->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); connect(NexusInterface::instance(&pluginContainer)->getAccessManager(), SIGNAL(credentialsReceived(const QString&, bool)), -- cgit v1.3.1 From 7688175c68c2f1ce85951173c0aa3e186587aa1f Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 17 Apr 2018 02:47:07 -0500 Subject: Add source game setter in mod info dialog --- src/modinfo.h | 23 ++++++++++++++-------- src/modinfobackup.h | 1 + src/modinfodialog.cpp | 25 ++++++++++++++++++++++++ src/modinfodialog.h | 1 + src/modinfodialog.ui | 52 +++++++++++++++++++++++++++++++++++++++----------- src/modinfoforeign.h | 1 + src/modinfooverwrite.h | 1 + src/modinforegular.cpp | 6 ++++++ src/modinforegular.h | 7 +++++++ 9 files changed, 98 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/modinfo.h b/src/modinfo.h index f321f068..07dccafd 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -265,7 +265,14 @@ public: * @brief change the notes (manually set information) for this mod * @param notes new notes */ - virtual void setNotes(const QString ¬es) = 0; + virtual void setNotes(const QString ¬es) = 0; + + /** + * @brief set/change the source game of this mod + * + * @param gameName the source game shortName + */ + virtual void setGameName(QString gameName) = 0; /** * @brief set/change the nexus mod id of this mod @@ -411,13 +418,13 @@ public: * * @return the nexus mod id. may be 0 if the mod id isn't known or doesn't exist **/ - virtual int getNexusID() const = 0; - - /** - * @brief getter for the source game repository - * - * @return the source game repository. should default to the active game. - **/ + virtual int getNexusID() const = 0; + + /** + * @brief getter for the source game repository + * + * @return the source game repository. should default to the active game. + **/ virtual QString getGameName() const = 0; /** diff --git a/src/modinfobackup.h b/src/modinfobackup.h index aa59dda4..0a59691b 100644 --- a/src/modinfobackup.h +++ b/src/modinfobackup.h @@ -14,6 +14,7 @@ public: virtual bool updateIgnored() const { return false; } virtual bool downgradeAvailable() const { return false; } virtual bool updateNXMInfo() { return false; } + virtual void setGameName(QString) {} virtual void setNexusID(int) {} virtual void endorse(bool) {} virtual int getFixedPriority() const { return -1; } diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 31ffe82d..e2da6a2c 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -92,6 +92,21 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->modIDEdit->setValidator(new QIntValidator(modIDEdit)); ui->modIDEdit->setText(QString("%1").arg(modInfo->getNexusID())); + ui->sourceGameEdit->addItem(organizerCore->managedGame()->gameName(), organizerCore->managedGame()->gameShortName()); + if (organizerCore->managedGame()->validShortNames().size() == 0) { + ui->sourceGameEdit->setDisabled(true); + } else { + for (auto game : pluginContainer->plugins()) { + for (QString gameName : organizerCore->managedGame()->validShortNames()) { + if (game->gameShortName().compare(gameName, Qt::CaseInsensitive) == 0) { + ui->sourceGameEdit->addItem(game->gameName(), game->gameShortName()); + break; + } + } + } + } + ui->sourceGameEdit->setCurrentIndex(ui->sourceGameEdit->findData(m_ModInfo->getGameName())); + ui->notesEdit->setText(modInfo->notes()); ui->descriptionView->setPage(new DescriptionPage); @@ -901,6 +916,16 @@ void ModInfoDialog::on_modIDEdit_editingFinished() } } +void ModInfoDialog::on_sourceGameEdit_currentIndexChanged(int) +{ + for (auto game : m_PluginContainer->plugins()) { + if (game->gameName() == ui->sourceGameEdit->currentText()) { + m_ModInfo->setGameName(game->gameShortName()); + return; + } + } +} + void ModInfoDialog::on_versionEdit_editingFinished() { VersionInfo version(ui->versionEdit->text()); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 9eb17be5..52db5654 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -179,6 +179,7 @@ private slots: void on_saveTXTButton_clicked(); void on_visitNexusLabel_linkActivated(const QString &link); void on_modIDEdit_editingFinished(); + void on_sourceGameEdit_currentIndexChanged(int); void on_versionEdit_editingFinished(); void on_iniFileView_textChanged(); void on_textFileView_textChanged(); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 05415445..9e9ba77b 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -110,19 +110,19 @@ - + false - + - Ini Tweaks *This feature is non-functional* + Ini Tweaks *This feature is non-functional* - + false - + 228 @@ -238,8 +238,8 @@ 0 0 - 98 - 28 + 676 + 126 @@ -490,7 +490,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - Qt::CustomContextMenu + Qt::CustomContextMenu Qt::ElideLeft @@ -502,7 +502,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e true - 2 + 2 365 @@ -613,6 +613,36 @@ p, li { white-space: pre-wrap; } + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + Source Game + + + + + + + Mod ID for this mod on Nexus. + + + @@ -819,9 +849,9 @@ p, li { white-space: pre-wrap; } - QQuickWidget + QWebEngineView QWidget -
QtQuickWidgets/QQuickWidget
+
QtWebEngineWidgets/QWebEngineView
diff --git a/src/modinfoforeign.h b/src/modinfoforeign.h index 23500df8..0d7b3847 100644 --- a/src/modinfoforeign.h +++ b/src/modinfoforeign.h @@ -19,6 +19,7 @@ public: virtual void setCategory(int, bool) {} virtual bool setName(const QString&) { return false; } virtual void setNotes(const QString&) {} + virtual void setGameName(QString) {} virtual void setNexusID(int) {} virtual void setNewestVersion(const MOBase::VersionInfo&) {} virtual void ignoreUpdate(bool) {} diff --git a/src/modinfooverwrite.h b/src/modinfooverwrite.h index 360c7335..5681dc6f 100644 --- a/src/modinfooverwrite.h +++ b/src/modinfooverwrite.h @@ -21,6 +21,7 @@ public: virtual void setCategory(int, bool) {} virtual bool setName(const QString&) { return false; } virtual void setNotes(const QString&) {} + virtual void setGameName(QString) {} virtual void setNexusID(int) {} virtual void setNewestVersion(const MOBase::VersionInfo&) {} virtual void ignoreUpdate(bool) {} diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index 69718705..a514e1bf 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -319,6 +319,12 @@ void ModInfoRegular::setNotes(const QString ¬es) m_MetaInfoChanged = true; } +void ModInfoRegular::setGameName(QString gameName) +{ + m_GameName = gameName; + m_MetaInfoChanged = true; +} + void ModInfoRegular::setNexusID(int modID) { m_NexusID = modID; diff --git a/src/modinforegular.h b/src/modinforegular.h index 7a6f4827..cf13872c 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -93,6 +93,13 @@ public: */ void setNotes(const QString ¬es); + /** + * @brief set/change the source game of this mod + * + * @param gameName the source game shortName + */ + void setGameName(QString gameName); + /** * @brief set/change the nexus mod id of this mod * -- cgit v1.3.1 From 634825e1b8954753315e270ee4744575f14e6ac3 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 17 Apr 2018 03:04:59 -0500 Subject: Adjust mod info dialog, add tooltips, update old --- src/modinfodialog.ui | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 9e9ba77b..20eb7c30 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -6,8 +6,8 @@ 0 0 - 720 - 648 + 790 + 534
@@ -238,7 +238,7 @@ 0 0 - 676 + 746 126 @@ -608,8 +608,8 @@ Most mods do not have optional esps, so chances are good you are looking at an e <!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; } -</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;">Mod ID for this mod on Nexus. This is filled in automatically if you downloaded and installed the mod from inside MO. Otherwise you can enter it manually. To find the correct id, find the mod on nexus. The URL will look like this: </span><a href=" http://www.skyrimnexus.com/downloads/file.php?id=1334"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">http://skyrim.nexusmods.com/downloads/file.php?id=1334</span></a><a href="http://www.skyrimnexus.com/downloads/file.php?id=1334"><span style=" font-size:8pt; color:#000000;">. In this example, 1334 is the id you're looking for. Besides: The above is the link to Mod Organizer on the Nexus. Why not go there now and endorse?</span></a></p></body></html> +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; 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;">Mod ID for this mod on Nexus. This is filled in automatically if you downloaded and installed the mod from inside MO. Otherwise you can enter it manually. To find the correct id, find the mod on nexus. The URL will look like this: <a href=" https://www.nexusmods.com/skyrimspecialedition/mods/6194"><span style=" text-decoration: underline; color:#0000ff;">https://www.nexusmods.com/skyrimspecialedition/mods/6194</span></a>. In this example, 6194 is the id you're looking for. Besides: The above is the link to Mod Organizer2 on Nexus. Why not go there now and endorse us?</p></body></html>
@@ -637,11 +637,14 @@ p, li { white-space: pre-wrap; } - - - Mod ID for this mod on Nexus. - - + + + Source game for this mod. + + + <html><head/><body><p>Source game for this mod. This determines where the mod was downloaded from and decides where to fetch info, version updates, and send endorsements. Changing this will likely require you to enter a new Mod ID.</p></body></html> + + -- cgit v1.3.1 From c669fed48ab11043c6773b64997d6a86427e40ff Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Apr 2018 21:22:46 +0200 Subject: Fixed inconsistency in line endings. --- src/modinfo.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/modinfo.h b/src/modinfo.h index 07dccafd..9e9f8971 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -265,13 +265,13 @@ public: * @brief change the notes (manually set information) for this mod * @param notes new notes */ - virtual void setNotes(const QString ¬es) = 0; - - /** - * @brief set/change the source game of this mod - * - * @param gameName the source game shortName - */ + virtual void setNotes(const QString ¬es) = 0; + + /** + * @brief set/change the source game of this mod + * + * @param gameName the source game shortName + */ virtual void setGameName(QString gameName) = 0; /** -- cgit v1.3.1 From a14ed8fcd1f0cb06fb9353a3f672a70251394e2c Mon Sep 17 00:00:00 2001 From: Al12rs Date: Mon, 16 Apr 2018 18:38:54 +0200 Subject: Changed the modifodialog to open the first enabled left most tab, if no particular tab was specified by the function caller. This will allow the users to choose what tab should be opened when opening the mod informations since the order of the tabs can be changed and is saved. --- src/mainwindow.cpp | 17 ++++++++++++++++- src/mainwindow.h | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 802adc24..401e3c8b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2461,9 +2461,24 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, connect(&dialog, SIGNAL(modOpenPrev()), this, SLOT(modOpenPrev()), Qt::QueuedConnection); connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); connect(&dialog, SIGNAL(endorseMod(ModInfo::Ptr)), this, SLOT(endorseMod(ModInfo::Ptr))); + + //Open the tab first if we want to use the standard indexes of the tabs. + if (tab != -1) { + dialog.openTab(tab); + } - dialog.openTab(tab); dialog.restoreTabState(m_OrganizerCore.settings().directInterface().value("mod_info_tabs").toByteArray()); + + //If no tab was specified use the first tab from the left based on the user order. + if (tab == -1) { + for (int i = 0; i < dialog.findChild("tabWidget")->count(); ++i) { + if (dialog.findChild("tabWidget")->isTabEnabled(i)) { + dialog.findChild("tabWidget")->setCurrentIndex(i); + break; + } + } + } + dialog.exec(); m_OrganizerCore.settings().directInterface().setValue("mod_info_tabs", dialog.saveTabState()); diff --git a/src/mainwindow.h b/src/mainwindow.h index d684dbc0..8faaa349 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -217,7 +217,7 @@ private: QList findFileInfos(const QString &path, const std::function &filter) const; bool modifyExecutablesDialog(); - void displayModInformation(int row, int tab = 0); + void displayModInformation(int row, int tab = -1); void testExtractBSA(int modIndex); void writeDataToFile(QFile &file, const QString &directory, const MOShared::DirectoryEntry &directoryEntry); -- cgit v1.3.1 From a2d7ce67aa236fe52d6e860a27950dc7e3a34bd8 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Mon, 16 Apr 2018 20:40:14 +0200 Subject: Merged the new open in explorer button with Silarn's code. --- src/modinfodialog.cpp | 8 ++++++-- src/modinfodialog.h | 1 + src/modinfodialog.ui | 36 +++++++++++++++++++++++++++++++++--- 3 files changed, 40 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index e2da6a2c..705f0019 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -721,7 +721,6 @@ void ModInfoDialog::on_deactivateESP_clicked() void ModInfoDialog::on_visitNexusLabel_linkActivated(const QString &link) { - this->close(); emit nexusLinkActivated(link); } @@ -730,7 +729,7 @@ void ModInfoDialog::linkClicked(const QUrl &url) //Ideally we'd ask the mod for the game and the web service then pass the game //and URL to the web service if (NexusInterface::instance(m_PluginContainer)->isURLGameRelated(url)) { - this->close(); + emit nexusLinkActivated(url.toString()); } else { ::ShellExecuteW(nullptr, L"open", ToWString(url.toString()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); @@ -958,6 +957,11 @@ bool ModInfoDialog::recursiveDelete(const QModelIndex &index) } +void ModInfoDialog::on_openInExplorerButton_clicked() +{ + ::ShellExecuteW(nullptr, L"explore", ToWString(m_ModInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); +} + void ModInfoDialog::deleteFile(const QModelIndex &index) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 52db5654..b5cc12a8 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -172,6 +172,7 @@ private slots: void hideTriggered(); void unhideTriggered(); + void on_openInExplorerButton_clicked(); void on_closeButton_clicked(); void on_saveButton_clicked(); void on_activateESP_clicked(); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 20eb7c30..4352ba4e 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -20,7 +20,7 @@ QTabWidget::Rounded - 0 + 8 true @@ -723,8 +723,8 @@ p, li { white-space: pre-wrap; } - - + + about:blank @@ -779,6 +779,36 @@ p, li { white-space: pre-wrap; } Filetree + + + + + + + 0 + 0 + + + + Open Mod in Explorer + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + -- cgit v1.3.1 From fc46a6a782108576ee43776109aceffbcb9668b1 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Mon, 16 Apr 2018 21:14:06 +0200 Subject: Disabled Hide inactive plugins checkbox in settings as it didn't work. --- src/settingsdialog.ui | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 3c47d226..85688cb8 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -890,6 +890,9 @@ tl;dr-version: If Nexus-features don't work, insert the current version number o + + false + Enforces that inactive ESPs and ESMs are never loaded. -- cgit v1.3.1 From 62dc387de644a0a47156079f78f7e0ab174abdaa Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Apr 2018 01:32:03 +0200 Subject: Added key shourtcuts to open in explorer a selected mo by pressing Enter or Return. Needs support for overwrte --- src/mainwindow.cpp | 18 ++++++++++++++++++ src/mainwindow.h | 4 ++++ 2 files changed, 22 insertions(+) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 401e3c8b..3eb7244a 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -150,6 +150,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include @@ -369,6 +370,9 @@ MainWindow::MainWindow(QSettings &initSettings connect(ui->espList->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(esplistSelectionsChanged(QItemSelection))); connect(ui->modList->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(modlistSelectionsChanged(QItemSelection))); + new QShortcut(QKeySequence(Qt::Key_Enter), this, SLOT(openExplorer_activated())); + new QShortcut(QKeySequence(Qt::Key_Return), this, SLOT(openExplorer_activated())); + m_UpdateProblemsTimer.setSingleShot(true); connect(&m_UpdateProblemsTimer, SIGNAL(timeout()), this, SLOT(updateProblemsButton())); @@ -2615,6 +2619,20 @@ void MainWindow::openExplorer_clicked() ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } +void MainWindow::openExplorer_activated() +{ + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() == 1 && ui->modList->hasFocus()) { + + QModelIndex idx = selection->currentIndex(); + ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + if (modInfo->isRegular()) { + ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } + + } +} + void MainWindow::information_clicked() { try { diff --git a/src/mainwindow.h b/src/mainwindow.h index 8faaa349..31c52bc0 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -386,6 +386,7 @@ private slots: void showMessage(const QString &message); void showError(const QString &message); + // main window actions void helpTriggered(); void issueTriggered(); @@ -393,6 +394,9 @@ private slots: void tutorialTriggered(); void extractBSATriggered(); + //modlist shortcuts + void openExplorer_activated(); + // modlist context menu void installMod_clicked(); void createEmptyMod_clicked(); -- cgit v1.3.1 From b1329c18cde91c9e245f59475ae3b32c2c663a8c Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Apr 2018 09:26:56 +0200 Subject: Added Enter shortcut support for ovewrite as well to open the Explorer window. --- src/mainwindow.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3eb7244a..0d9ca4a9 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2626,7 +2626,9 @@ void MainWindow::openExplorer_activated() QModelIndex idx = selection->currentIndex(); ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - if (modInfo->isRegular()) { + std::vector flags = modInfo->getFlags(); + + if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } -- cgit v1.3.1 From b64fd1ed4d68fc64ac061b0f126af54cf7147462 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Apr 2018 16:13:43 +0200 Subject: Enter shourcut will now open the mod containing the selected plugin if the ESPList has the focus. Added F5 shourcut to refresh the profile. Removed a redundant variable I saw by chance. --- src/mainwindow.cpp | 50 ++++++++++++++++++++++++++++++++++++++++---------- src/mainwindow.h | 1 + src/modlist.cpp | 1 - 3 files changed, 41 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0d9ca4a9..2d6ccc94 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -372,6 +372,9 @@ MainWindow::MainWindow(QSettings &initSettings new QShortcut(QKeySequence(Qt::Key_Enter), this, SLOT(openExplorer_activated())); new QShortcut(QKeySequence(Qt::Key_Return), this, SLOT(openExplorer_activated())); + + new QShortcut(QKeySequence::Refresh, this, SLOT(refreshProfile_activated())); + m_UpdateProblemsTimer.setSingleShot(true); connect(&m_UpdateProblemsTimer, SIGNAL(timeout()), this, SLOT(updateProblemsButton())); @@ -2621,20 +2624,47 @@ void MainWindow::openExplorer_clicked() void MainWindow::openExplorer_activated() { - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() == 1 && ui->modList->hasFocus()) { - - QModelIndex idx = selection->currentIndex(); - ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - std::vector flags = modInfo->getFlags(); - - if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { - ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + if (ui->modList->hasFocus()) { + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() == 1 ) { + + QModelIndex idx = selection->currentIndex(); + ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + std::vector flags = modInfo->getFlags(); + + if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { + ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } + } + } + + if (ui->espList->hasFocus()) { + QItemSelectionModel *selection = ui->espList->selectionModel(); + + if (selection->hasSelection() && selection->selectedRows().count() == 1) { + + 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(); + + if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) { + ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + } + + } } } +void MainWindow::refreshProfile_activated() +{ + m_OrganizerCore.profileRefresh(); +} + void MainWindow::information_clicked() { try { @@ -4557,7 +4587,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(lockESPIndex())); + menu.addAction(tr("Lock load order"), this, SLOT(f())); } try { diff --git a/src/mainwindow.h b/src/mainwindow.h index 31c52bc0..3d093131 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -396,6 +396,7 @@ private slots: //modlist shortcuts void openExplorer_activated(); + void refreshProfile_activated(); // modlist context menu void installMod_clicked(); diff --git a/src/modlist.cpp b/src/modlist.cpp index c8fd4b3e..3f9a732a 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -724,7 +724,6 @@ void ModList::highlightMods(const QItemSelection &selected, const MOShared::Dire const MOShared::FileEntry::Ptr fileEntry = directoryEntry.findFile(modName.toStdWString()); if (fileEntry.get() != nullptr) { - QString fileName; bool archive = false; std::vector> origins; { -- cgit v1.3.1 From f2762035171f60fdd0b16052c07f67a0f79d60a8 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Tue, 17 Apr 2018 21:16:44 +0200 Subject: Changed mod Information dialog "Next" and "Previous" buttons to remember the currently viewed tab. --- src/mainwindow.cpp | 16 ++++++++-------- src/mainwindow.h | 4 ++-- src/modinfodialog.cpp | 14 ++++++++++---- src/modinfodialog.h | 4 ++-- 4 files changed, 22 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2d6ccc94..00c7895e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2464,8 +2464,8 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, connect(&dialog, SIGNAL(nexusLinkActivated(QString)), this, SLOT(nexusLinkActivated(QString))); connect(&dialog, SIGNAL(downloadRequest(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection); - connect(&dialog, SIGNAL(modOpenNext()), this, SLOT(modOpenNext()), Qt::QueuedConnection); - connect(&dialog, SIGNAL(modOpenPrev()), this, SLOT(modOpenPrev()), Qt::QueuedConnection); + connect(&dialog, SIGNAL(modOpenNext(int)), this, SLOT(modOpenNext(int)), Qt::QueuedConnection); + 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))); @@ -2526,7 +2526,7 @@ void MainWindow::setWindowEnabled(bool enabled) } -void MainWindow::modOpenNext() +void MainWindow::modOpenNext(int tab) { QModelIndex index = m_ModListSortProxy->mapFromSource(m_OrganizerCore.modList()->index(m_ContextRow, 0)); index = m_ModListSortProxy->index((index.row() + 1) % m_ModListSortProxy->rowCount(), 0); @@ -2537,13 +2537,13 @@ void MainWindow::modOpenNext() if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end())) { // skip overwrite and backups - modOpenNext(); + modOpenNext(tab); } else { - displayModInformation(m_ContextRow); + displayModInformation(m_ContextRow,tab); } } -void MainWindow::modOpenPrev() +void MainWindow::modOpenPrev(int tab) { QModelIndex index = m_ModListSortProxy->mapFromSource(m_OrganizerCore.modList()->index(m_ContextRow, 0)); int row = index.row() - 1; @@ -2557,9 +2557,9 @@ void MainWindow::modOpenPrev() if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end())) { // skip overwrite and backups - modOpenPrev(); + modOpenPrev(tab); } else { - displayModInformation(m_ContextRow); + displayModInformation(m_ContextRow,tab); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 3d093131..b06da353 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -474,8 +474,8 @@ private slots: void deselectFilters(); void displayModInformation(const QString &modName, int tab); - void modOpenNext(); - void modOpenPrev(); + void modOpenNext(int tab=-1); + void modOpenPrev(int tab=-1); void modRenamed(const QString &oldName, const QString &newName); void modRemoved(const QString &fileName); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 705f0019..c25041f2 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -1448,14 +1448,20 @@ void ModInfoDialog::on_endorseBtn_clicked() void ModInfoDialog::on_nextButton_clicked() { - emit modOpenNext(); - this->accept(); + int currentTab = ui->tabWidget->currentIndex(); + int tab = m_RealTabPos[currentTab]; + + emit modOpenNext(tab); + this->accept(); } void ModInfoDialog::on_prevButton_clicked() { - emit modOpenPrev(); - this->accept(); + int currentTab = ui->tabWidget->currentIndex(); + int tab = m_RealTabPos[currentTab]; + + emit modOpenPrev(tab); + this->accept(); } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index b5cc12a8..ddd72f4d 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -112,8 +112,8 @@ signals: void nexusLinkActivated(const QString &link); void downloadRequest(const QString &link); void modOpen(const QString &modName, int tab); - void modOpenNext(); - void modOpenPrev(); + void modOpenNext(int tab=-1); + void modOpenPrev(int tab=-1); void originModified(int originID); void endorseMod(ModInfo::Ptr nexusID); -- cgit v1.3.1 From 189445191d40d5b53e05d44cb128d476e817e34d Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 18 Apr 2018 00:08:05 +0200 Subject: Added Delete shourcut to both modinfodialog and Overwriteinfodilaog. Added confirm messages since delete can't be undone. --- src/modinfodialog.cpp | 33 +++++++++++++++++++++++++++++++++ src/modinfodialog.h | 2 ++ src/overwriteinfodialog.cpp | 34 ++++++++++++++++++++++++++++++++++ src/overwriteinfodialog.h | 2 ++ 4 files changed, 71 insertions(+) (limited to 'src') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index c25041f2..710b9696 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -46,6 +46,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include @@ -118,6 +119,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo //TODO: No easy way to delegate links //ui->descriptionView->page()->acceptNavigationRequest(QWebEnginePage::DelegateAllLinks); + new QShortcut(QKeySequence::Delete, this, SLOT(delete_activated())); + if (directory->originExists(ToWString(modInfo->name()))) { m_Origin = &directory->getOriginByName(ToWString(modInfo->name())); if (m_Origin->isDisabled()) { @@ -973,6 +976,36 @@ void ModInfoDialog::deleteFile(const QModelIndex &index) } } +void ModInfoDialog::delete_activated() +{ + if (ui->fileTree->hasFocus()) { + QItemSelectionModel *selection = ui->fileTree->selectionModel(); + + if (selection->hasSelection() && selection->selectedRows().count() >= 1) { + + if (selection->selectedRows().count() == 0) { + return; + } + else if (selection->selectedRows().count() == 1) { + QString fileName = m_FileSystemModel->fileName(selection->selectedRows().at(0)); + if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete \"%1\"?").arg(fileName), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + else { + if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete the selected files?"), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + + foreach(QModelIndex index, selection->selectedRows()) { + deleteFile(index); + } + } + } +} void ModInfoDialog::deleteTriggered() { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index ddd72f4d..4709a1d2 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -165,6 +165,8 @@ private slots: void thumbnailClicked(const QString &fileName); void linkClicked(const QUrl &url); + void delete_activated(); + void deleteTriggered(); void renameTriggered(); void openTriggered(); diff --git a/src/overwriteinfodialog.cpp b/src/overwriteinfodialog.cpp index e956b025..3bb67354 100644 --- a/src/overwriteinfodialog.cpp +++ b/src/overwriteinfodialog.cpp @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "utility.h" #include #include +#include #include @@ -89,6 +90,9 @@ OverwriteInfoDialog::OverwriteInfoDialog(ModInfo::Ptr modInfo, QWidget *parent) m_RenameAction = new QAction(tr("&Rename"), ui->filesView); m_OpenAction = new QAction(tr("&Open"), ui->filesView); m_NewFolderAction = new QAction(tr("&New Folder"), ui->filesView); + + new QShortcut(QKeySequence::Delete, this, SLOT(delete_activated())); + QObject::connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered())); QObject::connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered())); QObject::connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered())); @@ -145,6 +149,36 @@ void OverwriteInfoDialog::deleteFile(const QModelIndex &index) } } +void OverwriteInfoDialog::delete_activated() +{ + if (ui->filesView->hasFocus()) { + QItemSelectionModel *selection = ui->filesView->selectionModel(); + + if (selection->hasSelection() && selection->selectedRows().count() >= 1) { + + if (selection->selectedRows().count() == 0) { + return; + } + else if (selection->selectedRows().count() == 1) { + QString fileName = m_FileSystemModel->fileName(selection->selectedRows().at(0)); + if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete \"%1\"?").arg(fileName), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + else { + if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete the selected files?"), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + + foreach(QModelIndex index, selection->selectedRows()) { + deleteFile(index); + } + } + } +} void OverwriteInfoDialog::deleteTriggered() { diff --git a/src/overwriteinfodialog.h b/src/overwriteinfodialog.h index eb182292..7d44c9e8 100644 --- a/src/overwriteinfodialog.h +++ b/src/overwriteinfodialog.h @@ -49,6 +49,8 @@ private: private slots: + void delete_activated(); + void deleteTriggered(); void renameTriggered(); void openTriggered(); -- cgit v1.3.1 From 85faed5de109c0d5773626370bb6dbd338293f63 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 17 Apr 2018 19:14:17 -0500 Subject: Fix issues with download query --- src/downloadmanager.cpp | 20 ++++++++++++++++---- src/nexusinterface.cpp | 39 +++++++++++++++++++++++++++------------ src/nexusinterface.h | 2 ++ 3 files changed, 45 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index d54d6f24..23a1931e 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -738,6 +738,20 @@ void DownloadManager::queryInfo(int index) return; } } + + if (info->m_FileInfo->gameName.size() == 0) { + SelectionDialog selection(tr("Please select the source game code for %1").arg(getFileName(index))); + + std::vector> choices = m_NexusInterface->getGameChoices(m_ManagedGame); + for (auto choice : choices) { + selection.addChoice(choice.first, choice.second, choice.first); + } + if (selection.exec() == QDialog::Accepted) { + info->m_FileInfo->gameName = selection.getChoiceData().toString(); + } else { + info->m_FileInfo->gameName = m_ManagedGame->gameShortName(); + } + } info->m_ReQueried = true; setState(info, STATE_FETCHINGMODINFO); } @@ -1204,13 +1218,11 @@ void DownloadManager::nxmFilesAvailable(QString, int, QVariant userData, QVarian if (!info->m_FileInfo->version.isValid()) { info->m_FileInfo->version = info->m_FileInfo->newestVersion; } - // we receive some names html-encoded. This is used to decode it - QTextDocument doc; - doc.setHtml(fileInfo["modName"].toString()); - info->m_FileInfo->modName = doc.toPlainText(); info->m_FileInfo->fileCategory = convertFileCategory(fileInfo["category_id"].toInt()); info->m_FileInfo->fileTime = matchDate(fileInfo["date"].toString()); info->m_FileInfo->fileID = fileInfo["id"].toInt(); + info->m_FileInfo->fileName = fileInfo["uri"].toString(); + info->m_FileInfo->description = BBCode::convertToHTML(fileInfo["description"].toString()); found = true; break; } diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 434c0334..6cf5b9e9 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -270,6 +270,21 @@ QString NexusInterface::getModURL(int modID, QString gameName = "") const return QString("%1/mods/%2").arg(getGameURL(gameName)).arg(modID); } +std::vector> NexusInterface::getGameChoices(const MOBase::IPluginGame *game) +{ + std::vector> choices; + choices.push_back(std::pair(game->gameShortName(), game->gameName())); + for (QString gameName : game->validShortNames()) { + for (auto gamePlugin : m_PluginContainer->plugins()) { + if (gamePlugin->gameShortName().compare(gameName, Qt::CaseInsensitive) == 0) { + choices.push_back(std::pair(gamePlugin->gameShortName(), gamePlugin->gameName())); + break; + } + } + } + return choices; +} + bool NexusInterface::isModURL(int modID, const QString &url) const { if (QUrl(url) == QUrl(getModURL(modID))) { @@ -291,11 +306,11 @@ int NexusInterface::requestDescription(QString gameName, int modID, QObject *rec NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_DESCRIPTION, userData, subModule, game); m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmDescriptionAvailable(QString,int,QVariant,QVariant,int)), - receiver, SLOT(nxmDescriptionAvailable(QString,int,QVariant,QVariant,int)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmDescriptionAvailable(QString, int, QVariant, QVariant, int)), + receiver, SLOT(nxmDescriptionAvailable(QString, int, QVariant, QVariant, int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(QString,int,int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(QString,int,int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; @@ -349,11 +364,11 @@ int NexusInterface::requestFiles(QString gameName, int modID, QObject *receiver, { NXMRequestInfo requestInfo(modID, NXMRequestInfo::TYPE_FILES, userData, subModule, game); m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmFilesAvailable(QString,int,QVariant,QVariant,int)), - receiver, SLOT(nxmFilesAvailable(QString,int,QVariant,QVariant,int)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmFilesAvailable(QString, int, QVariant, QVariant, int)), + receiver, SLOT(nxmFilesAvailable(QString, int, QVariant, QVariant, int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(QString,int,int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(QString,int,int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; @@ -404,11 +419,11 @@ int NexusInterface::requestToggleEndorsement(QString gameName, int modID, bool e requestInfo.m_Endorse = endorse; m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmEndorsementToggled(QString,int,QVariant,QVariant,int)), - receiver, SLOT(nxmEndorsementToggled(QString,int,QVariant,QVariant,int)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmEndorsementToggled(QString, int, QVariant, QVariant, int)), + receiver, SLOT(nxmEndorsementToggled(QString, int, QVariant, QVariant, int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(QString,int,int,QVariant,int,QString)), - receiver, SLOT(nxmRequestFailed(QString,int,int,QVariant,int,QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QString)), Qt::UniqueConnection); nextRequest(); return requestInfo.m_ID; diff --git a/src/nexusinterface.h b/src/nexusinterface.h index 9a01c5c5..4a5ac0dd 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -288,6 +288,8 @@ public: */ void loginCompleted(); + std::vector> getGameChoices(const MOBase::IPluginGame *game); + public: /** -- cgit v1.3.1 From 08b56ee783131537d3f87e60819e88316ea1e062 Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 18 Apr 2018 03:41:53 -0500 Subject: Improve and fix handling of external links --- src/CMakeLists.txt | 2 ++ src/aboutdialog.cpp | 5 +++++ src/aboutdialog.h | 5 +++++ src/aboutdialog.ui | 2 +- src/mainwindow.cpp | 31 ++++++++++++++----------------- src/mainwindow.h | 1 - src/modidlineedit.cpp | 19 +++++++++++++++++++ src/modidlineedit.h | 22 ++++++++++++++++++++++ src/modinfodialog.cpp | 12 ++++++++++-- src/modinfodialog.h | 3 ++- src/modinfodialog.ui | 15 ++++++++++----- src/modinforegular.cpp | 2 +- 12 files changed, 91 insertions(+), 28 deletions(-) create mode 100644 src/modidlineedit.cpp create mode 100644 src/modidlineedit.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fef640b3..066d08d6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,7 @@ SET(organizer_SRCS motddialog.cpp modlistsortproxy.cpp modlist.cpp + modidlineedit.cpp modinfodialog.cpp modinfo.cpp modinfobackup.cpp @@ -120,6 +121,7 @@ SET(organizer_HDRS motddialog.h modlistsortproxy.h modlist.h + modidlineedit.h modinfodialog.h modinfo.h modinfobackup.h diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp index a350036f..9d68fe2b 100644 --- a/src/aboutdialog.cpp +++ b/src/aboutdialog.cpp @@ -114,3 +114,8 @@ void AboutDialog::on_creditsList_currentItemChanged(QListWidgetItem *current, QL ui->licenseText->setText(tr("No license")); } } + +void AboutDialog::on_copyrightText_linkActivated(const QString &link) +{ + emit linkClicked(link); +} \ No newline at end of file diff --git a/src/aboutdialog.h b/src/aboutdialog.h index 3ae3237b..285d0066 100644 --- a/src/aboutdialog.h +++ b/src/aboutdialog.h @@ -40,6 +40,10 @@ public: explicit AboutDialog(const QString &version, QWidget *parent = 0); ~AboutDialog(); +signals: + + void linkClicked(QString link); + private: enum Licenses { @@ -70,6 +74,7 @@ private: private slots: void on_creditsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); + void on_copyrightText_linkActivated(const QString &link); private: diff --git a/src/aboutdialog.ui b/src/aboutdialog.ui index 69e52708..fc2f3290 100644 --- a/src/aboutdialog.ui +++ b/src/aboutdialog.ui @@ -125,7 +125,7 @@ - + <html><head/><body><p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.</p><p>See the GNU General Public License for more details.</p><p>Source code can be found at <a href="https://github.com/Modorganizer2/modorganizer"><span style=" text-decoration: underline; color:#007af4;">GitHub</span></a>.</p></body></html> diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 00c7895e..6b176868 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -98,6 +98,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -319,6 +320,11 @@ MainWindow::MainWindow(QSettings &initSettings ui->groupCombo->installEventFilter(noWheel); ui->profileBox->installEventFilter(noWheel); + if (organizerCore.managedGame()->sortMechanism() == MOBase::IPluginGame::SortMechanism::NONE) { + ui->bossButton->setDisabled(true); + ui->bossButton->setToolTip(tr("There is no supported sort mechanism for this game. You will probably have to use a third-party tool.")); + } + connect(ui->savegameList, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(saveSelectionChanged(QListWidgetItem*))); connect(ui->modList, SIGNAL(dropModeUpdate(bool)), m_OrganizerCore.modList(), SLOT(dropModeUpdate(bool))); @@ -667,6 +673,7 @@ size_t MainWindow::checkForProblems() void MainWindow::about() { AboutDialog dialog(m_OrganizerCore.getVersion().displayString(), this); + connect(&dialog, SIGNAL(linkClicked(QString)), this, SLOT(linkClicked(QString))); dialog.exec(); } @@ -1008,8 +1015,7 @@ void MainWindow::modPagePluginInvoke() m_IntegratedBrowser.setWindowTitle(plugin->displayName()); m_IntegratedBrowser.openUrl(plugin->pageURL()); } else { - ::ShellExecuteW(nullptr, L"open", ToWString(plugin->pageURL().toString()).c_str(), - nullptr, nullptr, SW_SHOWNORMAL); + QDesktopServices::openUrl(QUrl(plugin->pageURL())); } } } @@ -1894,12 +1900,12 @@ void MainWindow::helpTriggered() void MainWindow::wikiTriggered() { - ::ShellExecuteW(nullptr, L"open", L"http://wiki.step-project.com/Guide:Mod_Organizer", nullptr, nullptr, SW_SHOWNORMAL); + QDesktopServices::openUrl(QUrl("http://wiki.step-project.com/Guide:Mod_Organizer")); } void MainWindow::issueTriggered() { - ::ShellExecuteW(nullptr, L"open", L"http://github.com/Modorganizer2/modorganizer/issues", nullptr, nullptr, SW_SHOWNORMAL); + QDesktopServices::openUrl(QUrl("http://github.com/Modorganizer2/modorganizer/issues")); } void MainWindow::tutorialTriggered() @@ -2461,7 +2467,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, } else { modInfo->saveMeta(); ModInfoDialog dialog(modInfo, m_OrganizerCore.directoryStructure(), modInfo->hasFlag(ModInfo::FLAG_FOREIGN), &m_OrganizerCore, &m_PluginContainer,this); - connect(&dialog, SIGNAL(nexusLinkActivated(QString)), this, SLOT(nexusLinkActivated(QString))); + connect(&dialog, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString))); connect(&dialog, SIGNAL(downloadRequest(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenNext(int)), this, SLOT(modOpenNext(int)), Qt::QueuedConnection); @@ -2599,7 +2605,7 @@ void MainWindow::visitOnNexus_clicked() int modID = m_OrganizerCore.modList()->data(m_OrganizerCore.modList()->index(m_ContextRow, 0), Qt::UserRole).toInt(); QString gameName = m_OrganizerCore.modList()->data(m_OrganizerCore.modList()->index(m_ContextRow, 0), Qt::UserRole + 4).toString(); if (modID > 0) { - nexusLinkActivated(NexusInterface::instance(&m_PluginContainer)->getModURL(modID, gameName)); + linkClicked(NexusInterface::instance(&m_PluginContainer)->getModURL(modID, gameName)); } else { MessageDialog::showMessage(tr("Nexus ID for this Mod is unknown"), this); } @@ -3746,22 +3752,13 @@ void MainWindow::on_actionSettings_triggered() void MainWindow::on_actionNexus_triggered() { - ::ShellExecuteW(nullptr, L"open", - NexusInterface::instance(&m_PluginContainer)->getGameURL(m_OrganizerCore.managedGame()->gameShortName()).toStdWString().c_str(), - nullptr, nullptr, SW_SHOWNORMAL); -} - - -void MainWindow::nexusLinkActivated(const QString &link) -{ - ::ShellExecuteW(nullptr, L"open", ToWString(link).c_str(), nullptr, nullptr, SW_SHOWNORMAL); - ui->tabWidget->setCurrentIndex(4); + QDesktopServices::openUrl(QUrl(NexusInterface::instance(&m_PluginContainer)->getGameURL(m_OrganizerCore.managedGame()->gameShortName()))); } void MainWindow::linkClicked(const QString &url) { - ::ShellExecuteW(nullptr, L"open", ToWString(url).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + QDesktopServices::openUrl(QUrl(url)); } diff --git a/src/mainwindow.h b/src/mainwindow.h index b06da353..7f38d766 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -443,7 +443,6 @@ private slots: // nexus related void checkModsForUpdates(); - void nexusLinkActivated(const QString &link); void loginFailed(const QString &message); diff --git a/src/modidlineedit.cpp b/src/modidlineedit.cpp new file mode 100644 index 00000000..7d1f0c38 --- /dev/null +++ b/src/modidlineedit.cpp @@ -0,0 +1,19 @@ +#include "modidlineedit.h" +#include + +#include +#include + +ModIDLineEdit::ModIDLineEdit(QWidget *parent) : QLineEdit(parent) {} +ModIDLineEdit::ModIDLineEdit(const QString &text, QWidget *parent) : QLineEdit(text, parent) {} + +bool ModIDLineEdit::event(QEvent *event) +{ + if (event->type() == QEvent::WhatsThisClicked) + { + QWhatsThisClickedEvent *wtcEvent = static_cast(event); + emit linkClicked(wtcEvent->href().trimmed()); + } + + return QLineEdit::event(event); +} \ No newline at end of file diff --git a/src/modidlineedit.h b/src/modidlineedit.h new file mode 100644 index 00000000..2a3db36c --- /dev/null +++ b/src/modidlineedit.h @@ -0,0 +1,22 @@ +#ifndef MODIDLINEEDIT_H +#define MODIDLINEEDIT_H + +#include + +class ModIDLineEdit : public QLineEdit +{ + Q_OBJECT + +public: + explicit ModIDLineEdit(QWidget *parent = 0); + explicit ModIDLineEdit(const QString &text, QWidget *parent = 0); + +public: + virtual bool event(QEvent* event) override; + +signals: + void linkClicked(QString); + +}; + +#endif //MODIDLINEEDIT_H \ No newline at end of file diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 710b9696..a137873e 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -22,6 +22,7 @@ along with Mod Organizer. If not, see . #include "descriptionpage.h" #include "mainwindow.h" +#include "modidlineedit.h" #include "iplugingame.h" #include "nexusinterface.h" #include "report.h" @@ -93,6 +94,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->modIDEdit->setValidator(new QIntValidator(modIDEdit)); ui->modIDEdit->setText(QString("%1").arg(modInfo->getNexusID())); + connect(ui->modIDEdit, SIGNAL(linkClicked(QString)), this, SLOT(linkClicked(QString))); + ui->sourceGameEdit->addItem(organizerCore->managedGame()->gameName(), organizerCore->managedGame()->gameShortName()); if (organizerCore->managedGame()->validShortNames().size() == 0) { ui->sourceGameEdit->setDisabled(true); @@ -724,7 +727,7 @@ void ModInfoDialog::on_deactivateESP_clicked() void ModInfoDialog::on_visitNexusLabel_linkActivated(const QString &link) { - emit nexusLinkActivated(link); + emit linkActivated(link); } void ModInfoDialog::linkClicked(const QUrl &url) @@ -733,12 +736,17 @@ void ModInfoDialog::linkClicked(const QUrl &url) //and URL to the web service if (NexusInterface::instance(m_PluginContainer)->isURLGameRelated(url)) { - emit nexusLinkActivated(url.toString()); + emit linkActivated(url.toString()); } else { ::ShellExecuteW(nullptr, L"open", ToWString(url.toString()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); } } +void ModInfoDialog::linkClicked(QString url) +{ + emit linkActivated(url); +} + void ModInfoDialog::refreshNexusData(int modID) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 4709a1d2..f3bc48b4 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -109,7 +109,7 @@ public: signals: void thumbnailClickedSignal(const QString &filename); - void nexusLinkActivated(const QString &link); + void linkActivated(const QString &link); void downloadRequest(const QString &link); void modOpen(const QString &modName, int tab); void modOpenNext(int tab=-1); @@ -164,6 +164,7 @@ private slots: void thumbnailClicked(const QString &fileName); void linkClicked(const QUrl &url); + void linkClicked(QString url); void delete_activated(); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 4352ba4e..de9fe5ed 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -238,8 +238,8 @@ 0 0 - 746 - 126 + 98 + 28 @@ -600,7 +600,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + Mod ID for this mod on Nexus. @@ -723,8 +723,8 @@ p, li { white-space: pre-wrap; } - - + + about:blank @@ -886,6 +886,11 @@ p, li { white-space: pre-wrap; } QWidget
QtWebEngineWidgets/QWebEngineView
+ + ModIDLineEdit + QLineEdit +
modidlineedit.h
+
diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index a514e1bf..045366ba 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -459,7 +459,7 @@ std::vector ModInfoRegular::getContents() const if (dir.exists(sePluginPath)) m_CachedContent.push_back(CONTENT_SKSE); } - if (dir.exists("textures")) + if (dir.exists("textures") || dir.exists("icons") || dir.exists("bookart")) m_CachedContent.push_back(CONTENT_TEXTURE); if (dir.exists("meshes")) m_CachedContent.push_back(CONTENT_MESH); -- cgit v1.3.1 From 7e0454276207cb70feeb05850847204d1bb43fd9 Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 18 Apr 2018 11:46:42 -0500 Subject: Some tweaks to make MO the top window when loading or changing games --- src/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/main.cpp b/src/main.cpp index d67c2673..2e496e0f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -72,6 +72,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include @@ -481,6 +482,8 @@ int runApplication(MOApplication &application, SingleInstance &instance, return 1; } + QWindowsWindowFunctions::setWindowActivationBehavior(QWindowsWindowFunctions::AlwaysActivateWindow); + QStringList arguments = application.arguments(); try { @@ -539,6 +542,7 @@ int runApplication(MOApplication &application, SingleInstance &instance, "start the game correctly if this is set " "incorrectly!)"), nullptr); + selection.setWindowFlag(Qt::WindowStaysOnTopHint, true); int index = 0; for (const QString &edition : editions) { selection.addChoice(edition, "", index++); @@ -602,6 +606,7 @@ int runApplication(MOApplication &application, SingleInstance &instance, QPixmap pixmap(splashPath); QSplashScreen splash(pixmap); splash.show(); + splash.activateWindow(); NexusInterface::instance(&pluginContainer)->getAccessManager()->startLoginCheck(); @@ -630,6 +635,7 @@ int runApplication(MOApplication &application, SingleInstance &instance, qDebug("displaying main window"); mainWindow.show(); + mainWindow.activateWindow(); splash.finish(&mainWindow); return application.exec(); -- cgit v1.3.1 From fbb93eaf8679a64152251164a5fc2faff902efab Mon Sep 17 00:00:00 2001 From: Silarn Date: Thu, 19 Apr 2018 01:43:57 -0500 Subject: Remove remnants of QtScript and fix some CMake issues --- src/CMakeLists.txt | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 066d08d6..41891732 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -251,7 +251,6 @@ FIND_PACKAGE(Qt5Quick REQUIRED) FIND_PACKAGE(Qt5Network REQUIRED) FIND_PACKAGE(Qt5WinExtras REQUIRED) FIND_PACKAGE(Qt5WebEngineWidgets REQUIRED) -FIND_PACKAGE(Qt5Script REQUIRED) FIND_PACKAGE(Qt5Qml REQUIRED) FIND_PACKAGE(Qt5LinguistTools) QT5_WRAP_UI(organizer_UIHDRS ${organizer_UIS}) @@ -318,18 +317,27 @@ ENDIF() ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_UIHDRS} ${organizer_RCS} ${organizer_RCCPPS} ${organizer_translations_qm}) TARGET_LINK_LIBRARIES(ModOrganizer Qt5::Widgets Qt5::WinExtras Qt5::WebEngineWidgets Qt5::Quick - Qt5::Script Qt5::Qml Qt5::QuickWidgets Qt5::Network + Qt5::Qml Qt5::QuickWidgets Qt5::Network ${Boost_LIBRARIES} zlibstatic uibase esptk bsatk githubpp ${usvfs_name} Dbghelp advapi32 Version Shlwapi liblz4) + +IF (MSVC) + SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS "/std:c++latest") +ENDIF() +IF (MSVC AND "${CMAKE_SIZEOF_VOID_P}" EQUAL 4) + # 32 bits + SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES LINK_FLAGS "/LARGEADDRESSAWARE") +ENDIF() + IF (NOT "${OPTIMIZE_COMPILE_FLAGS}" STREQUAL "") - SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS_RELWITHDEBINFO - ${OPTIMIZE_COMPILE_FLAGS}) + SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS_RELWITHDEBINFO ${OPTIMIZE_COMPILE_FLAGS}) +ENDIF() +IF (NOT "${OPTIMIZE_LINK_FLAGS}" STREQUAL "") + SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES LINK_FLAGS_RELWITHDEBINFO ${OPTIMIZE_LINK_FLAGS}) ENDIF() -SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES LINK_FLAGS_RELWITHDEBINFO - "/LARGEADDRESSAWARE ${OPTIMIZE_LINK_FLAGS}") ############### -- cgit v1.3.1 From 141ac311d29f2fae6bf6ef0172708bd841e624fe Mon Sep 17 00:00:00 2001 From: Silarn Date: Sat, 21 Apr 2018 22:32:17 -0500 Subject: Don't create meta files if they don't exist --- src/downloadmanager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 23a1931e..3d88e803 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -79,6 +79,7 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(con DownloadInfo *info = new DownloadInfo; QString metaFileName = filePath + ".meta"; + if (!QFile(metaFileName).exists()) return nullptr; QSettings metaFile(metaFileName, QSettings::IniFormat); if (!showHidden && metaFile.value("removed", false).toBool()) { return nullptr; @@ -937,8 +938,8 @@ void DownloadManager::markInstalled(QString fileName) QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat); metaFile.setValue("installed", true); metaFile.setValue("uninstalled", false); + delete info; } - delete info; } } @@ -972,8 +973,8 @@ void DownloadManager::markUninstalled(QString fileName) if (info != nullptr) { QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat); metaFile.setValue("uninstalled", true); + delete info; } - delete info; } } -- cgit v1.3.1 From 001fddb069f35850b02ecac0d11f220461c70706 Mon Sep 17 00:00:00 2001 From: Al12rs Date: Sun, 22 Apr 2018 16:36:40 +0200 Subject: Changed the shortcut to open in explorer a mod to CTRL+Enter to avoid consuming the Enter key press so that people can still use it to finish renaming a mod. --- src/mainwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6b176868..e737dd38 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -376,8 +376,8 @@ MainWindow::MainWindow(QSettings &initSettings connect(ui->espList->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(esplistSelectionsChanged(QItemSelection))); connect(ui->modList->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(modlistSelectionsChanged(QItemSelection))); - new QShortcut(QKeySequence(Qt::Key_Enter), this, SLOT(openExplorer_activated())); - new QShortcut(QKeySequence(Qt::Key_Return), this, SLOT(openExplorer_activated())); + 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())); -- cgit v1.3.1 From 31bf15f3b43e52a9e9090b8e71191c81be084ba6 Mon Sep 17 00:00:00 2001 From: Silarn Date: Mon, 23 Apr 2018 22:12:46 -0700 Subject: Need to check if the file is in the download directory for creating meta --- src/downloadmanager.cpp | 9 +++++---- src/downloadmanager.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 3d88e803..aec2ad85 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -74,12 +74,13 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const Mo return info; } -DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(const QString &filePath, bool showHidden) +DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(const QString &filePath, bool showHidden, const QString outputDirectory) { DownloadInfo *info = new DownloadInfo; QString metaFileName = filePath + ".meta"; - if (!QFile(metaFileName).exists()) return nullptr; + QFileInfo metaFileInfo(metaFileName); + if (QDir::fromNativeSeparators(metaFileInfo.path()).compare(QDir::fromNativeSeparators(outputDirectory), Qt::CaseInsensitive) != 0) return nullptr; QSettings metaFile(metaFileName, QSettings::IniFormat); if (!showHidden && metaFile.value("removed", false).toBool()) { return nullptr; @@ -322,7 +323,7 @@ void DownloadManager::refreshList() QString fileName = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + file; - DownloadInfo *info = DownloadInfo::createFromMeta(fileName, m_ShowHidden); + DownloadInfo *info = DownloadInfo::createFromMeta(fileName, m_ShowHidden, m_OutputDirectory); if (info != nullptr) { m_ActiveDownloads.push_front(info); } @@ -945,7 +946,7 @@ void DownloadManager::markInstalled(QString fileName) DownloadManager::DownloadInfo* DownloadManager::getDownloadInfo(QString fileName) { - return DownloadInfo::createFromMeta(fileName, true); + return DownloadInfo::createFromMeta(fileName, true, m_OutputDirectory); } void DownloadManager::markUninstalled(int index) diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 780aeb5c..308d365b 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -93,7 +93,7 @@ private: bool m_Hidden; static DownloadInfo *createNew(const MOBase::ModRepositoryFileInfo *fileInfo, const QStringList &URLs); - static DownloadInfo *createFromMeta(const QString &filePath, bool showHidden); + static DownloadInfo *createFromMeta(const QString &filePath, bool showHidden, const QString outputDirectory); /** * @brief rename the file -- cgit v1.3.1 From b3ef1858661c73a022f8432dad0b3c9e957ac350 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 24 Apr 2018 09:22:51 -0700 Subject: Update Nexus protocol to https --- src/nexusinterface.cpp | 2 +- src/nxmaccessmanager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 6cf5b9e9..c79e17a0 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -645,7 +645,7 @@ void NexusInterface::managedGameChanged(IPluginGame const *game) namespace { QString get_management_url(MOBase::IPluginGame const *game) { - return "http://nmm.nexusmods.com/" + game->gameNexusName().toLower(); + return "https://nmm.nexusmods.com/" + game->gameNexusName().toLower(); } } diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 17c50e35..5b067d3c 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -42,7 +42,7 @@ along with Mod Organizer. If not, see . using namespace MOBase; namespace { - QString const Nexus_Management_URL("http://nmm.nexusmods.com"); + QString const Nexus_Management_URL("https://nmm.nexusmods.com"); } // unfortunately Nexus doesn't seem to document these states, all I know is all these listed -- cgit v1.3.1 From 10b8d121527fe7b2b79768717c27c563009c20ef Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 24 Apr 2018 09:28:08 -0700 Subject: Update to https for other nexus links For Security --- src/nexusinterface.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index c79e17a0..45811658 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -254,14 +254,14 @@ QString NexusInterface::getGameURL(QString gameName) const { IPluginGame *game = getGame(gameName); if (game != nullptr) { - return "http://www.nexusmods.com/" + game->gameNexusName().toLower(); + return "https://www.nexusmods.com/" + game->gameNexusName().toLower(); } - return "http://www.nexusmods.com/" + m_Game->gameNexusName().toLower(); + return "https://www.nexusmods.com/" + m_Game->gameNexusName().toLower(); } QString NexusInterface::getOldModsURL() const { - return "http://" + m_Game->gameNexusName().toLower() + ".nexusmods.com/mods"; + return "https://" + m_Game->gameNexusName().toLower() + ".nexusmods.com/mods"; } -- cgit v1.3.1 From cfbe399bbaa6c244359682b71a949bf6cf4d3b4e Mon Sep 17 00:00:00 2001 From: Al12rs Date: Wed, 25 Apr 2018 23:48:18 +0200 Subject: Fixed mod rename to allow just changing letters to uppercase or lowercase. (Previously it would not allow it as it detected an existing mod having the same name (check is caseInsensitive)). --- src/modlist.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modlist.cpp b/src/modlist.cpp index 3f9a732a..8c906822 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -436,14 +436,14 @@ bool ModList::renameMod(int index, const QString &newName) return false; } - if (ModList::allMods().contains(newName,Qt::CaseInsensitive)) { + if (ModList::allMods().contains(nameFixed, Qt::CaseInsensitive) && nameFixed.toLower()!=ModInfo::getByIndex(index)->name().toLower() ) { MessageDialog::showMessage(tr("Name is already in use by another mod"), nullptr); return false; } ModInfo::Ptr modInfo = ModInfo::getByIndex(index); QString oldName = modInfo->name(); - if (newName != oldName) { + if (nameFixed != oldName) { // before we rename, ensure there is no scheduled asynchronous to rewrite m_Profile->cancelModlistWrite(); @@ -1211,4 +1211,3 @@ bool ModList::eventFilter(QObject *obj, QEvent *event) } return QAbstractItemModel::eventFilter(obj, event); } - -- cgit v1.3.1 From e02614ee1455f0ae9fb9ec9df3b928381ae98079 Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 27 Apr 2018 01:57:59 -0500 Subject: Add alternative game flag and check --- src/modflagicondelegate.cpp | 1 + src/modinfo.cpp | 8 ++++---- src/modinfo.h | 5 +++-- src/modinfobackup.cpp | 4 ++-- src/modinfobackup.h | 2 +- src/modinforegular.cpp | 13 +++++++++---- src/modinforegular.h | 6 ++++-- src/modlist.cpp | 7 +++++++ src/organizercore.cpp | 2 +- 9 files changed, 32 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/modflagicondelegate.cpp b/src/modflagicondelegate.cpp index 61df0a0d..8b54ab00 100644 --- a/src/modflagicondelegate.cpp +++ b/src/modflagicondelegate.cpp @@ -48,6 +48,7 @@ QString ModFlagIconDelegate::getFlagIcon(ModInfo::EFlag flag) const case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return ":/MO/gui/emblem_conflict_overwritten"; case ModInfo::FLAG_CONFLICT_MIXED: return ":/MO/gui/emblem_conflict_mixed"; case ModInfo::FLAG_CONFLICT_REDUNDANT: return ":MO/gui/emblem_conflict_redundant"; + case ModInfo::FLAG_ALTERNATE_GAME: return ":MO/gui/problem"; default: return QString(); } } diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 2cd3aec7..d916d982 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -61,16 +61,16 @@ bool ModInfo::ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS) } -ModInfo::Ptr ModInfo::createFrom(PluginContainer *pluginContainer, QString gameName, const QDir &dir, DirectoryEntry **directoryStructure) +ModInfo::Ptr ModInfo::createFrom(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &dir, DirectoryEntry **directoryStructure) { QMutexLocker locker(&s_Mutex); // int id = s_NextID++; static QRegExp backupExp(".*backup[0-9]*"); ModInfo::Ptr result; if (backupExp.exactMatch(dir.dirName())) { - result = ModInfo::Ptr(new ModInfoBackup(pluginContainer, gameName, dir, directoryStructure)); + result = ModInfo::Ptr(new ModInfoBackup(pluginContainer, game, dir, directoryStructure)); } else { - result = ModInfo::Ptr(new ModInfoRegular(pluginContainer, gameName, dir, directoryStructure)); + result = ModInfo::Ptr(new ModInfoRegular(pluginContainer, game, dir, directoryStructure)); } s_Collection.push_back(result); return result; @@ -221,7 +221,7 @@ void ModInfo::updateFromDisc(const QString &modDirectory, mods.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QDirIterator modIter(mods); while (modIter.hasNext()) { - createFrom(pluginContainer, game->gameShortName(), QDir(modIter.next()), directoryStructure); + createFrom(pluginContainer, game, QDir(modIter.next()), directoryStructure); } } diff --git a/src/modinfo.h b/src/modinfo.h index 9e9f8971..19be2ca2 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -70,7 +70,8 @@ public: FLAG_CONFLICT_OVERWRITTEN, FLAG_CONFLICT_MIXED, FLAG_CONFLICT_REDUNDANT, - FLAG_PLUGIN_SELECTED + FLAG_PLUGIN_SELECTED, + FLAG_ALTERNATE_GAME }; enum EContent { @@ -191,7 +192,7 @@ public: * @param dir directory to create from * @return pointer to the info-structure of the newly created/added mod */ - static ModInfo::Ptr createFrom(PluginContainer *pluginContainer, QString gameName, const QDir &dir, MOShared::DirectoryEntry **directoryStructure); + static ModInfo::Ptr createFrom(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &dir, MOShared::DirectoryEntry **directoryStructure); /** * @brief create a new "foreign-managed" mod from a tuple of plugin and archives diff --git a/src/modinfobackup.cpp b/src/modinfobackup.cpp index b779dca1..6e307103 100644 --- a/src/modinfobackup.cpp +++ b/src/modinfobackup.cpp @@ -14,7 +14,7 @@ QString ModInfoBackup::getDescription() const } -ModInfoBackup::ModInfoBackup(PluginContainer *pluginContainer, QString gameName, const QDir &path, MOShared::DirectoryEntry **directoryStructure) - : ModInfoRegular(pluginContainer, gameName, path, directoryStructure) +ModInfoBackup::ModInfoBackup(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure) + : ModInfoRegular(pluginContainer, game, path, directoryStructure) { } diff --git a/src/modinfobackup.h b/src/modinfobackup.h index 0a59691b..da1fcd4a 100644 --- a/src/modinfobackup.h +++ b/src/modinfobackup.h @@ -33,7 +33,7 @@ public: private: - ModInfoBackup(PluginContainer *pluginContainer, QString gameName, const QDir &path, MOShared::DirectoryEntry **directoryStructure); + ModInfoBackup(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure); }; diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index 045366ba..49fc3aa2 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -1,7 +1,6 @@ #include "modinforegular.h" #include "categories.h" -#include "iplugingame.h" #include "messagedialog.h" #include "report.h" #include "scriptextender.h" @@ -24,12 +23,13 @@ namespace { } } -ModInfoRegular::ModInfoRegular(PluginContainer *pluginContainer, QString gameName, const QDir &path, DirectoryEntry **directoryStructure) +ModInfoRegular::ModInfoRegular(PluginContainer *pluginContainer, const IPluginGame *game, const QDir &path, DirectoryEntry **directoryStructure) : ModInfoWithConflictInfo(pluginContainer, directoryStructure) - , m_GameName(gameName) , m_Name(path.dirName()) , m_Path(path.absolutePath()) , m_Repository() + , m_GameName(game->gameShortName()) + , m_IsAlternate(false) , m_MetaInfoChanged(false) , m_EndorsedState(ENDORSED_UNKNOWN) , m_NexusBridge(pluginContainer) @@ -38,6 +38,8 @@ ModInfoRegular::ModInfoRegular(PluginContainer *pluginContainer, QString gameNam m_CreationTime = QFileInfo(path.absolutePath()).created(); // read out the meta-file for information readMeta(); + if (m_GameName.compare(game->gameShortName(), Qt::CaseInsensitive) != 0) + m_IsAlternate = true; connect(&m_NexusBridge, SIGNAL(descriptionAvailable(QString,int,QVariant,QVariant)) , this, SLOT(nxmDescriptionAvailable(QString,int,QVariant,QVariant))); @@ -73,7 +75,7 @@ void ModInfoRegular::readMeta() QSettings metaFile(m_Path + "/meta.ini", QSettings::IniFormat); m_Notes = metaFile.value("notes", "").toString(); QString tempGameName = metaFile.value("gameName", m_GameName).toString(); - if (tempGameName != "") m_GameName = tempGameName; + if (tempGameName.size()) m_GameName = tempGameName; m_NexusID = metaFile.value("modid", -1).toInt(); m_Version.parse(metaFile.value("version", "").toString()); m_NewestVersion = metaFile.value("newestVersion", "").toString(); @@ -433,6 +435,9 @@ std::vector ModInfoRegular::getFlags() const if (m_PluginSelected) { result.push_back(ModInfo::FLAG_PLUGIN_SELECTED); } + if (m_IsAlternate) { + result.push_back(ModInfo::FLAG_ALTERNATE_GAME); + } return result; } diff --git a/src/modinforegular.h b/src/modinforegular.h index cf13872c..c47f10c8 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -2,7 +2,6 @@ #define MODINFOREGULAR_H #include "modinfowithconflictinfo.h" - #include "nexusinterface.h" /** @@ -27,6 +26,8 @@ public: virtual bool isEmpty() const; + bool isAlternate() { return m_IsAlternate; } + /** * @brief test if there is a newer version of the mod * @@ -329,7 +330,7 @@ private slots: protected: - ModInfoRegular(PluginContainer *pluginContainer, QString gameName, const QDir &path, MOShared::DirectoryEntry **directoryStructure); + ModInfoRegular(PluginContainer *pluginContainer, const MOBase::IPluginGame *game, const QDir &path, MOShared::DirectoryEntry **directoryStructure); private: @@ -349,6 +350,7 @@ private: std::set> m_InstalledFileIDs; bool m_MetaInfoChanged; + bool m_IsAlternate; MOBase::VersionInfo m_NewestVersion; MOBase::VersionInfo m_IgnoredVersion; diff --git a/src/modlist.cpp b/src/modlist.cpp index 8c906822..aad88d1f 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -25,6 +25,7 @@ along with Mod Organizer. If not, see . #include "viewmarkingscrollbar.h" #include "modlistsortproxy.h" #include "settings.h" +#include "modinforegular.h" #include #include #include @@ -149,6 +150,7 @@ QString ModList::getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return tr("Overwritten files"); case ModInfo::FLAG_CONFLICT_MIXED: return tr("Overwrites & Overwritten"); case ModInfo::FLAG_CONFLICT_REDUNDANT: return tr("Redundant"); + case ModInfo::FLAG_ALTERNATE_GAME: return tr("Alternate game source"); default: return ""; } } @@ -759,6 +761,11 @@ IModList::ModStates ModList::state(unsigned int modIndex) const if (modInfo->isValid()) { result |= IModList::STATE_VALID; } + if (modInfo->isRegular()) { + QSharedPointer modInfoRegular = modInfo.staticCast(); + if (modInfoRegular->isAlternate()) + result |= IModList::STATE_ALTERNATE; + } if (modInfo->canBeEnabled()) { if (m_Profile->modEnabled(modIndex)) { result |= IModList::STATE_ACTIVE; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 15596f75..69c28c5c 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -819,7 +819,7 @@ MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) settingsFile.endArray(); } - return ModInfo::createFrom(m_PluginContainer, m_GamePlugin->gameShortName(), QDir(targetDirectory), &m_DirectoryStructure) + return ModInfo::createFrom(m_PluginContainer, m_GamePlugin, QDir(targetDirectory), &m_DirectoryStructure) .data(); } -- cgit v1.3.1 From 41a479bddfbcd21640c0871423b819b154956cfa Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 27 Apr 2018 20:00:51 -0500 Subject: Cache the current game as populating the dropdown triggers the save --- src/modinfodialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index a137873e..12654755 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -96,6 +96,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo connect(ui->modIDEdit, SIGNAL(linkClicked(QString)), this, SLOT(linkClicked(QString))); + QString gameName = modInfo->getGameName(); ui->sourceGameEdit->addItem(organizerCore->managedGame()->gameName(), organizerCore->managedGame()->gameShortName()); if (organizerCore->managedGame()->validShortNames().size() == 0) { ui->sourceGameEdit->setDisabled(true); @@ -109,7 +110,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } } } - ui->sourceGameEdit->setCurrentIndex(ui->sourceGameEdit->findData(m_ModInfo->getGameName())); + ui->sourceGameEdit->setCurrentIndex(ui->sourceGameEdit->findData(gameName)); ui->notesEdit->setText(modInfo->notes()); -- cgit v1.3.1 From 3114bcbd8dffd60f158dd5a164f5192ed8323f96 Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 27 Apr 2018 20:21:42 -0500 Subject: Fix ESL index display and sorting by index --- src/pluginlist.cpp | 23 +++++++++++++++++++++-- src/pluginlist.h | 1 + src/pluginlistsortproxy.cpp | 4 ++-- 3 files changed, 24 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index bd863f52..7642c87f 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -485,6 +485,25 @@ int PluginList::enabledCount() const return enabled; } +int PluginList::getIndexPriority(int index) const +{ + int numESLs = 0; + std::vector sortESPs(m_ESPs); + std::sort(sortESPs.begin(), sortESPs.end()); + for (auto sortedESP : sortESPs) { + if (sortedESP.m_LoadOrder == m_ESPs[index].m_LoadOrder) + break; + if ((sortedESP.m_IsLight || sortedESP.m_IsLightFlagged) && sortedESP.m_LoadOrder != -1) + ++numESLs; + } + if (m_ESPs[index].m_IsLight || m_ESPs[index].m_IsLightFlagged) { + int ESLpos = 254 + ((numESLs + 1) / 2048); + return ESLpos + (numESLs % 2048); + } else { + return m_ESPs[index].m_LoadOrder - numESLs; + } +} + bool PluginList::isESPLocked(int index) const { return m_LockedOrder.find(m_ESPs.at(index).m_Name.toLower()) != m_LockedOrder.end(); @@ -802,8 +821,8 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const ++numESLs; } if (m_ESPs[index].m_IsLight || m_ESPs[index].m_IsLightFlagged) { - int ESLpos = 254 + ((numESLs+1) / 4096); - return QString("%1:%2").arg(ESLpos, 2, 16, QChar('0')).arg((numESLs)%4096).toUpper(); + int ESLpos = 254 + ((numESLs+1) / 2048); + return QString("%1:%2").arg(ESLpos, 2, 16, QChar('0')).arg((numESLs)%2048, 3, 16, QChar('0')).toUpper(); } else { return QString("%1").arg(m_ESPs[index].m_LoadOrder - numESLs, 2, 16, QChar('0')).toUpper(); } diff --git a/src/pluginlist.h b/src/pluginlist.h index f9173810..6a53614a 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -194,6 +194,7 @@ public: QString getName(int index) const { return m_ESPs.at(index).m_Name; } int getPriority(int index) const { return m_ESPs.at(index).m_Priority; } + int getIndexPriority(int index) const; bool isESPLocked(int index) const; void lockESPIndex(int index, bool lock); diff --git a/src/pluginlistsortproxy.cpp b/src/pluginlistsortproxy.cpp index 342b3744..b844e1d8 100644 --- a/src/pluginlistsortproxy.cpp +++ b/src/pluginlistsortproxy.cpp @@ -100,8 +100,8 @@ bool PluginListSortProxy::lessThan(const QModelIndex &left, } } break; case PluginList::COL_MODINDEX: { - int leftVal = plugins->isEnabled(left.row()) ? plugins->getPriority(left.row()) : -1; - int rightVal = plugins->isEnabled(right.row()) ? plugins->getPriority(right.row()) : -1; + int leftVal = plugins->isEnabled(left.row()) ? plugins->getIndexPriority(left.row()) : -1; + int rightVal = plugins->isEnabled(right.row()) ? plugins->getIndexPriority(right.row()) : -1; return leftVal < rightVal; } break; default: { -- cgit v1.3.1 From 86c97c7c064faa8357c0f88f57d90ddab82d6d0a Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 27 Apr 2018 20:24:20 -0500 Subject: 4096 was the correct number --- src/pluginlist.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 7642c87f..f4bdf418 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -497,8 +497,8 @@ int PluginList::getIndexPriority(int index) const ++numESLs; } if (m_ESPs[index].m_IsLight || m_ESPs[index].m_IsLightFlagged) { - int ESLpos = 254 + ((numESLs + 1) / 2048); - return ESLpos + (numESLs % 2048); + int ESLpos = 254 + ((numESLs + 1) / 4096); + return ESLpos + (numESLs % 4096); } else { return m_ESPs[index].m_LoadOrder - numESLs; } @@ -821,8 +821,8 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const ++numESLs; } if (m_ESPs[index].m_IsLight || m_ESPs[index].m_IsLightFlagged) { - int ESLpos = 254 + ((numESLs+1) / 2048); - return QString("%1:%2").arg(ESLpos, 2, 16, QChar('0')).arg((numESLs)%2048, 3, 16, QChar('0')).toUpper(); + int ESLpos = 254 + ((numESLs+1) / 4096); + return QString("%1:%2").arg(ESLpos, 2, 16, QChar('0')).arg((numESLs)%4096, 3, 16, QChar('0')).toUpper(); } else { return QString("%1").arg(m_ESPs[index].m_LoadOrder - numESLs, 2, 16, QChar('0')).toUpper(); } -- cgit v1.3.1 From 0306b0fc42ba1dbd76d86466ed045eab4e45186d Mon Sep 17 00:00:00 2001 From: Silarn Date: Sat, 28 Apr 2018 23:32:39 -0500 Subject: Change Nexus API URL --- src/browserdialog.cpp | 4 ++-- src/mainwindow.cpp | 2 +- src/nexusinterface.cpp | 2 +- src/nxmaccessmanager.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/browserdialog.cpp b/src/browserdialog.cpp index c2c65acc..e186ad63 100644 --- a/src/browserdialog.cpp +++ b/src/browserdialog.cpp @@ -173,13 +173,13 @@ void BrowserDialog::titleChanged(const QString &title) QString BrowserDialog::guessFileName(const QString &url) { - QRegExp uploadsExp(QString("http://.+/uploads/([^/]+)$")); + QRegExp uploadsExp(QString("https://.+/uploads/([^/]+)$")); if (uploadsExp.indexIn(url) != -1) { // these seem to be premium downloads return uploadsExp.cap(1); } - QRegExp filesExp(QString("http://.+\\?file=([^&]+)")); + QRegExp filesExp(QString("https://.+\\?file=([^&]+)")); if (filesExp.indexIn(url) != -1) { // a regular manual download? return filesExp.cap(1); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e737dd38..6e244d49 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1905,7 +1905,7 @@ void MainWindow::wikiTriggered() void MainWindow::issueTriggered() { - QDesktopServices::openUrl(QUrl("http://github.com/Modorganizer2/modorganizer/issues")); + QDesktopServices::openUrl(QUrl("https://github.com/Modorganizer2/modorganizer/issues")); } void MainWindow::tutorialTriggered() diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 45811658..8bf0de6e 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -645,7 +645,7 @@ void NexusInterface::managedGameChanged(IPluginGame const *game) namespace { QString get_management_url(MOBase::IPluginGame const *game) { - return "https://nmm.nexusmods.com/" + game->gameNexusName().toLower(); + return "https://legacy-api.nexusmods.com/" + game->gameNexusName().toLower(); } } diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 5b067d3c..05016e8f 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -42,7 +42,7 @@ along with Mod Organizer. If not, see . using namespace MOBase; namespace { - QString const Nexus_Management_URL("https://nmm.nexusmods.com"); + QString const Nexus_Management_URL("https://legacy-api.nexusmods.com"); } // unfortunately Nexus doesn't seem to document these states, all I know is all these listed -- cgit v1.3.1 From 275afb730fa762f09836f3b38b2e25ff0128faa0 Mon Sep 17 00:00:00 2001 From: Silarn Date: Mon, 30 Apr 2018 13:56:27 -0500 Subject: A couple download header-based fixes * If an unknown content error, only fail if the filesize is off * If a text content type, output a warning but do not retry the download --- src/downloadmanager.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index aec2ad85..77a4cf98 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -1431,15 +1431,16 @@ void DownloadManager::downloadFinished() if ((info->m_State != STATE_CANCELING) && (info->m_State != STATE_PAUSING)) { bool textData = reply->header(QNetworkRequest::ContentTypeHeader).toString().startsWith("text", Qt::CaseInsensitive); + if (textData) + 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)) || - textData) { + ((reply->error() != QNetworkReply::NoError) + && (reply->error() != QNetworkReply::OperationCanceledError) + && (reply->error() == QNetworkReply::UnknownContentError && (info->m_Output.size() != reply->header(QNetworkRequest::ContentLengthHeader).toLongLong())))) { + 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) { - if (textData && (reply->error() == QNetworkReply::NoError)) { - emit showMessage(tr("Download failed. Server reported: %1").arg(QString(data))); - } else { - emit showMessage(tr("Download failed: %1 (%2)").arg(reply->errorString()).arg(reply->error())); - } + emit showMessage(tr("Download failed: %1 (%2)").arg(reply->errorString()).arg(reply->error())); } error = true; setState(info, STATE_PAUSING); -- cgit v1.3.1 From 2b7274380ea101e3d33e9177d7d8e06011cba6e7 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 1 May 2018 12:36:47 -0500 Subject: Release memory --- src/mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6e244d49..3933987e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3545,6 +3545,7 @@ void MainWindow::deleteSavegame_clicked() } else { ISaveGame const *save = info->getSaveGameInfo(name); deleteFiles += save->allFiles(); + delete save; } } -- cgit v1.3.1 From 584c6049f981e601f6c88c00823ee322c9002bbe Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 1 May 2018 12:37:07 -0500 Subject: Remove redundant code causing multiple installs --- src/installationmanager.cpp | 9 --------- 1 file changed, 9 deletions(-) (limited to 'src') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 6785bf67..668be84e 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -785,15 +785,6 @@ bool InstallationManager::install(const QString &fileName, if (installerExt.find(fileInfo.suffix()) != installerExt.end()) { installResult = installerCustom->install(modName, fileName, version, modID); - if (installResult == IPluginInstaller::RESULT_SUCCESS) { - mapToArchive(filesTree.data()); - // the simple installer only prepares the installation, the rest - // works the same for all installers - if (!doInstall(modName, gameName, modID, version, newestVersion, categoryID, - repository)) { - installResult = IPluginInstaller::RESULT_FAILED; - } - } unsigned int idx = ModInfo::getIndex(modName); if (idx != UINT_MAX) { ModInfo::Ptr info = ModInfo::getByIndex(idx); -- cgit v1.3.1 From d3c207193fe27c42ff958e3e74cbd2628c6a14a2 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 1 May 2018 13:01:44 -0500 Subject: Add all supported games to the nxmhandler config --- src/settings.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/settings.cpp b/src/settings.cpp index 9510999d..4174c397 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -133,7 +133,11 @@ void Settings::registerAsNXMHandler(bool force) std::wstring nxmPath = ToWString(QCoreApplication::applicationDirPath() + "/nxmhandler.exe"); std::wstring executable = ToWString(QCoreApplication::applicationFilePath()); std::wstring mode = force ? L"forcereg" : L"reg"; - std::wstring parameters = mode + L" " + m_GamePlugin->gameShortName().toStdWString() + L" \"" + executable + L"\""; + std::wstring parameters = mode + L" " + m_GamePlugin->gameShortName().toStdWString(); + for (QString altGame : m_GamePlugin->validShortNames()) { + parameters += L"," + altGame.toStdWString(); + } + parameters += L" \"" + executable + L"\""; HINSTANCE res = ::ShellExecuteW(nullptr, L"open", nxmPath.c_str(), parameters.c_str(), nullptr, SW_SHOWNORMAL); if ((int)res <= 32) { QMessageBox::critical(nullptr, tr("Failed"), -- cgit v1.3.1 From 4bf7b4e10326aee4cbf1f55873e8f8a207042de5 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 1 May 2018 13:18:39 -0500 Subject: Update the NMM version --- src/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/settings.cpp b/src/settings.cpp index 4174c397..21c73dec 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -295,7 +295,7 @@ QString Settings::getOverwriteDirectory(bool resolve) const QString Settings::getNMMVersion() const { - static const QString MIN_NMM_VERSION = "0.61.13"; + static const QString MIN_NMM_VERSION = "0.65.2"; QString result = m_Settings.value("Settings/nmm_version", MIN_NMM_VERSION).toString(); if (VersionInfo(result) < VersionInfo(MIN_NMM_VERSION)) { result = MIN_NMM_VERSION; -- cgit v1.3.1 From 3bb7a44610a770e3bd5525cfe2c129871864cb11 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 1 May 2018 17:02:05 -0500 Subject: Update translation files --- .gitignore | 1 - src/organizer_en.ts | 6557 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 6557 insertions(+), 1 deletion(-) create mode 100644 src/organizer_en.ts (limited to 'src') diff --git a/.gitignore b/.gitignore index 3f197288..ca437a73 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ Thumbs.db src/moc_*.cpp src/ui_*.h -src/organizer_en.ts src/*.qrc.depends src/Modorganizer.* src/*.bak diff --git a/src/organizer_en.ts b/src/organizer_en.ts new file mode 100644 index 00000000..59882025 --- /dev/null +++ b/src/organizer_en.ts @@ -0,0 +1,6557 @@ + + + + + AboutDialog + + + + About + + + + + Revision: + + + + + Used Software + + + + + Thanks + + + + + Current Maintainers + + + + + AL12 + + + + + Translators + + + + + Cyb3r (Dutch) + + + + + fruttyx (French) + + + + + Yoplala (French) + + + + + Faron (German) + + + + + Mordan (Greek) + + + + + Yoosk (Polish) + + + + + Brgodfx (Portuguese) + + + + + Jax (Swedish) + + + + + ...and all other Transifex contributors! + + + + + Other Supporters && Contributors + + + + + Close + + + + + No license + + + + + ActivateModsDialog + + + Activate Mods + + + + + This is a list of esps, esms, and esls that were active when the save game was created. + + + + + <!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; } +</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 is a list of esps, esms, and esls that were active when the save game was created.</span></p> +<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;">For each esp, the right column contains the mod (or mods) that can be enabled to make the missing esps/esms available.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<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;">If you hit Ok, all the mods selected in the right columns and all missing esps that have become available will be activated.</span></p></body></html> + + + + + Missing ESP + + + + + Mod + + + + + not found + + + + + BrowserDialog + + + Some Page + + + + + Search + + + + + new + + + + + failed to start download + + + + + CategoriesDialog + + + Categories + + + + + ID + + + + + Internal ID for the category. + + + + + Internal ID for the category. The categories a mod belongs to are stored by this ID. It is recommended you use new IDs for categories you add instead of re-using existing ones. + + + + + Name + + + + + + Name of the Categorie used for display. + + + + + Nexus IDs + + + + + Comma-Separated list of Nexus IDs to be matched to the internal ID. + + + + + <!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; } +</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;">You can match one or multiple nexus categories to a internal ID. Whenever you download a mod from a Nexus Page, Mod Organizer will try to resolve the category defined on the Nexus to one available in MO.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p> +<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;">To find out a category id used by the nexus, visit the categories list of the nexus page and hover over the links there.</span></p></body></html> + + + + + Parent ID + + + + + If set, the category is defined as a sub-category of another one. Parent ID needs to be a valid category ID. + + + + + Add + + + + + Remove + + + + + CredentialsDialog + + + Login + + + + + This feature may not work unless you're logged in with Nexus + + + + + Username + + + + + Password + + + + + Remember + + + + + Never ask again + + + + + DirectoryRefresher + + + failed to parse bsa %1: %2 + + + + + failed to read mod (%1): %2 + + + + + DownloadList + + + Name + + + + + Filetime + + + + + Done + + + + + Information missing, please select "Query Info" from the context menu to re-retrieve. + + + + + pending download + + + + + DownloadListWidget + + + + Placeholder + + + + + + + Done - Double Click to install + + + + + + Paused - Double Click to resume + + + + + + Installed - Double Click to re-install + + + + + + Uninstalled - Double Click to re-install + + + + + DownloadListWidgetCompact + + + + Placeholder + + + + + Done + + + + + DownloadListWidgetCompactDelegate + + + < game %1 mod %2 file %3 > + + + + + Pending + + + + + Paused + + + + + Fetching Info 1 + + + + + Fetching Info 2 + + + + + Installed + + + + + Uninstalled + + + + + Done + + + + + + + + Are you sure? + + + + + 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 + + + + + Delete + + + + + Un-Hide + + + + + Hide + + + + + Cancel + + + + + Pause + + + + + Remove + + + + + Resume + + + + + Delete Installed... + + + + + Delete All... + + + + + Hide Installed... + + + + + 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. + + + + + 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 + + + + + Delete + + + + + Un-Hide + + + + + Hide + + + + + Cancel + + + + + Pause + + + + + Remove + + + + + Resume + + + + + Delete Installed... + + + + + Delete All... + + + + + Hide Installed... + + + + + Hide All... + + + + + DownloadManager + + + failed to rename "%1" to "%2" + + + + + Memory allocation error (in refreshing directory). + + + + + 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. + + + + + failed to download %1: could not open output file: %2 + + + + + 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 (%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) + + + + + failed to re-open %1 + + + + + EditExecutablesDialog + + + Modify Executables + + + + + List of configured executables + + + + + This is a list of your configured executables. Executables in grey are automatically recognised and can not be modified. + + + + + Title + + + + + + Name of the executable. This is only for display purposes. + + + + + Binary + + + + + + Binary to run + + + + + Browse filesystem + + + + + Browse filesystem for the executable to run. + + + + + + ... + + + + + Start in + + + + + Arguments + + + + + + Arguments to pass to the application + + + + + Allow the Steam AppID to be used for this executable to be changed. + + + + + Allow the Steam AppID to be used for this executable to be changed. +Every game/tool distributed through Steam has a unique ID. MO needs to know this ID to start those programs directly, otherwise the program is started by steam and then MO will not work. By default, MO will use the AppID for the game. +Right now the only case I know of where this needs to be overwritten is for the Skyrim Creation Kit which has its own AppID. This overwrite is already preconfigured. + + + + + Overwrite Steam AppID + + + + + Steam AppID to use for this executable that differs from the games AppID. + + + + + Steam AppID to use for this executable that differs from the games AppID. +Every game/tool distributed through Steam has a unique ID. MO needs to know this ID to start those programs directly, otherwise the program is started by steam and then MO will not work. By default, MO will use the AppID for the game (usually 72850). +Right now the only case I know of where this needs to be overwritten is for the Skyrim Creation Kit which has its own AppID (usually 202480). This overwrite is already preconfigured. + + + + + If this is enabled, new files are created in the specified mod instead of the "Overwrite" mod. + + + + + Create Files in Mod instead of Overwrite (*) + + + + + Use Application's Icon for shortcuts + + + + + (*) This setting is profile-specific + + + + + + Add an executable + + + + + + Add + + + + + + Remove the selected executable + + + + + Remove + + + + + Close + + + + + Select a binary + + + + + Executable (%1) + + + + + Java (32-bit) required + + + + + MO requires 32-bit java to run this application. If you already have it installed, select javaw.exe from that installation as the binary. + + + + + Select a directory + + + + + Confirm + + + + + Really remove "%1" from executables? + + + + + Modify + + + + + + Save Changes? + + + + + + You made changes to the current executable, do you want to save them? + + + + + FindDialog + + + Find + + + + + Find what: + + + + + + Search term + + + + + + Find next occurence from current file position. + + + + + &Find Next + + + + + + + Close + + + + + InstallDialog + + + Install Mods + + + + + New Mod + + + + + Name + + + + + Pick a name for the mod + + + + + Pick a name for the mod. This is also used as a directory name, so please don't use characters that are illegal in file names. + + + + + Content + + + + + Content of the archive. You can change the directory structure by using drag&drop. Hint: Also try right clicking... + + + + + <!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; } +</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 displays the content of the archive. &lt;data&gt; represents the base directory which will map to the game's data directory. You can change the base directory via the right-click context menu and you can move around files via drag&amp;drop</span></p></body></html> + + + + + Placeholder + + + + + OK + + + + + Cancel + + + + + InstallationManager + + + Password required + + + + + Password + + + + + + Extracting files + + + + + failed to create backup + + + + + Mod Name + + + + + Name + + + + + Invalid name + + + + + The name you entered is invalid, please enter a different one. + + + + + File format "%1" not supported + + + + + None of the available installer plugins were able to handle that archive + + + + + no error + + + + + 7z.dll not found + + + + + 7z.dll isn't valid + + + + + archive not found + + + + + failed to open archive + + + + + unsupported archive type + + + + + internal library error + + + + + archive invalid + + + + + unknown archive error + + + + + LockedDialog + + + Running virtualized processes + + + + + This dialog should disappear automatically if the application/game is done. Click unlock if it didn't. + + + + + MO is locked while the executable is running. + + + + + Unlock + + + + + LogBuffer + + + failed to write log to %1: %2 + + + + + MOApplication + + + an error occured: %1 + + + + + an error occured + + + + + MainWindow + + + + Categories + + + + + Clear + + + + + If checked, only mods that match all selected categories are displayed. + + + + + And + + + + + If checked, all mods that match at least one of the selected categories are displayed. + + + + + Or + + + + + Profile + + + + + Pick a module collection + + + + + <!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; } +</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;">Create profiles here. Each profile contains its own list of active mods and esps. This way you can quickly switch between setups for different play throughs.</span></p> +<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;">Please note that right now your esp load order is not kept seperate for different profiles.</span></p></body></html> + + + + + Open list options... + + + + + Refresh list. This is usually not necessary unless you modified data outside the program. + + + + + Show Open Folders menu... + + + + + + 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; } +</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;">Choose the program to run. Once you start using ModOrganizer, you should always run your game and tools from here or through shortcuts created here, otherwise mods installed through MO will not be visible.</span></p> +<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;">You can add new Tools to this list, but I can't promise tools I haven't tested will work.</span></p></body></html> + + + + + 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; } +</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;">Run the selected program with ModOrganizer enabled.</span></p></body></html> + + + + + 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; } +</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 creates a start menu shortcut that directly starts the selected program with the MO active.</span></p></body></html> + + + + + 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; } +</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> + + + + + 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! + + BSAs checked here are loaded in such a way that your installation order is obeyed properly. + + + + + + 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; } +</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 is a list of all savegames for this game. Hover over a list entry to get detailed information about the save including a list of esps/esms that were used at the time this save was created but aren't active now.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p> +<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;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html> + + + + + 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! +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 + + + + + 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 + + + + + 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 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 + + + + + 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. + + + + + + + + Enable Mods... + + + + + Delete %n save(s) + + + + + + + + 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 + + + + + MessageDialog + + + + Placeholder + + + + + ModInfo + + + Plugins + + + + + Textures + + + + + Meshes + + + + + Bethesda Archive + + + + + UI Changes + + + + + Sound Effects + + + + + Scripts + + + + + Script Extender + + + + + SkyProc Tools + + + + + MCM Data + + + + + invalid content type %1 + + + + + invalid mod index %1 + + + + + remove: invalid mod index %1 + + + + + ModInfoBackup + + + This is the backup of a mod + + + + + ModInfoDialog + + + Mod Info + + + + + Textfiles + + + + + A list of text-files in the mod directory. + + + + + A list of text-files in the mod directory like readmes. + + + + + + Save + + + + + INI-Files + + + + + Ini Files + + + + + This is a list of .ini files in the mod. + + + + + This is a list of .ini files in the mod. These are usually used to configure the behaviour of mods if there are configurable parameters. + + + + + Ini Tweaks *This feature is non-functional* + + + + + This is a list of ini tweaks (ini modifications that can be toggled). + + + + + This is a list of ini tweaks. Ini Tweaks are (usually small) fragments of ini files that are applied over existing settings in skyrim.ini/skyrimprefs.ini. Each tweak can be toggled individually. You should check the description of the mod wether the tweaks are really optional. + + + + + Save changes to the file. + + + + + Save changes to the file. This overwrites the original. There is no automatic backup! + + + + + Images + + + + + Images located in the mod. + + + + + This lists all the images (.jpg and .png) in the mod directory, like screenshots and such. Click one to get a larger view. + + + + + + Optional ESPs + + + + + List of esps, esms, and esls that can not be loaded by the game. + + + + + List of esps, esms, and esls contained in this plugin that currently can not be loaded by the game. They will not even appear in the esp-list in the main MO-window. +They usually contain optional functionality, see the readme. + +Most mods do not have optional esps, so chances are good you are looking at an empty list. + + + + + Make the selected mod in the lower list unavailable. + + + + + The selected esp (in the lower list) will be pushed into a subdirectory of the mod and will thus become "invisible" to the game. It can then no longer be activated. + + + + + Move a file to the data directory. + + + + + This moves a esp to the esp directory so it can be enabled in the main window. Please note that the ESP merely becomes "available", it will not necessarily be loaded! That is configured in the main window of omo. + + + + + ESPs in the data directory and thus visible to the game. + + + + + These are the mod files that are in the (virtual) data directory of your game and will thus be selecteable in the esp list in the main window. + + + + + Available ESPs + + + + + Conflicts + + + + + The following conflicted files are provided by this mod + + + + + + File + + + + + Overwritten Mods + + + + + The following conflicted files are provided by other mods + + + + + Providing Mod + + + + + Non-Conflicted files + + + + + Categories + + + + + Primary Category + + + + + Nexus Info + + + + + Mod ID + + + + + Mod ID for this mod on Nexus. + + + + + <!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; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; 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;">Mod ID for this mod on Nexus. This is filled in automatically if you downloaded and installed the mod from inside MO. Otherwise you can enter it manually. To find the correct id, find the mod on nexus. The URL will look like this: <a href=" https://www.nexusmods.com/skyrimspecialedition/mods/6194"><span style=" text-decoration: underline; color:#0000ff;">https://www.nexusmods.com/skyrimspecialedition/mods/6194</span></a>. In this example, 6194 is the id you're looking for. Besides: The above is the link to Mod Organizer2 on Nexus. Why not go there now and endorse us?</p></body></html> + + + + + Source Game + + + + + Source game for this mod. + + + + + <html><head/><body><p>Source game for this mod. This determines where the mod was downloaded from and decides where to fetch info, version updates, and send endorsements. Changing this will likely require you to enter a new Mod ID.</p></body></html> + + + + + <!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; } +</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;">Installed Version of the Mod. The tooltip will contain the current version available on nexus. The installed version is only set if you installed the mod through MO.</span></p></body></html> + + + + + Version + + + + + Refresh + + + + + Refresh all information from Nexus. + + + + + Description + + + + + about:blank + + + + + Endorse + + + + + Notes + + + + + Filetree + + + + + Open Mod in Explorer + + + + + A directory view of this mod + + + + + <!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; } +</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 is a modifiable directory view of the mod directory. You can move around files using drag &amp; drop and rename them (double click).</span></p> +<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;">Changes happen immediately on disc, so do</span><span style=" font-size:8pt; font-weight:600;"> be careful</span><span style=" font-size:8pt;">.</span></p></body></html> + + + + + Previous + + + + + Next + + + + + Close + + + + + &Delete + + + + + &Rename + + + + + &Hide + + + + + &Unhide + + + + + &Open + + + + + &New Folder + + + + + + Save changes? + + + + + + Save changes to "%1"? + + + + + File Exists + + + + + A file with that name exists, please enter a new one + + + + + failed to move file + + + + + failed to create directory "optional" + + + + + + Info requested, please wait + + + + + Main + + + + + Update + + + + + Optional + + + + + Old + + + + + Misc + + + + + Unknown + + + + + Current Version: %1 + + + + + No update available + + + + + (description incomplete, please visit nexus) + + + + + <a href="%1">Visit on Nexus</a> + + + + + Failed to delete %1 + + + + + + + + Confirm + + + + + + Are sure you want to delete "%1"? + + + + + + Are sure you want to delete the selected files? + + + + + + New Folder + + + + + Failed to create "%1" + + + + + + 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? + + + + + + failed to rename %1 to %2 + + + + + There already is a visible version of this file. Replace it? + + + + + Select binary + + + + + Binary + + + + + file not found: %1 + + + + + failed to generate preview for %1 + + + + + Sorry + + + + + Sorry, can't preview anything. This function currently does not support extracting from bsas. + + + + + Un-Hide + + + + + Hide + + + + + + Open/Execute + + + + + + Preview + + + + + Name + + + + + Please enter a name + + + + + + Error + + + + + Invalid name. Must be a valid file name + + + + + A tweak by that name exists + + + + + Create Tweak + + + + + ModInfoForeign + + + This pseudo mod represents content managed outside MO. It isn't modified by MO. + + + + + DLC: + + + + + Creation Club: + + + + + Unmanaged: + + + + + ModInfoOverwrite + + + This pseudo mod contains files from the virtual data tree that got modified (i.e. by the construction kit) + + + + + ModInfoRegular + + + + failed to write %1/meta.ini: error %2 + + + + + %1 contains no esp/esm/esl and no asset (textures, meshes, interface, ...) directory + + + + + Categories: <br> + + + + + ModList + + + Game Plugins (ESP/ESM/ESL) + + + + + Interface + + + + + Meshes + + + + + Bethesda Archive + + + + + Scripts (Papyrus) + + + + + Script Extender Plugin + + + + + SkyProc Patcher + + + + + Sound or Music + + + + + Textures + + + + + MCM Configuration + + + + + 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> + + + + + Time this mod was installed + + + + + ModListSortProxy + + + Drag&Drop is only supported when sorting by priority + + + + + MotDDialog + + + Message of the Day + + + + + OK + + + + + MyFileSystemModel + + + Overwrites + + + + + not implemented + + + + + NXMAccessManager + + + Verifying Nexus login + + + + + Logging into Nexus + + + + + timeout + + + + + Unknown error + + + + + Please check your password + + + + + NexusInterface + + + Failed to guess mod id for "%1", please pick the correct one + + + + + empty response + + + + + invalid response + + + + + 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 + + + + + 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. + + + + + OverwriteInfoDialog + + + Overwrite + + + + + You can use drag&drop to move files and directories to regular mods. + + + + + &Delete + + + + + &Rename + + + + + &Open + + + + + &New Folder + + + + + %1 not found + + + + + Failed to delete "%1" + + + + + + + + Confirm + + + + + + Are sure you want to delete "%1"? + + + + + + Are sure you want to delete the selected files? + + + + + + New Folder + + + + + Failed to create "%1" + + + + + PluginContainer + + + Some plugins could not be loaded + + + + + + Description missing + + + + + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: + + + + + 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. + + + + + 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 + + + + + PluginListSortProxy + + + Drag&Drop is only supported when sorting by priority or mod index + + + + + PreviewDialog + + + Preview + + + + + Close + + + + + ProblemsDialog + + + Problems + + + + + <!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; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:7.8pt;"><br /></p></body></html> + + + + + Close + + + + + + Fix + + + + + No guided fix + + + + + Profile + + + invalid profile name %1 + + + + + failed to create %1 + + + + + failed to write mod list: %1 + + + + + failed to update tweaked ini file, wrong settings may be used: %1 + + + + + failed to create tweaked ini: %1 + + + + + failed to open %1 + + + + + "%1" is missing or inaccessible + + + + + + + + + invalid index %1 + + + + + Overwrite directory couldn't be parsed + + + + + invalid priority %1 + + + + + Delete savegames? + + + + + Do you want to delete local savegames? (If you select "No", the save games will show up again if you re-enable local savegames) + + + + + ProfileInputDialog + + + Dialog + + + + + Please enter a name for the new profile + + + + + If checked, the new profile will use the default game settings. + + + + + If checked, the new profile will use the default game settings instead of the "global" settings. Global settings are the settings you configure when running the game launcher directly, without MO. + + + + + Default Game Settings + + + + + ProfilesDialog + + + Profiles + + + + + List of Profiles + + + + + <!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; } +</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;">This is the list of profiles. Each Profile contains its own list and installation order of enabled mods (from a shared pool), a configuration of enabled esps/esms, a copy of the games ini-file and an optional savegame filter.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Note</span> For technical reasons it's currently not possible to have seperate load-orders for esps. This means you can't load moda.esp before modb.esp in one profile and the other way around in another.</p></body></html> + + + + + + If checked, savegames are local to this profile and will not appear when starting with a different profile. + + + + + Local Savegames + + + + + Local Game Settings + + + + + This ensures data files from mods are actually used. You want to enable this unless you use a different tool for Archive Invalidation. + + + + + <!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; } +</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;">The games Oblivion, Fallout 3 and Fallout NV contain a bug which prevents texture and mesh replacers (that is: all modifications to meshes and textures already in game) from working.</span></p> +<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;">The Mod Organizer uses a workaround called &quot;BSA redirection&quot; (google is your friend) to fix this issue reliably and without further work. Simply activate and forget.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p> +<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;">With Skyrim this bug seems to be fixed to a degree but whether a mod becomes active still depends on file dates. Therefore, it still makes sense to activate this.</span></p></body></html> + + + + + Automatic Archive Invalidation + + + + + + Create a new profile from scratch + + + + + Create + + + + + Clone the selected profile + + + + + This creates a new profile with the same settings and active mods as the selected one. + + + + + Copy + + + + + + Delete the selected Profile. This can not be un-done! + + + + + Remove + + + + + Rename + + + + + + Transfer save games to the selected profile. + + + + + Transfer Saves + + + + + Close + + + + + Archive invalidation isn't required for this game. + + + + + + failed to create profile: %1 + + + + + Name + + + + + Please enter a name for the new profile + + + + + failed to copy profile: %1 + + + + + Invalid name + + + + + Invalid profile name + + + + + Confirm + + + + + Are you sure you want to remove this profile (including local savegames if any)? + + + + + Profile broken + + + + + This profile you're about to delete seems to be broken or the path is invalid. I'm about to delete the following folder: "%1". Proceed? + + + + + Rename Profile + + + + + New Name + + + + + failed to change archive invalidation state: %1 + + + + + failed to determine if invalidation is active: %1 + + + + + QObject + + + Failed to save custom categories + + + + + + + + invalid index %1 + + + + + invalid category id %1 + + + + + invalid field name "%1" + + + + + invalid type for "%1" (should be integer) + + + + + invalid type for "%1" (should be string) + + + + + invalid type for "%1" (should be float) + + + + + no fields set up yet! + + + + + field not set "%1" + + + + + invalid character in field "%1" + + + + + empty field name + + + + + invalid game type %1 + + + + + helper failed + + + + + failed to determine account name + + + + + + invalid 7-zip32.dll: %1 + + + + + archive.dll not loaded: "%1" + + + + + Deleting folder + + + + + I'm about to delete the following folder: "%1". Proceed? + + + + + Choose Instance to Delete + + + + + Be Carefull! Deleting an Instance will remove all your files for that Instance (mods, downloads, profiles, configuration, ...). Custom paths outside of the instance folder for downloads, mods, etc. will be left untoched. + + + + + Are you sure? + + + + + Are you really sure you want to delete the Instance "%1" with all its files? + + + + + Failed to delete Instance + + + + + Could not delete Instance "%1". +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): + + + + + + Canceled + + + + + 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. + + + + + failed to open %1: %2 + + + + + + %1 not found + + + + + + game doesn't support a script extender + + + + + Failed to delete %1 + + + + + Failed to deactivate script extender loading + + + + + Failed to remove %1: %2 + + + + + + Failed to rename %1 to %2 + + + + + Failed to deactivate proxy-dll loading + + + + + + + Failed to copy %1 to %2 + + + + + Failed to set up script extender loading + + + + + Failed to delete old proxy-dll %1 + + + + + Failed to overwrite %1 + + + + + Failed to set up proxy-dll loading + + + + + + Error + + + + + + Failed to create "%1". Your user account probably lacks permission. + + + + + Plugin to handle %1 no longer installed + + + + + Could not use configuration settings for game "%1", path "%2". + + + + + + Please select the game to manage + + + + + No game identified in "%1". The directory is required to contain the game binary and its launcher. + + + + + 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 + + + + + Please use "Help" from the toolbar to get usage instructions to all elements + + + + + + <Manage...> + + + + + failed to parse profile %1: %2 + + + + + Failed to start "%1" + + + + + Waiting + + + + + Please press OK once you're logged into steam. + + + + + failed to initialize plugin %1: %2 + + + + + Plugin error + + + + + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? +(Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) + + + + + failed to access %1 + + + + + failed to set file time %1 + + + + + Before you can use ModOrganizer, you need to create at least one profile. ATTENTION: Run the game at least once before creating a profile! + + + + + Script Extender + + + + + Proxy DLL + + + + + failed to spawn "%1" + + + + + Elevation required + + + + + This process requires elevation to run. +This is a potential security risk so I highly advice you to investigate if +"%1" +can be installed to work without elevation. + +Start elevated anyway? (you will be asked if you want to allow ModOrganizer.exe to make changes to the system) + + + + + failed to spawn "%1": %2 + + + + + QueryOverwriteDialog + + + Mod Exists + + + + + This mod seems to be installed already. Do you want to add files from this archive (overwriting existing ones) or do you want to completely replace the existing files (old files are deleted)? Alternatively you can install this mod under a different name. + + + + + Keep Backup + + + + + Merge + + + + + Replace + + + + + Rename + + + + + Cancel + + + + + SaveTextAsDialog + + + Dialog + + + + + Copy To Clipboard + + + + + Save As... + + + + + Close + + + + + Save CSV + + + + + Text Files + + + + + failed to open "%1" for writing + + + + + SelectionDialog + + + Select + + + + + Placeholder + + + + + Cancel + + + + + SelfUpdater + + + archive.dll not loaded: "%1" + + + + + 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 + + + + + Settings + + + Failed + + + + + Sorry, failed to start the helper application + + + + + + attempt to store setting for unknown plugin "%1" + + + + + Error + + + + + Failed to create "%1", you may not have the necessary permission. path remains unchanged. + + + + + SettingsDialog + + + Settings + + + + + General + + + + + Language + + + + + The display language + + + + + <!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; } +</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;">The display language. This will only displaye languages for which you have a translation installed.</span></p></body></html> + + + + + Style + + + + + graphical style + + + + + graphical style of the MO user interface + + + + + Update to non-stable releases. + + + + + If this is enabled, the integrated update mechanism will notify of all releases, including pre-releases (alphas, betas). + +Please use this only if you're sufficiently tech-savvy to investigate issues, look for known problems in the issue tracker and create meaningful reports. + +If you use pre-releases, never contact me directly by e-mail or via private messages! + + + + + Install Pre-releases (Betas) + + + + + User interface + + + + + If checked, the download interface will be more compact. + + + + + Compact Download Interface + + + + + If checked, the download list will display meta information instead of file names. + + + + + Download Meta Information + + + + + Reset stored information from dialogs. + + + + + This will make all dialogs show up again where you checked the "Remember selection"-box. + + + + + Reset Dialogs + + + + + + Modify the categories available to arrange your mods. + + + + + Configure Mod Categories + + + + + Paths + + + + + 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 + + + + + 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; } +</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;">Allows automatic log-in when the Nexus-Page for the game is clicked. Please note that the obfuscation with which the password is stored in modorganizer.ini is not very strong. If you're worried someone might steal your password, don't store it here.</span></p></body></html> + + + + + 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; } +</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;">The Steam App ID is required to directly start some games. For Skyrim, if this is not set or wrong, the &quot;Mod Organizer&quot; load mechanism may not work properly.</span></p> +<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;">The preset for this is the App ID of the &quot;regular&quot; version so in most cases, you should be set.</span></p> +<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;">If you think you have a different version (GotY or something), follow these steps to get to the id:</span></p> +<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;">1. Navigate to the game library in steam</span></p> +<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;">2. right-click on the game you need the id for and choose </span><span style=" font-size:8pt; font-weight:600;">Create desktop shortcut</span></p> +<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;">3. right-click on the newly created shortcut on your desktop and select </span><span style=" font-size:8pt; font-weight:600;">Properties</span></p> +<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;">4. in the URL-field you should see something like this: </span><span style=" font-size:8pt; font-style:italic;">steam://rungameid/22380</span></p> +<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;">22380 is the id you're looking for.</span></p></body></html> + + + + + 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. +*Script Extender* In this mode, MO is installed as a Script Extender (obse, fose, nvse, skse) plugin. +*Proxy DLL* In this mode, MO replaces one of the game's dlls with one that loads MO (and the original dll of course). This will ONLY work with Steam games and it has only been tested with Skyrim. Please use this only if the other mechanisms don't work. + +If you use the Steam version of Oblivion the default will NOT work. In this case, please install obse and use "Script Extender" as the load mechanism. Also you can then not start Oblivion from MO. Instead, use MO only to set up your mods, then quit and start Oblivion through Steam. + + + + + 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. + +tl;dr-version: If Nexus-features don't work, insert the current version number of NMM here and try again. + + + + + 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. + +If you disable this feature, MO will only display official DLCs this way. Please note that plugins (esps, esms, and esls) displayed in the right pane are completely unaffected by this feature. + + + + + 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. + + + + + + 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. + "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 + + + + + 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. + + + + + + + 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. + + + + + + Hint: right click link and copy link location + + + + + Confirm + + + + + Changing the mod directory affects all your profiles! Mods not present (or named differently) in the new location will be disabled in all profiles. There is no way to undo this unless you backed up your profiles manually. Proceed? + + + + + Select base directory + + + + + Select download directory + + + + + Select mod directory + + + + + Select cache directory + + + + + Select profiles directory + + + + + Select overwrite directory + + + + + Confirm? + + + + + This will make all dialogs show up again where you checked the "Remember selection"-box. Continue? + + + + + SimpleInstallDialog + + + Quick Install + + + + + Name + + + + + + Opens a Dialog that allows custom modifications. + + + + + Manual + + + + + OK + + + + + Cancel + + + + + SingleInstance + + + SHM error: %1 + + + + + failed to connect to running instance: %1 + + + + + failed to communicate with running instance: %1 + + + + + failed to receive data from secondary instance: %1 + + + + + SyncOverwriteDialog + + + Sync Overwrite + + + + + Name + + + + + Sync To + + + + + <don't sync> + + + + + failed to remove %1 + + + + + failed to move %1 to %2 + + + + + TransferSavesDialog + + + Transfer Savegames + + + + + Global Characters + + + + + This is a list of characters in the global location. + + + + + This is a list of characters in the global location. + +On Windows Vista/Windows 7: + C:\Users\[UserName]\Documents\My Games\Skyrim\Saves + +On Windows XP: + C:\Documents and Settings\[UserName]\My Documents\My Games\Skyrim\Saves + + + + + + This is a list of save games for the selected character in the global location. + +On Windows Vista/Windows 7: + C:\Users\[UserName]\Documents\My Games\Skyrim\Saves + +On Windows XP: + C:\Documents and Settings[UserName]\My Documents\My Games\Skyrim\Saves + + + + + + + Move -> + + + + + Copy -> + + + + + <- Move + + + + + <- Copy + + + + + Done + + + + + Profile Characters + + + + + Characters for profile %1 + + + + + Overwrite + + + + + Overwrite the file "%1" + + + + + Confirm + + + + + UsvfsConnector + + + Preparing vfs + + + + + WaitingOnCloseDialog + + + Waiting for virtualized processes + + + + + This dialog should disappear automatically if the application/game is done. + + + + + Virtualized processes are still running, it is prefered to keep MO running until they are finished. + + + + + Close Now + + + + + Cancel + + + + + tutorial_conflictresolution_main + + + There are multiple types of conflicts you may encounter when dealing with Mods. This tutorial will try to cover and explain how to deal with all of them. + + + + + First up are file conflicts. These occur when two mods contain the same file. Most commonly this happens when several mods replace the same standard asset from the game, like the texture of an armor. + + + + + As an example, say you install "ModA" which contains stylish new iron and leather armor. Then you install "ModB" which contains sexy ebony and leather armor. Obviously there is a conflict now: which leather armor to use? + + + + + If you were to install the mods manually, when installing "ModB" you would be asked if you want to overwrite conflicting files. If you choose yes, you get the leather armor from "ModB" otherwise you keep the one from "ModA". If you later decide you made the wrong choice, you have to reinstall one of the mods. + + + + + With MO, both ModA and ModB get installed completely (no overwrite dialog) and by default "ModB" gets to provide the leather armor because it's automatically assigned the higher priority. + + + + + However, you can change the mod priority at any time using drag&drop on this list. If you assign "ModA" a higher priority, it provides the leather armor, no re-installation required. Since the priorities of mods in this list are treated as if the mods were installed in that order, I tend to talk about "installation order". + + + + + If the "Flags"-column is enabled in the mod list, it will show you which mods are involved in a conflict and how... + + + + + <img src="qrc:///MO/gui/emblem_conflict_overwrite" /> indicates that the mod overwrites files that are also available in another mod. + + + + + <img src="qrc:///MO/gui/emblem_conflict_overwritten" /> indicates that the mod is <b>partially</b> overwritten by another. + + + + + <img src="qrc:///MO/gui/emblem_conflict_mixed" /> indicates that both of the above is true. + + + + + <img src="qrc:///MO/gui/emblem_conflict_redundant" /> indicates that the mod is completely overwrtten by another. You could as well disable it. + + + + + There are two ways to see the individual files involved in a conflict: + + + + + Option A: Switch to the "Data"-tab if necessary + + + + + ... here, if you mark the highlighted control, the tree will only display files in conflict. In the right column, it says which mod currently provides the mod (because it has highest priority) and if you hover your mouse over that info, it will list which other mods contains it. + + + + + Option B: Open the <i>Information</i>-Dialog of an <b>enabled</b> mod you're interested in by either double-clicking it or selecting <i>Information...</i> from the right-click menu + + + + + This was everything to know about file conflicts. The second type of conflict we have to deal with are "record conflicts". + + + + + I told you in the "First Steps" tutorial how the esp/esm/esl plugins contain changes to the game world like modifications to the terrain or existing NPCs. Each change like this is stored in a record, hence the name "record conflict". For example when two mods try to change the same location, only one change can become active. + + + + + As with file conflicts you can't really fix these conflicts, you have to choose which change you want. This time around however, if you choose wrong, your game may become unstable because there may be dependencies between the records of a mod. + + + + + Please open the "Plugins"-tab... + + + + + Again you can use drag&drop to change priorities of plugins, thus deciding which plugin takes precedence in regards to conflicts. This is commonly called the "load order". But how do you know how to order the plugins? + + + + + Unlike with file conflicts, MO does not provide help on finding conflicts. The good news is, there already is a perfect tool for that called LOOT. LOOT is available on the Nexus and integrates neatly with MO. Basically, if you don't have LOOT yet, install it once this tutorial is over. + + + + + After you installed LOOT in the default location (follow its instructions), start MO again and LOOT should automatically appear as an Executable... + + + + + When you run LOOT, it will automatically re-organize plugins for best compatibility (overwriting your manual changes). It will also open a report in your browser that warns about incompatibilities. You should read the report, at least for new mods. + + + + + The final type of conflicts are also "record conflicts". Like the previous type. It's confusing, so I'll just call them "lists conflicts" instead. The difference is the types of records in conflict. The ones in question here can be merged so you may be able to get all modifications in your game. + + + + + One common example of such records are leveled lists that contain all the items that may spawn at a specific character level. Traditionally, if multiple mods add items to such a list, only one is in effect... + + + + + ... but there are tools to merge those mods so you can have the effects of all of them. Again, this functionality is not integrated with MO because there are already great tools. For Oblivion and Skyrim look for wrye bash, for fallout 3/nv it's wrye flash. For Skyrim there is also "SkyBash". All of these can create a so-called "bashed patch" which is a plugin that contains the combined mergeable records from all your mods. + + + + + This completes the tutorial. + + + + + tutorial_conflictresolution_modinfo + + + Please switch to the "Conflicts"-Tab. + + + + + Here you can see a list of files in this mod that out-prioritize others in a file conflict and one with files where this mod is overridden. + + + + + Please close the information dialog again. + + + + + tutorial_firststeps_browser + + + This is a fully featured browser that is set up to open the correct nexus page for your game. You can download any mod using the "DOWNLOAD WITH MANAGER"-button or the "manual"-link and it will be downloaded by MO. + + + + + tutorial_firststeps_main + + + Welcome to the ModOrganizer Tutorial! This will guide you through the most common features of MO. +Please go along with the tutorial because some things can't be demonstrated if you skip something. + + + + + Before we continue with the step-by-step tutorial, I'd like to tell you about the other ways you can receive help on ModOrganizer. + + + + + The highlighted button provides hints on solving problems MO recognized automatically. + + + + + +There IS a problem now but you may want to hold off on fixing it until after completing the tutorial. + + + + + This button provides multiple sources of information and further tutorials. + + + + + Finally there are tooltips on almost every part of Mod Organizer. If there is a control in MO you don't understand, please try hovering over it to get a short description or use "Help on UI" from the help menu to get a longer explanation + + + + + This list displays all mods installed through MO. It also displays installed DLCs and some mods installed outside MO but you have limited control over those in MO. + + + + + Before we start installing mods, let's have a quick look at the settings. + + + + + Now it's time to install a few mods!Please go along with this because we need a few mods installed to demonstrate other features + + + + + There are a few ways to get mods into ModOrganizer. If you associated MO with NXM links in the settings you can now use your regular browser to send downloads from Nexus to MO. Click on "Nexus" to open nexus, find a mod and click the green download buttons on Nexus saying "Download with Manager". + + + + + You can also install mods from disk using the "Install Mod" button. + + + + + Downloads will appear on the "Downloads"-tab here. You have to download and install at least one mod to proceed. + + + + + Great, you just installed your first mod. Please note that the installation procedure may differ based on how a mod was packaged. + + + + + Now you know all about downloading and installing mods but they are not enabled yet... + + + + + Install a few more mods if you want, then enable mods by checking them in the left pane. Mods that aren't enabled have no effect on the game whatsoever. + + + + + For some mods, enabling it on the left pane is all you have to do... + + + + + ...but most contain plugins. These are plugins for the game and are required to add stuff to the game (new weapons, armors, quests, areas, ...). Please open the "Plugins"-tab to get a list of plugins. + + + + + You will notice some plugins are grayed out. These are part of the main game and can't be disabled. + + + + + A single mod may contain zero, one or multiple esps. Some or all may be optional. If in doubt, please consult the documentation of the indiviual mod. To do so, right-click the mod and select "Information". + + + + + Now you know how to download, install and enable mods. +It's important you always start the game from inside MO, otherwise the mods you installed here won't work. + + + + + This combobox lets you choose <i>what</i> to start. This way you can start the game, Launcher, Script Extender, the Creation Kit or other tools. If you miss a tool you can also configure this list but that is an advanced topic. + + + + + This completes the basic tutorial. As homework go play a bit! After you have installed more mods you may want to read the tutorial on conflict resolution. + + + + + tutorial_firststeps_modinfo + + + This dialog tries to expose as much information about a mod as possible. Depending on the mod this may include readmes, screenshots, optional plugins and so on. If a certain type of information was not found in a mod, the corresponding tab is grayed out. + + + + + If you installed the mod from Nexus, the corresponding tab should give you direct access to the mod page. + + + + + We may re-visit this screen in later tutorials. + + + + + tutorial_firststeps_settings + + + You can use your regular browser to download from Nexus. +Please open the "Nexus"-tab + + + + + Click this button so that "DOWNLOAD WITH MANAGER"-buttons are download with Mod Organizer. + + + + + You can also store your Nexus-credentials here for automatic login. The password is stored unencrypted on your disk! + + + + + tutorial_primer_main + + + This window shows all the mods that are installed. The column headers can be used for sorting. Only checked mods are active in the current profile. + + + + + Each profile is a separate set of enabled mods and ini settings. + + + + + The dropdown allows various ways of grouping the mods shown in the mod list. + + + + + Show/hide the category pane. + + + + + Quickly filter the mod list as you type. + + + + + Switch between information views. + + + + + This shows mod categories and some meta categories (in angle-brackets). Select some to filter the mod list. For example select "<Checked>" to show only active mods. + + + + + Customizable list for choosing the program to run. + + + + + When this button is clicked, Mod Organizer creates a virtual directory structure then runs the program selected to the left. + + + + + Will create a shortcut for quick access. The shortcut can be placed in the toolbar at the top, in the Start Menu or on the Windows Desktop. + + + + + Log messages produced by MO. Please note that messages with a light bulb usually don't require your attention. + + + + + Configure Mod Organizer. + + + + + Reports potential Problems about the current setup. + + + + + Activates if there is an update for MO. Please note that if, for any reason, MO can't communicate with NMM, this will not work either. + + + + + Plugins (esp/esm/esl files) of the mods in the current profile. They need to be checked to be loaded. + + + + + Automatically sort plugins using the bundled LOOT application. + + + + + Quickly filter plugin list as you type. + + + + + The directory tree and all files that the program will see. + + + + + Save game browser. Shows all the saves for the current profile and whether or not the current mod-load status is correct. + + + + + Shows the mods that have been downloaded and if they’ve been installed. + + + + + Click to quit + + + + + tutorial_window_installer + + + This mod has been packaged in a way that Mod Organizer did not automatically recognize... + + + + + You can use drag&drop on this list to fix the structure of the mod. + + + + + The correct structure replicates the data-directory of the game. That means esps, the "meshes"- or "textures"-directory and so on should be directly below "<data>". + + + + + You can also disable files and directories that you don't want to unpack. + + + + + From the context menu (right-click) you can open textfiles, in case you want to access a readme. + + + + + This text will turn green if MO thinks the structure looks good. + + + + -- cgit v1.3.1 From d62ec9c867b448276ab9873ddd3f320487ab5572 Mon Sep 17 00:00:00 2001 From: Silarn Date: Tue, 1 May 2018 21:50:20 -0500 Subject: Allow downloads to display speed and complete % --- src/downloadlistwidget.cpp | 4 +++- src/downloadlistwidgetcompact.cpp | 3 ++- src/downloadmanager.cpp | 30 +++++++++++++++++++++++------- src/downloadmanager.h | 4 ++-- 4 files changed, 30 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/downloadlistwidget.cpp b/src/downloadlistwidget.cpp index cb7a49c3..2af74cc2 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistwidget.cpp @@ -53,6 +53,7 @@ DownloadListWidgetDelegate::DownloadListWidgetDelegate(DownloadManager *manager, m_InstallLabel = m_ItemWidget->findChild("installLabel"); m_InstallLabel->setVisible(false); + m_Progress->setTextVisible(true); connect(manager, SIGNAL(stateChanged(int,DownloadManager::DownloadState)), this, SLOT(stateChanged(int,DownloadManager::DownloadState))); connect(manager, SIGNAL(downloadRemoved(int)), this, SLOT(resetCache(int))); @@ -159,7 +160,8 @@ void DownloadListWidgetDelegate::paintRegularDownload(int downloadIndex) const } else { m_InstallLabel->setVisible(false); m_Progress->setVisible(true); - m_Progress->setValue(m_Manager->getProgress(downloadIndex)); + m_Progress->setValue(m_Manager->getProgress(downloadIndex).first); + m_Progress->setFormat(m_Manager->getProgress(downloadIndex).second); } } diff --git a/src/downloadlistwidgetcompact.cpp b/src/downloadlistwidgetcompact.cpp index b390bff4..898d400a 100644 --- a/src/downloadlistwidgetcompact.cpp +++ b/src/downloadlistwidgetcompact.cpp @@ -138,7 +138,8 @@ void DownloadListWidgetCompactDelegate::paintRegularDownload(int downloadIndex) } else { m_DoneLabel->setVisible(false); m_Progress->setVisible(true); - m_Progress->setValue(m_Manager->getProgress(downloadIndex)); + m_Progress->setValue(m_Manager->getProgress(downloadIndex).first); + m_Progress->setFormat(m_Manager->getProgress(downloadIndex).second); } } diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 77a4cf98..097a1168 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -62,7 +62,7 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const Mo info->m_DownloadID = s_NextDownloadID++; info->m_StartTime.start(); info->m_PreResumeSize = 0LL; - info->m_Progress = 0; + info->m_Progress = std::make_pair(0, "0 bytes/sec"); info->m_ResumePos = 0; info->m_FileInfo = new ModRepositoryFileInfo(*fileInfo); info->m_Urls = URLs; @@ -852,7 +852,7 @@ qint64 DownloadManager::getFileSize(int index) const } -int DownloadManager::getProgress(int index) const +std::pair DownloadManager::getProgress(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { throw MyException(tr("progress: invalid download index %1").arg(index)); @@ -1076,12 +1076,28 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) if (bytesTotal > info->m_TotalSize) { info->m_TotalSize = bytesTotal; } - int oldProgress = info->m_Progress; - info->m_Progress = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal); - TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal); - if (oldProgress != info->m_Progress) { - emit update(index); + int oldProgress = info->m_Progress.first; + info->m_Progress.first = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal); + + // calculate the download speed + double speed = bytesReceived * 1000.0 / info->m_StartTime.elapsed(); + QString unit; + if (speed < 1024) { + unit = "bytes/sec"; + } + else if (speed < 1024 * 1024) { + speed /= 1024; + 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); + + TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal); + emit update(index); } } } catch (const std::bad_alloc&) { diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 308d365b..0ff77011 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -75,7 +75,7 @@ private: QNetworkReply *m_Reply; QTime m_StartTime; qint64 m_PreResumeSize; - int m_Progress; + std::pair m_Progress; DownloadState m_State; int m_CurrentUrl; QStringList m_Urls; @@ -259,7 +259,7 @@ public: * @param index index of the file to look up * @return progress of the download in percent (integer) **/ - int getProgress(int index) const; + std::pair getProgress(int index) const; /** * @brief retrieve the current state of the download -- cgit v1.3.1 From 938d3ce91bd436e840684c0d633df71ef33fd4e0 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 2 May 2018 17:10:38 +0100 Subject: Take a list of proxied plugins for each name to work around multiple inheritance issues --- src/plugincontainer.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index f6376496..8935c472 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -155,14 +155,18 @@ bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName) QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath())); for (const QString &pluginName : pluginNames) { try { - QObject *proxiedPlugin = proxy->instantiate(pluginName); - if (proxiedPlugin != nullptr) { - if (registerPlugin(proxiedPlugin, pluginName)) { - qDebug("loaded plugin \"%s\"", qPrintable(QFileInfo(pluginName).fileName())); - } else { - qWarning("plugin \"%s\" failed to load. If this plugin is for an older version of MO " - "you have to update it or delete it if no update exists.", - qPrintable(pluginName)); + // we get a list of matching plugins as proxies don't necessarily have a good way of supporting multiple inheritance + QList matchingPlugins = proxy->instantiate(pluginName); + for (QObject *proxiedPlugin : matchingPlugins) { + if (proxiedPlugin != nullptr) { + if (registerPlugin(proxiedPlugin, pluginName)) { + qDebug("loaded plugin \"%s\"", qPrintable(QFileInfo(pluginName).fileName())); + } + else { + qWarning("plugin \"%s\" failed to load. If this plugin is for an older version of MO " + "you have to update it or delete it if no update exists.", + qPrintable(pluginName)); + } } } } catch (const std::exception &e) { -- cgit v1.3.1 From 238c252b4c836433d41054387263bb91c07169a3 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 2 May 2018 17:11:54 +0100 Subject: Coalesce plugins with the same name in the settings dialogue (as they already share setting values) --- src/settings.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/settings.cpp b/src/settings.cpp index 21c73dec..2bcf2d02 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -908,12 +908,16 @@ Settings::PluginsTab::PluginsTab(Settings *m_parent, SettingsDialog &m_dialog) , m_pluginBlacklistList(m_dialog.findChild("pluginBlacklist")) { // display plugin settings + QSet handledNames; for (IPlugin *plugin : m_parent->m_Plugins) { + if (handledNames.contains(plugin->name())) + continue; QListWidgetItem *listItem = new QListWidgetItem(plugin->name(), m_pluginsList); listItem->setData(Qt::UserRole, QVariant::fromValue((void*)plugin)); listItem->setData(Qt::UserRole + 1, m_parent->m_PluginSettings[plugin->name()]); listItem->setData(Qt::UserRole + 2, m_parent->m_PluginDescriptions[plugin->name()]); m_pluginsList->addItem(listItem); + handledNames.insert(plugin->name()); } // display plugin blacklist -- cgit v1.3.1 From 4245036f524121aa113a44e02b2893bc08fb3d31 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 2 May 2018 17:12:34 +0100 Subject: Remove unused function which made things confusing --- src/settingsdialog.cpp | 7 ------- src/settingsdialog.h | 2 -- 2 files changed, 9 deletions(-) (limited to 'src') diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index a4071c26..44fc9b5e 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -58,13 +58,6 @@ SettingsDialog::~SettingsDialog() delete ui; } -void SettingsDialog::addPlugins(const std::vector &plugins) -{ - for (IPlugin *plugin : plugins) { - ui->pluginsList->addItem(plugin->name()); - } -} - void SettingsDialog::accept() { QString newModPath = ui->modDirEdit->text(); diff --git a/src/settingsdialog.h b/src/settingsdialog.h index 32a8d0c5..2e270131 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -44,8 +44,6 @@ public: explicit SettingsDialog(PluginContainer *pluginContainer, QWidget *parent = 0); ~SettingsDialog(); - void addPlugins(const std::vector &plugins); - public slots: virtual void accept(); -- cgit v1.3.1 From 168da18aa51868ad3ac908affb55a8aabdba1aed Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 2 May 2018 12:38:23 -0500 Subject: Fix MO endorsement check and add game to ModInfo s_ModsByModID --- src/downloadmanager.cpp | 8 ++++++++ src/downloadmanager.h | 8 ++++++++ src/mainwindow.cpp | 12 ++++++++++-- src/modinfo.cpp | 14 ++++++++------ src/modinfo.h | 4 ++-- src/organizercore.cpp | 3 ++- 6 files changed, 38 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 097a1168..d9e86544 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -895,6 +895,14 @@ int DownloadManager::getModID(int index) const return m_ActiveDownloads.at(index)->m_FileInfo->modID; } +QString DownloadManager::getGameName(int index) const +{ + if ((index < 0) || (index >= m_ActiveDownloads.size())) { + throw MyException(tr("mod id: invalid download index %1").arg(index)); + } + return m_ActiveDownloads.at(index)->m_FileInfo->gameName; +} + bool DownloadManager::isHidden(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 0ff77011..e1925040 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -288,6 +288,14 @@ public: **/ int getModID(int index) const; + /** + * @brief retrieve the game name of the downlaod specified by the index + * + * @param index index of the file to look up + * @return the game name + **/ + QString getGameName(int index) const; + /** * @brief determine if the specified file is supposed to be hidden * @param index index of the file to look up diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3933987e..a95b7b70 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4231,12 +4231,20 @@ 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()) { + if (result["id"].toInt() == m_OrganizerCore.managedGame()->nexusModOrganizerID() + && result["game_id"].toInt() == m_OrganizerCore.managedGame()->nexusGameID()) { if (!result["voted_by_user"].toBool()) { ui->actionEndorseMO->setVisible(true); } } else { - std::vector info = ModInfo::getByModID(result["id"].toInt()); + QString gameName = m_OrganizerCore.managedGame()->gameShortName(); + for (IPluginGame *game : m_PluginContainer.plugins()) { + if (game->nexusGameID() == result["game_id"].toInt()) { + gameName = game->gameShortName(); + break; + } + } + std::vector info = ModInfo::getByModID(gameName, result["id"].toInt()); for (auto iter = info.begin(); iter != info.end(); ++iter) { (*iter)->setNewestVersion(result["version"].toString()); (*iter)->setNexusDescription(result["description"].toString()); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index d916d982..6b3fd528 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -48,7 +48,7 @@ using namespace MOShared; std::vector ModInfo::s_Collection; std::map ModInfo::s_ModsByName; -std::map > ModInfo::s_ModsByModID; +std::map, std::vector> ModInfo::s_ModsByModID; int ModInfo::s_NextID; QMutex ModInfo::s_Mutex(QMutex::Recursive); @@ -132,11 +132,11 @@ ModInfo::Ptr ModInfo::getByIndex(unsigned int index) } -std::vector ModInfo::getByModID(int modID) +std::vector ModInfo::getByModID(QString game, int modID) { QMutexLocker locker(&s_Mutex); - auto iter = s_ModsByModID.find(modID); + auto iter = s_ModsByModID.find(std::pair(game, modID)); if (iter == s_ModsByModID.end()) { return std::vector(); } @@ -161,11 +161,11 @@ bool ModInfo::removeMod(unsigned int index) ModInfo::Ptr modInfo = s_Collection[index]; s_ModsByName.erase(s_ModsByName.find(modInfo->name())); - auto iter = s_ModsByModID.find(modInfo->getNexusID()); + auto iter = s_ModsByModID.find(std::pair(modInfo->getGameName(), modInfo->getNexusID())); if (iter != s_ModsByModID.end()) { std::vector indices = iter->second; indices.erase(std::remove(indices.begin(), indices.end(), index), indices.end()); - s_ModsByModID[modInfo->getNexusID()] = indices; + s_ModsByModID[std::pair(modInfo->getGameName(), modInfo->getNexusID())] = indices; } // physically remove the mod directory @@ -255,9 +255,10 @@ void ModInfo::updateIndices() for (unsigned int i = 0; i < s_Collection.size(); ++i) { QString modName = s_Collection[i]->internalName(); + QString game = s_Collection[i]->getGameName(); int modID = s_Collection[i]->getNexusID(); s_ModsByName[modName] = i; - s_ModsByModID[modID].push_back(i); + s_ModsByModID[std::pair(game, modID)].push_back(i); } } @@ -289,6 +290,7 @@ int ModInfo::checkAllForUpdate(PluginContainer *pluginContainer, QObject *receiv IPluginGame const *game = qApp->property("managed_game").value(); modIDs.push_back(game->nexusModOrganizerID()); checkChunkForUpdate(pluginContainer, modIDs, receiver, game->gameShortName()); + modIDs.clear(); std::multimap> organizedGames; for (auto mod : s_Collection) { diff --git a/src/modinfo.h b/src/modinfo.h index 19be2ca2..7bb2bdad 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -148,7 +148,7 @@ public: * @todo in its current form, this function is broken! There may be multiple mods with the same nexus id, * this function will return only one of them **/ - static std::vector getByModID(int modID); + static std::vector getByModID(QString game, int modID); /** * @brief remove a mod by index @@ -638,7 +638,7 @@ protected: private: static QMutex s_Mutex; - static std::map > s_ModsByModID; + static std::map, std::vector > s_ModsByModID; static int s_NextID; bool m_Valid; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 69c28c5c..4739021a 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -919,13 +919,14 @@ void OrganizerCore::installDownload(int index) { try { QString fileName = m_DownloadManager.getFilePath(index); + QString gameName = m_DownloadManager.getGameName(index); int modID = m_DownloadManager.getModID(index); int fileID = m_DownloadManager.getFileInfo(index)->fileID; GuessedValue modName; // see if there already are mods with the specified mod id if (modID != 0) { - std::vector modInfo = ModInfo::getByModID(modID); + std::vector modInfo = ModInfo::getByModID(gameName, modID); for (auto iter = modInfo.begin(); iter != modInfo.end(); ++iter) { std::vector flags = (*iter)->getFlags(); if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) -- cgit v1.3.1 From 2091330af253399805a08c6d8f142261fc2fc8f6 Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 2 May 2018 13:47:59 -0500 Subject: Clean up handling of managed game instance in NexusInterface --- src/nexusinterface.cpp | 61 +++++++++++++++++++------------------------------- src/nexusinterface.h | 6 +---- src/organizercore.cpp | 3 --- 3 files changed, 24 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 8bf0de6e..d2a52c7b 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -247,21 +247,19 @@ bool NexusInterface::isURLGameRelated(const QUrl &url) const { QString const name(url.toString()); return name.startsWith(getGameURL("") + "/") || - name.startsWith(getOldModsURL() + "/"); + name.startsWith(getOldModsURL("") + "/"); } QString NexusInterface::getGameURL(QString gameName) const { IPluginGame *game = getGame(gameName); - if (game != nullptr) { - return "https://www.nexusmods.com/" + game->gameNexusName().toLower(); - } - return "https://www.nexusmods.com/" + m_Game->gameNexusName().toLower(); + return "https://www.nexusmods.com/" + game->gameNexusName().toLower(); } -QString NexusInterface::getOldModsURL() const +QString NexusInterface::getOldModsURL(QString gameName) const { - return "https://" + m_Game->gameNexusName().toLower() + ".nexusmods.com/mods"; + IPluginGame *game = getGame(gameName); + return "https://" + game->gameNexusName().toLower() + ".nexusmods.com/mods"; } @@ -291,7 +289,7 @@ bool NexusInterface::isModURL(int modID, const QString &url) const return true; } //Try the alternate (old style) mod name - QString alt = QString("%1/%2").arg(getOldModsURL()).arg(modID); + QString alt = QString("%1/%2").arg(getOldModsURL("")).arg(modID); return QUrl(alt) == QUrl(url); } @@ -321,22 +319,17 @@ int NexusInterface::requestUpdates(const std::vector &modIDs, QObject *rece QString gameName, const QString &subModule) { IPluginGame *game = getGame(gameName); + NXMRequestInfo requestInfo(modIDs, NXMRequestInfo::TYPE_GETUPDATES, userData, subModule, game); + m_RequestQueue.enqueue(requestInfo); - if (game != nullptr) { - NXMRequestInfo requestInfo(modIDs, NXMRequestInfo::TYPE_GETUPDATES, userData, subModule, game); - m_RequestQueue.enqueue(requestInfo); - - connect(this, SIGNAL(nxmUpdatesAvailable(std::vector, QVariant, QVariant, int)), - receiver, SLOT(nxmUpdatesAvailable(std::vector, QVariant, QVariant, int)), Qt::UniqueConnection); - - connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QString)), - receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmUpdatesAvailable(std::vector, QVariant, QVariant, int)), + receiver, SLOT(nxmUpdatesAvailable(std::vector, QVariant, QVariant, int)), Qt::UniqueConnection); - nextRequest(); - return requestInfo.m_ID; - } + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QString)), Qt::UniqueConnection); - return -1; + nextRequest(); + return requestInfo.m_ID; } @@ -378,20 +371,17 @@ int NexusInterface::requestFiles(QString gameName, int modID, QObject *receiver, int NexusInterface::requestFileInfo(QString gameName, int modID, int fileID, QObject *receiver, QVariant userData, const QString &subModule) { IPluginGame *gamePlugin = getGame(gameName); - if (gamePlugin != nullptr) { - NXMRequestInfo requestInfo(modID, fileID, NXMRequestInfo::TYPE_FILEINFO, userData, subModule, gamePlugin); - m_RequestQueue.enqueue(requestInfo); + NXMRequestInfo requestInfo(modID, fileID, NXMRequestInfo::TYPE_FILEINFO, userData, subModule, gamePlugin); + m_RequestQueue.enqueue(requestInfo); - connect(this, SIGNAL(nxmFileInfoAvailable(QString, int, int, QVariant, QVariant, int)), - receiver, SLOT(nxmFileInfoAvailable(QString, int, int, QVariant, QVariant, int)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmFileInfoAvailable(QString, int, int, QVariant, QVariant, int)), + receiver, SLOT(nxmFileInfoAvailable(QString, int, int, QVariant, QVariant, int)), Qt::UniqueConnection); - connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QString)), - receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QString)), Qt::UniqueConnection); + connect(this, SIGNAL(nxmRequestFailed(QString, int, int, QVariant, int, QString)), + receiver, SLOT(nxmRequestFailed(QString, int, int, QVariant, int, QString)), Qt::UniqueConnection); - nextRequest(); - return requestInfo.m_ID; - } - return -1; + nextRequest(); + return requestInfo.m_ID; } @@ -438,7 +428,7 @@ bool NexusInterface::requiresLogin(const NXMRequestInfo &info) IPluginGame* NexusInterface::getGame(QString gameName) const { auto gamePlugins = m_PluginContainer->plugins(); - IPluginGame *gamePlugin = nullptr; + IPluginGame *gamePlugin = qApp->property("managed_game").value(); for (auto plugin : gamePlugins) { if (plugin->gameShortName().compare(gameName, Qt::CaseInsensitive) == 0) { gamePlugin = plugin; @@ -637,11 +627,6 @@ void NexusInterface::requestTimeout() } } -void NexusInterface::managedGameChanged(IPluginGame const *game) -{ - m_Game = game; -} - namespace { QString get_management_url(MOBase::IPluginGame const *game) { diff --git a/src/nexusinterface.h b/src/nexusinterface.h index 4a5ac0dd..defb370f 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -349,9 +349,6 @@ signals: void nxmEndorsementToggled(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID); void nxmRequestFailed(QString gameName, int modID, int fileID, QVariant userData, int requestID, const QString &errorString); -public slots: - void managedGameChanged(MOBase::IPluginGame const *game); - private slots: void requestFinished(); @@ -404,7 +401,7 @@ private: void requestFinished(std::list::iterator iter); bool requiresLogin(const NXMRequestInfo &info); MOBase::IPluginGame *getGame(QString gameName) const; - QString getOldModsURL() const; + QString getOldModsURL(QString gameName) const; private: @@ -418,7 +415,6 @@ private: MOBase::VersionInfo m_MOVersion; QString m_NMMVersion; - MOBase::IPluginGame const *m_Game; PluginContainer *m_PluginContainer; }; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 4739021a..d1b1fcd7 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -240,9 +240,6 @@ OrganizerCore::OrganizerCore(const QSettings &initSettings) SLOT(managedGameChanged(MOBase::IPluginGame const *))); connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const *)), &m_PluginList, SLOT(managedGameChanged(MOBase::IPluginGame const *))); - connect(this, SIGNAL(managedGameChanged(MOBase::IPluginGame const *)), - NexusInterface::instance(m_PluginContainer), - SLOT(managedGameChanged(MOBase::IPluginGame const *))); connect(&m_PluginList, &PluginList::writePluginsList, &m_PluginListsWriter, &DelayedFileWriterBase::write); -- cgit v1.3.1 From 6588a81b7f52e94c4fca4a7378ca13307d51b58e Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 2 May 2018 13:48:31 -0500 Subject: Only check for the mod organizer endorsement if the ID is set --- src/modinfo.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 6b3fd528..e505bacc 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -288,9 +288,11 @@ int ModInfo::checkAllForUpdate(PluginContainer *pluginContainer, QObject *receiv //I ought to store this, it's used elsewhere IPluginGame const *game = qApp->property("managed_game").value(); - modIDs.push_back(game->nexusModOrganizerID()); - checkChunkForUpdate(pluginContainer, modIDs, receiver, game->gameShortName()); - modIDs.clear(); + if (game->nexusModOrganizerID()) { + modIDs.push_back(game->nexusModOrganizerID()); + checkChunkForUpdate(pluginContainer, modIDs, receiver, game->gameShortName()); + modIDs.clear(); + } std::multimap> organizedGames; for (auto mod : s_Collection) { -- cgit v1.3.1 From f7c9b8c3336da02d3360b810247c00494df114fd Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 2 May 2018 17:04:28 -0500 Subject: Thread extraction process and properly offload function calls from threads --- src/installationmanager.cpp | 80 +++++++++++++++++++++++++++++++++++---------- src/installationmanager.h | 10 ++++++ 2 files changed, 72 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 668be84e..8ee2220e 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -49,6 +49,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include @@ -94,6 +95,9 @@ InstallationManager::InstallationManager() if (!m_ArchiveHandler->isValid()) { throw MyException(getErrorString(m_ArchiveHandler->getLastError())); } + + connect(this, SIGNAL(progressUpdate(float)), this, (SLOT(doProgressUpdate(float)))); + connect(this, SIGNAL(progressUpdate(const QString)), this, (SLOT(doProgressFileUpdate(const QString)))); } InstallationManager::~InstallationManager() @@ -190,10 +194,19 @@ bool InstallationManager::unpackSingleFile(const QString &fileName) m_InstallationProgress->setFixedSize(600, 100); m_InstallationProgress->show(); - bool res = m_ArchiveHandler->extract(QDir::tempPath(), - new MethodCallback(this, &InstallationManager::updateProgress), - nullptr, - new MethodCallback(this, &InstallationManager::report7ZipError)); + QFuture future = QtConcurrent::run([&]() -> bool { + return m_ArchiveHandler->extract( + QDir::tempPath(), + new MethodCallback(this, &InstallationManager::updateProgress), + nullptr, + new MethodCallback(this, &InstallationManager::report7ZipError) + ); + }); + while (!future.isFinished()) { + QCoreApplication::processEvents(); + ::Sleep(50); + } + bool res = future.result(); return res; } @@ -279,10 +292,19 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool m_InstallationProgress->show(); // unpack only the files we need for the installer - if (!m_ArchiveHandler->extract(QDir::tempPath(), - new MethodCallback(this, &InstallationManager::updateProgress), - nullptr, - new MethodCallback(this, &InstallationManager::report7ZipError))) { + QFuture future = QtConcurrent::run([&]() -> bool { + return m_ArchiveHandler->extract( + QDir::tempPath(), + new MethodCallback(this, &InstallationManager::updateProgress), + nullptr, + new MethodCallback(this, &InstallationManager::report7ZipError) + ); + }); + while (!future.isFinished()) { + QCoreApplication::processEvents(); + ::Sleep(50); + } + if (!future.result()) { throw MyException(QString("extracting failed (%1)").arg(m_ArchiveHandler->getLastError())); } @@ -408,9 +430,23 @@ DirectoryTree::Node *InstallationManager::getSimpleArchiveBase(DirectoryTree *da void InstallationManager::updateProgress(float percentage) +{ + emit progressUpdate(percentage); +} + + +void InstallationManager::updateProgressFile(QString const &fileName) +{ + emit progressUpdate(fileName); +} + + +void InstallationManager::doProgressUpdate(float percentage) { if (m_InstallationProgress != nullptr) { - m_InstallationProgress->setValue(static_cast(percentage * 100.0)); + QMetaObject::invokeMethod(m_InstallationProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, static_cast(percentage * 100.0))); + //m_InstallationProgress->setValue(percent); + if (m_InstallationProgress->wasCanceled()) { m_ArchiveHandler->cancel(); m_InstallationProgress->reset(); @@ -419,12 +455,11 @@ void InstallationManager::updateProgress(float percentage) } -void InstallationManager::updateProgressFile(QString const &fileName) +void InstallationManager::doProgressFileUpdate(QString const fileName) { - if (m_InstallationProgress != nullptr) { - m_InstallationProgress->setLabelText(fileName); - QCoreApplication::processEvents(); - } + if (m_InstallationProgress != nullptr) { + m_InstallationProgress->setLabelText(fileName); + } } @@ -568,10 +603,19 @@ bool InstallationManager::doInstall(GuessedValue &modName, QString game m_InstallationProgress->setWindowModality(Qt::WindowModal); m_InstallationProgress->setFixedSize(600, 100); m_InstallationProgress->show(); - if (!m_ArchiveHandler->extract(targetDirectory, - new MethodCallback(this, &InstallationManager::updateProgress), - new MethodCallback(this, &InstallationManager::updateProgressFile), - new MethodCallback(this, &InstallationManager::report7ZipError))) { + QFuture future = QtConcurrent::run([&]() -> bool { + return m_ArchiveHandler->extract( + targetDirectory, + new MethodCallback(this, &InstallationManager::updateProgress), + new MethodCallback(this, &InstallationManager::updateProgressFile), + new MethodCallback(this, &InstallationManager::report7ZipError) + ); + }); + while (!future.isFinished()) { + QCoreApplication::processEvents(); + ::Sleep(50); + } + if (!future.result()) { if (m_ArchiveHandler->getLastError() == Archive::ERROR_EXTRACT_CANCELLED) { return false; } else { diff --git a/src/installationmanager.h b/src/installationmanager.h index bdf71a8d..84ccd1a4 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -177,6 +177,16 @@ private: void postInstallCleanup(); +signals: + + void progressUpdate(float percentage); + void progressUpdate(QString const fileName); + +private slots: + + void doProgressUpdate(float percentage); + void doProgressFileUpdate(const QString fileName); + private: struct ByPriority { -- cgit v1.3.1 From 9872c1e462d100221ca38aeb1f8ab35eda7fc8c2 Mon Sep 17 00:00:00 2001 From: Silarn Date: Thu, 3 May 2018 16:17:40 -0500 Subject: Allow for primary game sources and marking mods as converted/working --- src/mainwindow.cpp | 12 ++++++++++++ src/mainwindow.h | 1 + src/modinfo.cpp | 2 +- src/modinfo.h | 5 +++++ src/modinforegular.cpp | 22 +++++++++++++++++----- src/modinforegular.h | 7 +++++++ src/modlist.cpp | 2 +- 7 files changed, 44 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a95b7b70..d3ea2b08 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2599,6 +2599,14 @@ void MainWindow::ignoreMissingData_clicked() emit modListDataChanged(m_OrganizerCore.modList()->index(m_ContextRow, 0), m_OrganizerCore.modList()->index(m_ContextRow, m_OrganizerCore.modList()->columnCount() - 1)); } +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)); +} + void MainWindow::visitOnNexus_clicked() { @@ -3463,6 +3471,10 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addAction(tr("Ignore missing data"), this, SLOT(ignoreMissingData_clicked())); } + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_ALTERNATE_GAME) != flags.end()) { + menu->addAction(tr("Mark as converted/working"), this, SLOT(markConverted_clicked())); + } + if (info->getNexusID() > 0) { menu->addAction(tr("Visit on Nexus"), this, SLOT(visitOnNexus_clicked())); } else if ((info->getURL() != "")) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 7f38d766..7af6d712 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -409,6 +409,7 @@ private slots: void dontendorse_clicked(); void unendorse_clicked(); void ignoreMissingData_clicked(); + void markConverted_clicked(); void visitOnNexus_clicked(); void visitWebPage_clicked(); void openExplorer_clicked(); diff --git a/src/modinfo.cpp b/src/modinfo.cpp index e505bacc..bd4c1254 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -414,4 +414,4 @@ void ModInfo::testValid() while (dirIter.hasNext()) { dirIter.next(); } -} +} \ No newline at end of file diff --git a/src/modinfo.h b/src/modinfo.h index 7bb2bdad..001a78dc 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -568,6 +568,11 @@ public: */ void testValid(); + /** + * @brief updates the mod to flag it as converted in order to ignore the alternate game warning + */ + virtual void markConverted(bool converted) {} + /** * @brief reads meta information from disk */ diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index 49fc3aa2..c19294f1 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -30,6 +30,7 @@ ModInfoRegular::ModInfoRegular(PluginContainer *pluginContainer, const IPluginGa , m_Repository() , m_GameName(game->gameShortName()) , m_IsAlternate(false) + , m_Converted(false) , m_MetaInfoChanged(false) , m_EndorsedState(ENDORSED_UNKNOWN) , m_NexusBridge(pluginContainer) @@ -39,7 +40,8 @@ ModInfoRegular::ModInfoRegular(PluginContainer *pluginContainer, const IPluginGa // read out the meta-file for information readMeta(); if (m_GameName.compare(game->gameShortName(), Qt::CaseInsensitive) != 0) - m_IsAlternate = true; + if (!game->primarySources().contains(m_GameName, Qt::CaseInsensitive)) + m_IsAlternate = true; connect(&m_NexusBridge, SIGNAL(descriptionAvailable(QString,int,QVariant,QVariant)) , this, SLOT(nxmDescriptionAvailable(QString,int,QVariant,QVariant))); @@ -82,9 +84,10 @@ void ModInfoRegular::readMeta() m_IgnoredVersion = metaFile.value("ignoredVersion", "").toString(); m_InstallationFile = metaFile.value("installationFile", "").toString(); m_NexusDescription = metaFile.value("nexusDescription", "").toString(); - m_Repository = metaFile.value("repository", "Nexus").toString(); - m_URL = metaFile.value("url", "").toString(); - m_LastNexusQuery = QDateTime::fromString(metaFile.value("lastNexusQuery", "").toString(), Qt::ISODate); + m_Repository = metaFile.value("repository", "Nexus").toString(); + m_Converted = metaFile.value("converted", false).toBool(); + m_URL = metaFile.value("url", "").toString(); + m_LastNexusQuery = QDateTime::fromString(metaFile.value("lastNexusQuery", "").toString(), Qt::ISODate); if (metaFile.contains("endorsed")) { if (metaFile.value("endorsed").canConvert()) { switch (metaFile.value("endorsed").toInt()) { @@ -146,6 +149,7 @@ void ModInfoRegular::saveMeta() metaFile.setValue("nexusDescription", m_NexusDescription); metaFile.setValue("url", m_URL); metaFile.setValue("lastNexusQuery", m_LastNexusQuery.toString(Qt::ISODate)); + metaFile.setValue("converted", m_Converted); if (m_EndorsedState != ENDORSED_UNKNOWN) { metaFile.setValue("endorsed", m_EndorsedState); } @@ -403,6 +407,14 @@ void ModInfoRegular::endorse(bool doEndorse) } } +void ModInfoRegular::markConverted(bool converted) +{ + m_Converted = converted; + m_MetaInfoChanged = true; + saveMeta(); + emit modDetailsUpdated(true); +} + QString ModInfoRegular::absolutePath() const { @@ -435,7 +447,7 @@ std::vector ModInfoRegular::getFlags() const if (m_PluginSelected) { result.push_back(ModInfo::FLAG_PLUGIN_SELECTED); } - if (m_IsAlternate) { + if (m_IsAlternate && !m_Converted) { result.push_back(ModInfo::FLAG_ALTERNATE_GAME); } return result; diff --git a/src/modinforegular.h b/src/modinforegular.h index c47f10c8..3abaded4 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -27,6 +27,7 @@ public: virtual bool isEmpty() const; bool isAlternate() { return m_IsAlternate; } + bool isConverted() { return m_Converted; } /** * @brief test if there is a newer version of the mod @@ -182,6 +183,11 @@ public: */ virtual void endorse(bool doEndorse); + /** + * @brief updates the mod to flag it as converted in order to ignore the alternate game warning + */ + virtual void markConverted(bool converted) override; + /** * @brief getter for the mod name * @@ -351,6 +357,7 @@ private: bool m_MetaInfoChanged; bool m_IsAlternate; + bool m_Converted; MOBase::VersionInfo m_NewestVersion; MOBase::VersionInfo m_IgnoredVersion; diff --git a/src/modlist.cpp b/src/modlist.cpp index aad88d1f..2d58081d 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -763,7 +763,7 @@ IModList::ModStates ModList::state(unsigned int modIndex) const } if (modInfo->isRegular()) { QSharedPointer modInfoRegular = modInfo.staticCast(); - if (modInfoRegular->isAlternate()) + if (modInfoRegular->isAlternate() && !modInfoRegular->isConverted()) result |= IModList::STATE_ALTERNATE; } if (modInfo->canBeEnabled()) { -- cgit v1.3.1 From cc32c8c06bc20dbd0905e48ffd8c6d027184ad06 Mon Sep 17 00:00:00 2001 From: Silarn Date: Thu, 3 May 2018 17:14:05 -0500 Subject: Use a five second delta to calculate speed for more responsive display --- src/downloadmanager.cpp | 18 ++++++++++++++++-- src/downloadmanager.h | 3 ++- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index d9e86544..a60057ef 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -686,6 +686,9 @@ 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(); + std::get<0>(info->m_SpeedDiff) = 0; + std::get<1>(info->m_SpeedDiff) = 0; + std::get<2>(info->m_SpeedDiff) = 0; qDebug("resume at %lld bytes", info->m_ResumePos); QByteArray rangeHeader = "bytes=" + QByteArray::number(info->m_ResumePos) + "-"; request.setRawHeader("Range", rangeHeader); @@ -1080,7 +1083,8 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) setState(info, STATE_CANCELED); } else if (info->m_State == STATE_PAUSING) { setState(info, STATE_PAUSED); - } else { + } + else { if (bytesTotal > info->m_TotalSize) { info->m_TotalSize = bytesTotal; } @@ -1088,7 +1092,11 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) info->m_Progress.first = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal); // calculate the download speed - double speed = bytesReceived * 1000.0 / info->m_StartTime.elapsed(); + double speed = (bytesReceived - std::get<1>(info->m_SpeedDiff)) * 1000.0 / // Calculate with the data transferred in the last 5 seconds... + (std::get<1>(info->m_SpeedDiff) ? // If we are past 5 seconds + (5 * 1000) + (info->m_StartTime.elapsed() - std::get<2>(info->m_SpeedDiff)) : // Divide by 5 seconds + the diff between chunks + info->m_StartTime.elapsed()); // Else just use the current elapsed time + QString unit; if (speed < 1024) { unit = "bytes/sec"; @@ -1104,6 +1112,12 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) info->m_Progress.second = QString::fromLatin1("%1% - %2 %3").arg(info->m_Progress.first).arg(speed, 3, 'f', 1).arg(unit); + if (info->m_StartTime.elapsed() >= 5 * 1000) { + std::get<1>(info->m_SpeedDiff) = std::get<1>(info->m_SpeedDiff) + bytesReceived - std::get<0>(info->m_SpeedDiff); + std::get<2>(info->m_SpeedDiff) = info->m_StartTime.elapsed(); + } + std::get<0>(info->m_SpeedDiff) = bytesReceived; + TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal); emit update(index); } diff --git a/src/downloadmanager.h b/src/downloadmanager.h index e1925040..6a110a41 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -76,6 +76,7 @@ private: QTime m_StartTime; qint64 m_PreResumeSize; std::pair m_Progress; + std::tuple m_SpeedDiff; DownloadState m_State; int m_CurrentUrl; QStringList m_Urls; @@ -113,7 +114,7 @@ private: private: static unsigned int s_NextDownloadID; private: - DownloadInfo() : m_TotalSize(0), m_ReQueried(false), m_Hidden(false) {} + DownloadInfo() : m_TotalSize(0), m_ReQueried(false), m_Hidden(false), m_SpeedDiff(std::tuple(0,0,0)) {} }; public: -- cgit v1.3.1 From fa193ddabbaef66cbd53a19dd7ff00e755b10909 Mon Sep 17 00:00:00 2001 From: Silarn Date: Thu, 3 May 2018 18:25:27 -0500 Subject: Initial icons for alternate games --- src/modflagicondelegate.cpp | 2 +- src/resources.qrc | 2 ++ src/resources/game-warning-16.png | Bin 0 -> 780 bytes src/resources/game-warning.png | Bin 0 -> 2528 bytes 4 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 src/resources/game-warning-16.png create mode 100644 src/resources/game-warning.png (limited to 'src') diff --git a/src/modflagicondelegate.cpp b/src/modflagicondelegate.cpp index 8b54ab00..1995964b 100644 --- a/src/modflagicondelegate.cpp +++ b/src/modflagicondelegate.cpp @@ -48,7 +48,7 @@ QString ModFlagIconDelegate::getFlagIcon(ModInfo::EFlag flag) const case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return ":/MO/gui/emblem_conflict_overwritten"; case ModInfo::FLAG_CONFLICT_MIXED: return ":/MO/gui/emblem_conflict_mixed"; case ModInfo::FLAG_CONFLICT_REDUNDANT: return ":MO/gui/emblem_conflict_redundant"; - case ModInfo::FLAG_ALTERNATE_GAME: return ":MO/gui/problem"; + case ModInfo::FLAG_ALTERNATE_GAME: return ":MO/gui/alternate_game"; default: return QString(); } } diff --git a/src/resources.qrc b/src/resources.qrc index 14c8e533..a18baf45 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -77,6 +77,8 @@ resources/archive-conflict-mixed.png resources/archive-conflict-neutral.png resources/archive-conflict-winner.png + resources/game-warning.png + resources/game-warning-16.png resources/contents/jigsaw-piece.png diff --git a/src/resources/game-warning-16.png b/src/resources/game-warning-16.png new file mode 100644 index 00000000..0c421b17 Binary files /dev/null and b/src/resources/game-warning-16.png differ diff --git a/src/resources/game-warning.png b/src/resources/game-warning.png new file mode 100644 index 00000000..d54a5a36 Binary files /dev/null and b/src/resources/game-warning.png differ -- cgit v1.3.1 From 0eaa1c6791910ad54227e5d578c6570e6f68c046 Mon Sep 17 00:00:00 2001 From: Silarn Date: Thu, 3 May 2018 19:36:14 -0500 Subject: Add version check and update processor --- src/main.cpp | 1 + src/mainwindow.cpp | 24 ++++++++++++++++++++++++ src/mainwindow.h | 1 + 3 files changed, 26 insertions(+) (limited to 'src') diff --git a/src/main.cpp b/src/main.cpp index 2e496e0f..9c30a1c6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -631,6 +631,7 @@ int runApplication(MOApplication &application, SingleInstance &instance, QObject::connect(&instance, SIGNAL(messageSent(QString)), &organizer, SLOT(externalMessage(QString))); + mainWindow.processUpdates(); mainWindow.readSettings(); qDebug("displaying main window"); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d3ea2b08..2aaf7f59 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -148,6 +148,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -1665,6 +1666,29 @@ void MainWindow::readSettings() } } +void MainWindow::processUpdates() { + QSettings settings(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::iniFileName()), QSettings::IniFormat); + QVersionNumber lastVersion = QVersionNumber::fromString(settings.value("version", "2.1.2").toString()).normalized(); + QVersionNumber currentVersion = QVersionNumber::fromString(m_OrganizerCore.getVersion().displayString()).normalized(); + if (!m_OrganizerCore.settings().directInterface().value("first_start", true).toBool()) { + if (lastVersion < QVersionNumber(2, 1, 3)) { + bool lastHidden = true; + for (int i = ModList::COL_GAME; i < ui->modList->model()->columnCount(); ++i) { + bool hidden = ui->modList->header()->isSectionHidden(i); + ui->modList->header()->setSectionHidden(i, lastHidden); + lastHidden = hidden; + } + } + } + + if (currentVersion > lastVersion) + settings.setValue("version", currentVersion.toString()); + else if (currentVersion < lastVersion) + qWarning() << tr("Notice: Your current MO version (%1) is lower than the previous version (%2).
" + "The GUI may not downgrade gracefully, so you may experience oddities.
" + "However, there should be no serious issues.").arg(lastVersion.toString()).arg(currentVersion.toString()).toStdWString(); +} + void MainWindow::storeSettings(QSettings &settings) { settings.setValue("group_state", ui->groupCombo->currentIndex()); diff --git a/src/mainwindow.h b/src/mainwindow.h index 7af6d712..bbff0d93 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -116,6 +116,7 @@ public: void storeSettings(QSettings &settings) override; void readSettings(); + void processUpdates(); virtual ILockedWaitingForProcess* lock() override; virtual void unlock() override; -- cgit v1.3.1 From cf5833437d1c4cffdb3c4f37be0e59d059c07000 Mon Sep 17 00:00:00 2001 From: Silarn Date: Thu, 3 May 2018 19:36:39 -0500 Subject: Bump to version 2.1.3 --- src/version.rc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/version.rc b/src/version.rc index 142065bf..083cbfb0 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 2,1,2 -#define VER_FILEVERSION_STR "2.1.2\0" +#define VER_FILEVERSION 2,1,3 +#define VER_FILEVERSION_STR "2.1.3\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION @@ -18,9 +18,9 @@ BEGIN BEGIN VALUE "FileVersion", VER_FILEVERSION_STR VALUE "CompanyName", "Mod Organizer 2 Team\0" - VALUE "FileDescription", "Main Mod Oranizer 2 UI\0" + VALUE "FileDescription", "Mod Organizer 2 GUI\0" VALUE "OriginalFilename", "ModOrganizer.exe\0" - VALUE "InternalName", "ModOrganizer\0" + VALUE "InternalName", "ModOrganizer2\0" VALUE "LegalCopyright", "Copyright 2011-2016 Sebastian Herbord\r\nCopyright 2016-2018 Mod Organizer 2 contributors\0" VALUE "ProductName", "Mod Organizer 2\0" VALUE "ProductVersion", VER_FILEVERSION_STR -- cgit v1.3.1 From 979c09b33449b5a57e8825b5bbb62195511c53fc Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 4 May 2018 00:15:48 -0500 Subject: Update translation file --- src/organizer_en.ts | 841 ++++++++++++++++++++++++++-------------------------- 1 file changed, 426 insertions(+), 415 deletions(-) (limited to 'src') diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 59882025..8c36e720 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -322,26 +322,26 @@ 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 @@ -403,95 +403,95 @@ p, li { white-space: pre-wrap; } - - - - + + + + Are you sure? - + 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 - + Delete - + Un-Hide - + Hide - + Cancel - + Pause - + Remove - + Resume - + Delete Installed... - + Delete All... - + Hide Installed... - + Hide All... @@ -499,115 +499,115 @@ p, li { white-space: pre-wrap; } 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. - + 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 - + Delete - + Un-Hide - + Hide - + Cancel - + Pause - + Remove - + Resume - + Delete Installed... - + Delete All... - + Hide Installed... - + Hide All... @@ -712,178 +712,179 @@ p, li { white-space: pre-wrap; } - + 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) - + failed to re-open %1 @@ -1191,98 +1192,98 @@ p, li { white-space: pre-wrap; } InstallationManager - + Password required - + Password - - + + Extracting files - + failed to create backup - + Mod Name - + Name - + Invalid name - + The name you entered is invalid, please enter a different one. - + File format "%1" not supported - + None of the available installer plugins were able to handle that archive - + no error - + 7z.dll not found - + 7z.dll isn't valid - + archive not found - + failed to open archive - + unsupported archive type - + internal library error - + archive invalid - + unknown archive error @@ -1576,8 +1577,8 @@ p, li { white-space: pre-wrap; } - - + + Refresh @@ -1751,7 +1752,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1762,7 +1763,7 @@ p, li { white-space: pre-wrap; } - + No Problems @@ -1792,7 +1793,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -1817,678 +1818,688 @@ 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 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. @@ -2496,12 +2507,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -2509,319 +2520,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 @@ -3528,18 +3539,18 @@ p, li { white-space: pre-wrap; } ModInfoRegular - - + + failed to write %1/meta.ini: error %2 - + %1 contains no esp/esm/esl and no asset (textures, meshes, interface, ...) directory - + Categories: <br> @@ -3858,12 +3869,12 @@ p, li { white-space: pre-wrap; } - + empty response - + invalid response @@ -3871,189 +3882,189 @@ 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 - + 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. @@ -4135,18 +4146,18 @@ Continue? PluginContainer - + Some plugins could not be loaded - - + + Description missing - + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: @@ -4693,13 +4704,13 @@ p, li { white-space: pre-wrap; } - + invalid 7-zip32.dll: %1 - + archive.dll not loaded: "%1" @@ -4898,13 +4909,13 @@ If the folder was still in use, restart MO and try again. - + Error - + Failed to create "%1". Your user account probably lacks permission. @@ -4945,34 +4956,34 @@ If the folder was still in use, restart MO and try again. - - + + Mod Organizer - + An instance of Mod Organizer is already running - + Failed to set up instance - + Please use "Help" from the toolbar to get usage instructions to all elements - - + + <Manage...> - + failed to parse profile %1: %2 @@ -4992,17 +5003,17 @@ If the folder was still in use, restart MO and try again. - + failed to initialize plugin %1: %2 - + Plugin error - + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? (Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) @@ -5023,12 +5034,12 @@ If the folder was still in use, restart MO and try again. - + Script Extender - + Proxy DLL @@ -5827,52 +5838,52 @@ For the other games this is not a sufficient replacement for AI! - + Confirm - + Changing the mod directory affects all your profiles! Mods not present (or named differently) in the new location will be disabled in all profiles. There is no way to undo this unless you backed up your profiles manually. Proceed? - + Select base directory - + Select download directory - + Select mod directory - + Select cache directory - + Select profiles directory - + Select overwrite directory - + Confirm? - + This will make all dialogs show up again where you checked the "Remember selection"-box. Continue? -- cgit v1.3.1 From d9b1cfdb4b287fe43742269aaec26907ddcb6445 Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 4 May 2018 02:30:10 -0500 Subject: invokeMethod was getting called too late and is unneeded now --- src/installationmanager.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'src') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 8ee2220e..4b433899 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -444,8 +444,7 @@ void InstallationManager::updateProgressFile(QString const &fileName) void InstallationManager::doProgressUpdate(float percentage) { if (m_InstallationProgress != nullptr) { - QMetaObject::invokeMethod(m_InstallationProgress, "setValue", Qt::QueuedConnection, Q_ARG(int, static_cast(percentage * 100.0))); - //m_InstallationProgress->setValue(percent); + m_InstallationProgress->setValue(static_cast(percentage * 100.0)); if (m_InstallationProgress->wasCanceled()) { m_ArchiveHandler->cancel(); @@ -454,7 +453,6 @@ void InstallationManager::doProgressUpdate(float percentage) } } - void InstallationManager::doProgressFileUpdate(QString const fileName) { if (m_InstallationProgress != nullptr) { -- cgit v1.3.1 From 0d7460996ae92c59f78cd2b5ccac43a1bf8869b2 Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 4 May 2018 11:34:49 -0500 Subject: Further offload setValue to avoid threading issues --- src/installationmanager.cpp | 9 ++++++++- src/installationmanager.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 4b433899..a99a07e1 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -444,7 +444,7 @@ void InstallationManager::updateProgressFile(QString const &fileName) void InstallationManager::doProgressUpdate(float percentage) { if (m_InstallationProgress != nullptr) { - m_InstallationProgress->setValue(static_cast(percentage * 100.0)); + QMetaObject::invokeMethod(this, "setProgressValue", Qt::QueuedConnection, Q_ARG(int, static_cast(percentage * 100.0))); if (m_InstallationProgress->wasCanceled()) { m_ArchiveHandler->cancel(); @@ -453,6 +453,13 @@ void InstallationManager::doProgressUpdate(float percentage) } } +void InstallationManager::setProgressValue(int percentage) +{ + if (m_InstallationProgress != nullptr) { + m_InstallationProgress->setValue(percentage); + } +} + void InstallationManager::doProgressFileUpdate(QString const fileName) { if (m_InstallationProgress != nullptr) { diff --git a/src/installationmanager.h b/src/installationmanager.h index 84ccd1a4..851390a7 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -186,6 +186,7 @@ private slots: void doProgressUpdate(float percentage); void doProgressFileUpdate(const QString fileName); + void setProgressValue(int percentage); private: -- cgit v1.3.1 From 54e219759283c4805f40c03d2215831081f55d51 Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 4 May 2018 11:35:44 -0500 Subject: Allow update processor to include the managed game if nexus info matches --- src/mainwindow.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2aaf7f59..2830c776 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4274,13 +4274,21 @@ void MainWindow::nxmUpdatesAvailable(const std::vector &modIDs, QVariant us } } else { QString gameName = m_OrganizerCore.managedGame()->gameShortName(); + bool sameNexus = false; for (IPluginGame *game : m_PluginContainer.plugins()) { if (game->nexusGameID() == result["game_id"].toInt()) { gameName = game->gameShortName(); + if (game->nexusGameID() == m_OrganizerCore.managedGame()->nexusGameID()) + sameNexus = true; break; } } std::vector info = ModInfo::getByModID(gameName, result["id"].toInt()); + if (sameNexus) { + std::vector mainInfo = ModInfo::getByModID(m_OrganizerCore.managedGame()->gameShortName(), result["id"].toInt()); + info.reserve(info.size() + mainInfo.size()); + info.insert(info.end(), mainInfo.begin(), mainInfo.end()); + } for (auto iter = info.begin(); iter != info.end(); ++iter) { (*iter)->setNewestVersion(result["version"].toString()); (*iter)->setNexusDescription(result["description"].toString()); -- cgit v1.3.1 From a77433f8810c100e6934c64afe9d69c6c0cff2f4 Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 4 May 2018 14:28:19 -0500 Subject: Use queued connections and do while loops to finish event processing --- src/installationmanager.cpp | 31 ++++++++++++------------------- src/installationmanager.h | 1 - 2 files changed, 12 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index a99a07e1..27c83787 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -96,8 +96,8 @@ InstallationManager::InstallationManager() throw MyException(getErrorString(m_ArchiveHandler->getLastError())); } - connect(this, SIGNAL(progressUpdate(float)), this, (SLOT(doProgressUpdate(float)))); - connect(this, SIGNAL(progressUpdate(const QString)), this, (SLOT(doProgressFileUpdate(const QString)))); + connect(this, SIGNAL(progressUpdate(float)), this, (SLOT(doProgressUpdate(float))), Qt::QueuedConnection); + connect(this, SIGNAL(progressUpdate(const QString)), this, (SLOT(doProgressFileUpdate(const QString))), Qt::QueuedConnection); } InstallationManager::~InstallationManager() @@ -202,10 +202,10 @@ bool InstallationManager::unpackSingleFile(const QString &fileName) new MethodCallback(this, &InstallationManager::report7ZipError) ); }); - while (!future.isFinished()) { - QCoreApplication::processEvents(); + do { ::Sleep(50); - } + QCoreApplication::processEvents(); + } while (!future.isFinished()); bool res = future.result(); return res; @@ -300,10 +300,10 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool new MethodCallback(this, &InstallationManager::report7ZipError) ); }); - while (!future.isFinished()) { - QCoreApplication::processEvents(); + do { ::Sleep(50); - } + QCoreApplication::processEvents(); + } while (!future.isFinished()); if (!future.result()) { throw MyException(QString("extracting failed (%1)").arg(m_ArchiveHandler->getLastError())); } @@ -444,7 +444,7 @@ void InstallationManager::updateProgressFile(QString const &fileName) void InstallationManager::doProgressUpdate(float percentage) { if (m_InstallationProgress != nullptr) { - QMetaObject::invokeMethod(this, "setProgressValue", Qt::QueuedConnection, Q_ARG(int, static_cast(percentage * 100.0))); + m_InstallationProgress->setValue(static_cast(percentage * 100.0)); if (m_InstallationProgress->wasCanceled()) { m_ArchiveHandler->cancel(); @@ -453,13 +453,6 @@ void InstallationManager::doProgressUpdate(float percentage) } } -void InstallationManager::setProgressValue(int percentage) -{ - if (m_InstallationProgress != nullptr) { - m_InstallationProgress->setValue(percentage); - } -} - void InstallationManager::doProgressFileUpdate(QString const fileName) { if (m_InstallationProgress != nullptr) { @@ -616,10 +609,10 @@ bool InstallationManager::doInstall(GuessedValue &modName, QString game new MethodCallback(this, &InstallationManager::report7ZipError) ); }); - while (!future.isFinished()) { - QCoreApplication::processEvents(); + do { ::Sleep(50); - } + QCoreApplication::processEvents(); + } while (!future.isFinished()); if (!future.result()) { if (m_ArchiveHandler->getLastError() == Archive::ERROR_EXTRACT_CANCELLED) { return false; diff --git a/src/installationmanager.h b/src/installationmanager.h index 851390a7..84ccd1a4 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -186,7 +186,6 @@ private slots: void doProgressUpdate(float percentage); void doProgressFileUpdate(const QString fileName); - void setProgressValue(int percentage); private: -- cgit v1.3.1 From a29192f9b2fe6086f0640864b7e70d38c3d3dc2b Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 4 May 2018 16:16:32 -0500 Subject: Incorporate uibase translations into organizer --- src/CMakeLists.txt | 3 +- src/organizer_en.ts | 238 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 182 insertions(+), 59 deletions(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 41891732..8463f098 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -255,7 +255,8 @@ FIND_PACKAGE(Qt5Qml REQUIRED) FIND_PACKAGE(Qt5LinguistTools) QT5_WRAP_UI(organizer_UIHDRS ${organizer_UIS}) QT5_ADD_RESOURCES(organizer_RCCPPS ${organizer_QRCS}) -QT5_CREATE_TRANSLATION(organizer_translations_qm ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/organizer_en.ts) +SET(mo_translation_sources ${CMAKE_SOURCE_DIR}/src ${project_path}/uibase/src) +QT5_CREATE_TRANSLATION(organizer_translations_qm ${mo_translation_sources} ${CMAKE_SOURCE_DIR}/src/organizer_en.ts) ADD_CUSTOM_TARGET(translations DEPENDS ${organizer_translations_qm}) INCLUDE_DIRECTORIES(${Qt5Declarative_INCLUDES}) diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 8c36e720..4a5593fa 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -1093,33 +1093,43 @@ Right now the only case I know of where this needs to be overwritten is for the FindDialog + Find + Find what: + + Search term + + Find next occurence from current file position. + &Find Next + + + @@ -1208,82 +1218,82 @@ p, li { white-space: pre-wrap; } - + failed to create backup - + Mod Name - + Name - + Invalid name - + The name you entered is invalid, please enter a different one. - + File format "%1" not supported - + None of the available installer plugins were able to handle that archive - + no error - + 7z.dll not found - + 7z.dll isn't valid - + archive not found - + failed to open archive - + unsupported archive type - + internal library error - + archive invalid - + unknown archive error @@ -1332,6 +1342,50 @@ p, li { white-space: pre-wrap; } + + MOBase::TextViewer + + + Save changes? + + + + + Do you want to save changes to %1? + + + + + failed to write to %1 + + + + + file not found: %1 + + + + + Save + + + + + MOBase::TutorialControl + + + Tutorial failed to start, please check "mo_interface.log" for details. + + + + + MOBase::TutorialManager + + + tutorial manager not set up yet + + + MainWindow @@ -2156,7 +2210,7 @@ Please enter a name: - + Are you sure? @@ -2473,13 +2527,13 @@ You can also use online editors and converters instead. - + Exception: - + Unknown exception @@ -2658,181 +2712,181 @@ You can also use online editors and converters instead. - + 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 @@ -3861,6 +3915,14 @@ p, li { white-space: pre-wrap; } + + NXMUrl + + + invalid nxm-link: %1 + + + NexusInterface @@ -4908,6 +4970,8 @@ If the folder was still in use, restart MO and try again. + + Error @@ -5068,6 +5132,38 @@ Start elevated anyway? (you will be asked if you want to allow ModOrganizer.exe failed to spawn "%1": %2 + + + failed to open temporary file + + + + + removal of "%1" failed: %2 + + + + + removal of "%1" failed + + + + + "%1" doesn't exist (remove) + + + + + + failed to create directory "%1" + + + + + + failed to copy "%1" to "%2" + + QueryOverwriteDialog @@ -5107,6 +5203,19 @@ Start elevated anyway? (you will be asked if you want to allow ModOrganizer.exe + + QuestionBoxMemory + + + Remember selection + + + + + Remember selection only for + + + SaveTextAsDialog @@ -5978,6 +6087,19 @@ For the other games this is not a sufficient replacement for AI! + + TextViewer + + + Log Viewer + + + + + Placeholder + + + TransferSavesDialog -- cgit v1.3.1 From a76d9876472c6bc43cc78a5c6642d58d364bd843 Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 4 May 2018 17:28:58 -0500 Subject: Update the about dialog --- src/aboutdialog.ui | 40 ++++++++++++------ src/organizer_en.ts | 114 ++++++++++++++++++++++++++++------------------------ 2 files changed, 90 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/aboutdialog.ui b/src/aboutdialog.ui index fc2f3290..57517623 100644 --- a/src/aboutdialog.ui +++ b/src/aboutdialog.ui @@ -153,8 +153,8 @@ Thanks - - + + Current Maintainers @@ -167,12 +167,12 @@ - LePresidente (Project Lead) + LePresidente (Project Lead) - AL12 + AL12 @@ -195,8 +195,29 @@
- + + + Major Contributors + + + + + + QAbstractItemView::NoSelection + + + + AnyOldName3 (Python Plugins) + + + + + + + + + Translators @@ -317,8 +338,8 @@ - - + + Other Supporters && Contributors @@ -328,11 +349,6 @@ QAbstractItemView::NoSelection - - - AnyOldName3 - - blacksol diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 4a5593fa..851b5d71 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -29,6 +29,11 @@ Current Maintainers + + + LePresidente (Project Lead) + + AL12 @@ -36,61 +41,66 @@ + Other Supporters && Contributors + + + + Translators - + Cyb3r (Dutch) - + fruttyx (French) - + Yoplala (French) - + Faron (German) - + Mordan (Greek) - + Yoosk (Polish) - + Brgodfx (Portuguese) - + Jax (Swedish) - + ...and all other Transifex contributors! - - Other Supporters && Contributors + + Major Contributors - + Close @@ -4692,6 +4702,46 @@ p, li { white-space: pre-wrap; } QObject + + + + + + Error + + + + + failed to open temporary file + + + + + removal of "%1" failed: %2 + + + + + removal of "%1" failed + + + + + "%1" doesn't exist (remove) + + + + + + failed to create directory "%1" + + + + + + failed to copy "%1" to "%2" + + Failed to save custom categories @@ -4969,14 +5019,6 @@ If the folder was still in use, restart MO and try again. Failed to set up proxy-dll loading - - - - - - Error - - @@ -5132,38 +5174,6 @@ Start elevated anyway? (you will be asked if you want to allow ModOrganizer.exe failed to spawn "%1": %2 - - - failed to open temporary file - - - - - removal of "%1" failed: %2 - - - - - removal of "%1" failed - - - - - "%1" doesn't exist (remove) - - - - - - failed to create directory "%1" - - - - - - failed to copy "%1" to "%2" - - QueryOverwriteDialog -- cgit v1.3.1 From 9998727dbac0eb77b597cb299576d9ca7561b7cf Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 4 May 2018 18:26:57 -0500 Subject: Vastly improved plugin index generation and sorting --- src/pluginlist.cpp | 56 +++++++++++++++++---------------------------- src/pluginlist.h | 3 ++- src/pluginlistsortproxy.cpp | 4 ++-- 3 files changed, 25 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index f4bdf418..fe654c7e 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -485,23 +485,9 @@ int PluginList::enabledCount() const return enabled; } -int PluginList::getIndexPriority(int index) const +QString PluginList::getIndexPriority(int index) const { - int numESLs = 0; - std::vector sortESPs(m_ESPs); - std::sort(sortESPs.begin(), sortESPs.end()); - for (auto sortedESP : sortESPs) { - if (sortedESP.m_LoadOrder == m_ESPs[index].m_LoadOrder) - break; - if ((sortedESP.m_IsLight || sortedESP.m_IsLightFlagged) && sortedESP.m_LoadOrder != -1) - ++numESLs; - } - if (m_ESPs[index].m_IsLight || m_ESPs[index].m_IsLightFlagged) { - int ESLpos = 254 + ((numESLs + 1) / 4096); - return ESLpos + (numESLs % 4096); - } else { - return m_ESPs[index].m_LoadOrder - numESLs; - } + return m_ESPs[index].m_Index; } bool PluginList::isESPLocked(int index) const @@ -751,6 +737,24 @@ void PluginList::updateIndices() m_ESPsByName[m_ESPs[i].m_Name.toLower()] = i; m_ESPsByPriority.at(static_cast(m_ESPs[i].m_Priority)) = i; } + + int numESLs = 0; + int numSkipped = 0; + for (int l = 0; l < m_ESPs.size(); ++l) { + int i = m_ESPsByPriority.at(l); + if (!m_ESPs[i].m_Enabled) { + m_ESPs[i].m_Index = QString(); + ++numSkipped; + continue; + } + if (m_ESPs[i].m_IsLight || m_ESPs[i].m_IsLightFlagged) { + int ESLpos = 254 + ((numESLs + 1) / 4096); + m_ESPs[i].m_Index = QString("%1:%2").arg(ESLpos, 2, 16, QChar('0')).arg((numESLs) % 4096, 3, 16, QChar('0')).toUpper(); + ++numESLs; + } else { + m_ESPs[i].m_Index = QString("%1").arg(l - numESLs - numSkipped, 2, 16, QChar('0')).toUpper(); + } + } } @@ -808,25 +812,7 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const return m_ESPs[index].m_Priority; } break; case COL_MODINDEX: { - if (m_ESPs[index].m_LoadOrder == -1) { - return QString(); - } else { - int numESLs = 0; - std::vector sortESPs(m_ESPs); - std::sort(sortESPs.begin(), sortESPs.end()); - for (auto sortedESP: sortESPs) { - if (sortedESP.m_LoadOrder == m_ESPs[index].m_LoadOrder) - break; - if ((sortedESP.m_IsLight || sortedESP.m_IsLightFlagged) && sortedESP.m_LoadOrder != -1) - ++numESLs; - } - if (m_ESPs[index].m_IsLight || m_ESPs[index].m_IsLightFlagged) { - int ESLpos = 254 + ((numESLs+1) / 4096); - return QString("%1:%2").arg(ESLpos, 2, 16, QChar('0')).arg((numESLs)%4096, 3, 16, QChar('0')).toUpper(); - } else { - return QString("%1").arg(m_ESPs[index].m_LoadOrder - numESLs, 2, 16, QChar('0')).toUpper(); - } - } + return m_ESPs[index].m_Index; } break; default: { return QVariant(); diff --git a/src/pluginlist.h b/src/pluginlist.h index 6a53614a..b3b0ee13 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -194,7 +194,7 @@ public: QString getName(int index) const { return m_ESPs.at(index).m_Name; } int getPriority(int index) const { return m_ESPs.at(index).m_Priority; } - int getIndexPriority(int index) const; + QString getIndexPriority(int index) const; bool isESPLocked(int index) const; void lockESPIndex(int index, bool lock); @@ -278,6 +278,7 @@ private: bool m_Enabled; bool m_ForceEnabled; int m_Priority; + QString m_Index; int m_LoadOrder; FILETIME m_Time; QString m_OriginName; diff --git a/src/pluginlistsortproxy.cpp b/src/pluginlistsortproxy.cpp index b844e1d8..e09e64a5 100644 --- a/src/pluginlistsortproxy.cpp +++ b/src/pluginlistsortproxy.cpp @@ -100,8 +100,8 @@ bool PluginListSortProxy::lessThan(const QModelIndex &left, } } break; case PluginList::COL_MODINDEX: { - int leftVal = plugins->isEnabled(left.row()) ? plugins->getIndexPriority(left.row()) : -1; - int rightVal = plugins->isEnabled(right.row()) ? plugins->getIndexPriority(right.row()) : -1; + QString leftVal = plugins->getIndexPriority(left.row()); + QString rightVal = plugins->getIndexPriority(right.row()); return leftVal < rightVal; } break; default: { -- cgit v1.3.1 From 9ae442810b479dd61747fea03eceae500a6c2411 Mon Sep 17 00:00:00 2001 From: Silarn Date: Fri, 4 May 2018 19:00:55 -0500 Subject: Make sure to update the index when data is changed --- src/pluginlist.cpp | 6 ++++++ src/pluginlist.h | 5 +++++ 2 files changed, 11 insertions(+) (limited to 'src') diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index fe654c7e..a6ae8fa7 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -76,6 +76,7 @@ PluginList::PluginList(QObject *parent) : QAbstractItemModel(parent) , m_FontMetrics(QFont()) { + connect(this, SIGNAL(writePluginsList()), this, SLOT(generatePluginIndexes())); } PluginList::~PluginList() @@ -738,6 +739,11 @@ void PluginList::updateIndices() m_ESPsByPriority.at(static_cast(m_ESPs[i].m_Priority)) = i; } + generatePluginIndexes(); +} + +void PluginList::generatePluginIndexes() +{ int numESLs = 0; int numSkipped = 0; for (int l = 0; l < m_ESPs.size(); ++l) { diff --git a/src/pluginlist.h b/src/pluginlist.h index b3b0ee13..f6745aa8 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -257,6 +257,11 @@ public slots: */ void managedGameChanged(MOBase::IPluginGame const *gamePlugin); + /** + * @brief Generate the plugin indexes because something was changed + **/ + void generatePluginIndexes(); + signals: /** -- cgit v1.3.1 From fe33d7af43465f0ee46eab8353bb4f3d4b145331 Mon Sep 17 00:00:00 2001 From: Silarn Date: Sat, 5 May 2018 01:28:09 -0500 Subject: Rework the existing file download dialog to pause the download while waiting - Also fixes direct calls to downloadFinished --- src/downloadmanager.cpp | 51 ++++++++++++++++++++++++++++++++----------------- src/downloadmanager.h | 4 ++-- 2 files changed, 36 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index a60057ef..0da5ced3 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -381,14 +381,6 @@ bool DownloadManager::addDownload(QNetworkReply *reply, const QStringList &URLs, baseName = dispoName; } } - if (QFile::exists(m_OutputDirectory + "/" + baseName) && - (QMessageBox::question(nullptr, tr("Download again?"), tr("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."), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)) { - removePending(gameName, modID, fileID); - delete newDownload; - return false; - } newDownload->setName(getDownloadFileName(baseName), false); startDownload(reply, newDownload, false); @@ -434,7 +426,6 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl } connect(newDownload->m_Reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64))); - connect(newDownload->m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished())); connect(newDownload->m_Reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError))); connect(newDownload->m_Reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead())); connect(newDownload->m_Reply, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged())); @@ -449,11 +440,33 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl emit update(-1); emit downloadAdded(); - if (reply->isFinished()) { - // it's possible the download has already finished before this function ran - downloadFinished(); + if (QFile::exists(m_OutputDirectory + "/" + newDownload->m_FileName)) { + setState(newDownload, STATE_PAUSING); + QCoreApplication::processEvents(); + if (QMessageBox::question(nullptr, tr("Download again?"), tr("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."), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { + if (reply->isFinished()) + setState(newDownload, STATE_CANCELED); + else + setState(newDownload, STATE_CANCELING); + } else { + newDownload->setName(getDownloadFileName(newDownload->m_FileName, true), true); + if (newDownload->m_State == STATE_PAUSED) + resumeDownload(indexByName(newDownload->m_FileName)); + else + setState(newDownload, STATE_DOWNLOADING); + } + } + + QCoreApplication::processEvents(); + + if (newDownload->m_State != STATE_DOWNLOADING && reply->isFinished()) { + downloadFinished(indexByName(newDownload->m_FileName)); + return; } } + connect(newDownload->m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished())); } @@ -991,10 +1004,10 @@ void DownloadManager::markUninstalled(QString fileName) } -QString DownloadManager::getDownloadFileName(const QString &baseName) const +QString DownloadManager::getDownloadFileName(const QString &baseName, bool rename) const { QString fullPath = m_OutputDirectory + "/" + baseName; - if (QFile::exists(fullPath)) { + if (QFile::exists(fullPath) && rename) { int i = 1; while (QFile::exists(QString("%1/%2_%3").arg(m_OutputDirectory).arg(i).arg(baseName))) { ++i; @@ -1035,6 +1048,7 @@ void DownloadManager::setState(DownloadManager::DownloadInfo *info, DownloadMana case STATE_PAUSED: case STATE_ERROR: { info->m_Reply->abort(); + info->m_Output.close(); } break; case STATE_CANCELED: { info->m_Reply->abort(); @@ -1450,11 +1464,14 @@ void DownloadManager::nxmRequestFailed(QString gameName, int modID, int fileID, } -void DownloadManager::downloadFinished() +void DownloadManager::downloadFinished(int index) { - int index = 0; + DownloadInfo *info; + if (index) + info = m_ActiveDownloads[index]; + else + info = findDownload(this->sender(), &index); - DownloadInfo *info = findDownload(this->sender(), &index); if (info != nullptr) { QNetworkReply *reply = info->m_Reply; QByteArray data; diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 6a110a41..6e3f9c2c 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -439,7 +439,7 @@ private slots: void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); void downloadReadyRead(); - void downloadFinished(); + void downloadFinished(int index = 0); void downloadError(QNetworkReply::NetworkError error); void metaDataChanged(); void directoryChanged(const QString &dirctory); @@ -460,7 +460,7 @@ public: * * @return Unique(ish) name */ - QString getDownloadFileName(const QString &baseName) const; + QString getDownloadFileName(const QString &baseName, bool rename = false) const; private: -- cgit v1.3.1 From 5f8bbaae2aa8486859fedc12ec0372b0cf83d709 Mon Sep 17 00:00:00 2001 From: Silarn Date: Sun, 6 May 2018 10:18:23 -0500 Subject: Make sure project_path is set before trying to use it --- src/CMakeLists.txt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8463f098..77ee4dfc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -240,6 +240,13 @@ SOURCE_GROUP(Source FILES ${organizer_SRCS}) SOURCE_GROUP(Headers FILES ${organizer_HDRS}) SOURCE_GROUP(UI FILES ${organizer_UIS}) +# MO projects +SET(default_project_path "${CMAKE_SOURCE_DIR}/..") +GET_FILENAME_COMPONENT(${default_project_path} ${default_project_path} REALPATH) + +SET(project_path "${default_project_path}" CACHE PATH "path to the other mo projects") +#TODO this should not be a hardcoded path +SET(lib_path "${project_path}/../../install/libs") # Qt5 SET(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -276,16 +283,6 @@ LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) FIND_PACKAGE(zlib REQUIRED) # TODO FindZlib doesn't find the static zlib library - -# MO projects -SET(default_project_path "${CMAKE_SOURCE_DIR}/..") -GET_FILENAME_COMPONENT(${default_project_path} ${default_project_path} REALPATH) - -SET(project_path "${default_project_path}" CACHE PATH "path to the other mo projects") -#TODO this should not be a hardcoded path -SET(lib_path "${project_path}/../../install/libs") - - INCLUDE_DIRECTORIES(${project_path}/uibase/src ${project_path}/bsatk/src ${project_path}/esptk/src -- cgit v1.3.1 From 6359d7d23f4137a35dfd241a0daa22c6a885e426 Mon Sep 17 00:00:00 2001 From: Silarn Date: Sun, 6 May 2018 17:21:35 -0500 Subject: Fixes for downloads and installs --- src/downloadmanager.cpp | 49 +++++++++++++++++++++++++-------------------- src/downloadmanager.h | 4 ++-- src/installationmanager.cpp | 43 +++++++++++++++++---------------------- src/installationmanager.h | 7 ++----- 4 files changed, 49 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 0da5ced3..2ab56fab 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -145,8 +145,10 @@ void DownloadManager::DownloadInfo::setName(QString newName, bool renameFile) QString oldMetaFileName = QString("%1.meta").arg(m_FileName); m_FileName = QFileInfo(newName).fileName(); if ((m_State == DownloadManager::STATE_STARTED) || - (m_State == DownloadManager::STATE_DOWNLOADING)) { + (m_State == DownloadManager::STATE_DOWNLOADING) || + (m_State == DownloadManager::STATE_PAUSED)) { newName.append(UNFINISHED); + oldMetaFileName = QString("%1%2.meta").arg(m_FileName).arg(UNFINISHED); } if (renameFile) { if ((newName != m_Output.fileName()) && !m_Output.rename(newName)) { @@ -154,10 +156,9 @@ void DownloadManager::DownloadInfo::setName(QString newName, bool renameFile) return; } - QFile metaFile(oldMetaFileName); - if (metaFile.exists()) { + QFile metaFile(QFileInfo(newName).path() + "/" + oldMetaFileName); + if (metaFile.exists()) metaFile.rename(newName.mid(0).append(".meta")); - } } if (!m_Output.isOpen()) { // can't set file name if it's open @@ -457,7 +458,9 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl else setState(newDownload, STATE_DOWNLOADING); } - } + } else + connect(newDownload->m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished())); + QCoreApplication::processEvents(); @@ -465,8 +468,8 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl downloadFinished(indexByName(newDownload->m_FileName)); return; } - } - connect(newDownload->m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished())); + } else + connect(newDownload->m_Reply, SIGNAL(finished()), this, SLOT(downloadFinished())); } @@ -496,11 +499,11 @@ void DownloadManager::addNXMDownload(const QString &url) for (DownloadInfo *download : m_ActiveDownloads) { if (download->m_FileInfo->modID == nxmInfo.modId() && download->m_FileInfo->fileID == nxmInfo.fileId()) { if (download->m_State == STATE_DOWNLOADING || download->m_State == STATE_PAUSED || download->m_State == STATE_STARTED) { - qDebug("download requested is already started (mod: %s, file: %s)", qPrintable(download->m_FileInfo->modName), + qDebug("download requested is already started (mod: %s, file: %s)", qPrintable(download->m_FileInfo->modID), qPrintable(download->m_FileInfo->fileName)); - QMessageBox::information(nullptr, tr("Already Started"), tr("There is already a download started for this file (%2).") - .arg(download->m_FileInfo->modName).arg(download->m_FileInfo->name), QMessageBox::Ok); + QMessageBox::information(nullptr, tr("Already Started"), tr("There is already a download started for this file (mod: %1, file: %2).") + .arg(download->m_FileInfo->modName).arg(download->m_FileInfo->fileName), QMessageBox::Ok); return; } } @@ -702,6 +705,8 @@ void DownloadManager::resumeDownloadInt(int index) 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); @@ -1105,11 +1110,17 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) int oldProgress = info->m_Progress.first; info->m_Progress.first = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal); + int elapsed = info->m_StartTime.elapsed(); + std::get<0>(info->m_SpeedDiff) = bytesReceived - std::get<2>(info->m_SpeedDiff); + std::get<1>(info->m_SpeedDiff) = elapsed - std::get<3>(info->m_SpeedDiff); + std::get<2>(info->m_SpeedDiff) = bytesReceived; + std::get<3>(info->m_SpeedDiff) = elapsed; + + double calc = ((double)std::get<0>(info->m_SpeedDiff)) / (((double)(std::get<1>(info->m_SpeedDiff)) / 5000.0)); + std::get<4>(info->m_SpeedDiff) = ((calc*0.5) + (std::get<4>(info->m_SpeedDiff)*1.5)) / 2; + // calculate the download speed - double speed = (bytesReceived - std::get<1>(info->m_SpeedDiff)) * 1000.0 / // Calculate with the data transferred in the last 5 seconds... - (std::get<1>(info->m_SpeedDiff) ? // If we are past 5 seconds - (5 * 1000) + (info->m_StartTime.elapsed() - std::get<2>(info->m_SpeedDiff)) : // Divide by 5 seconds + the diff between chunks - info->m_StartTime.elapsed()); // Else just use the current elapsed time + double speed = (std::get<4>(info->m_SpeedDiff) * 1000.0) / (5 * 1000); QString unit; if (speed < 1024) { @@ -1126,12 +1137,6 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) info->m_Progress.second = QString::fromLatin1("%1% - %2 %3").arg(info->m_Progress.first).arg(speed, 3, 'f', 1).arg(unit); - if (info->m_StartTime.elapsed() >= 5 * 1000) { - std::get<1>(info->m_SpeedDiff) = std::get<1>(info->m_SpeedDiff) + bytesReceived - std::get<0>(info->m_SpeedDiff); - std::get<2>(info->m_SpeedDiff) = info->m_StartTime.elapsed(); - } - std::get<0>(info->m_SpeedDiff) = bytesReceived; - TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal); emit update(index); } @@ -1551,7 +1556,7 @@ void DownloadManager::downloadFinished(int index) QString newName = getFileNameFromNetworkReply(reply); QString oldName = QFileInfo(info->m_Output).fileName(); - if (!newName.isEmpty() && (newName != oldName)) { + 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 @@ -1594,7 +1599,7 @@ void DownloadManager::metaDataChanged() DownloadInfo *info = findDownload(this->sender(), &index); if (info != nullptr) { QString newName = getFileNameFromNetworkReply(info->m_Reply); - if (!newName.isEmpty() && (newName != info->m_FileName)) { + if (!newName.isEmpty() && (info->m_FileName.isEmpty())) { info->setName(getDownloadFileName(newName), true); refreshAlphabeticalTranslation(); if (!info->m_Output.isOpen() && !info->m_Output.open(QIODevice::WriteOnly | QIODevice::Append)) { diff --git a/src/downloadmanager.h b/src/downloadmanager.h index 6e3f9c2c..98f5e468 100644 --- a/src/downloadmanager.h +++ b/src/downloadmanager.h @@ -76,7 +76,7 @@ private: QTime m_StartTime; qint64 m_PreResumeSize; std::pair m_Progress; - std::tuple m_SpeedDiff; + std::tuple m_SpeedDiff; DownloadState m_State; int m_CurrentUrl; QStringList m_Urls; @@ -114,7 +114,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)) {} + DownloadInfo() : m_TotalSize(0), m_ReQueried(false), m_Hidden(false), m_SpeedDiff(std::tuple(0,0,0,0,0)) {} }; public: diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 27c83787..b7ec3d68 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -95,9 +95,6 @@ InstallationManager::InstallationManager() if (!m_ArchiveHandler->isValid()) { throw MyException(getErrorString(m_ArchiveHandler->getLastError())); } - - connect(this, SIGNAL(progressUpdate(float)), this, (SLOT(doProgressUpdate(float))), Qt::QueuedConnection); - connect(this, SIGNAL(progressUpdate(const QString)), this, (SLOT(doProgressFileUpdate(const QString))), Qt::QueuedConnection); } InstallationManager::~InstallationManager() @@ -203,9 +200,12 @@ bool InstallationManager::unpackSingleFile(const QString &fileName) ); }); do { - ::Sleep(50); + if (m_Progress != m_InstallationProgress->value()) + m_InstallationProgress->setValue(m_Progress); + if (m_ProgressFile != m_InstallationProgress->labelText()) + m_InstallationProgress->setLabelText(m_ProgressFile); QCoreApplication::processEvents(); - } while (!future.isFinished()); + } while (!future.isFinished() || m_InstallationProgress->isVisible()); bool res = future.result(); return res; @@ -301,9 +301,12 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig, bool ); }); do { - ::Sleep(50); + if (m_Progress != m_InstallationProgress->value()) + m_InstallationProgress->setValue(m_Progress); + if (m_ProgressFile != m_InstallationProgress->labelText()) + m_InstallationProgress->setLabelText(m_ProgressFile); QCoreApplication::processEvents(); - } while (!future.isFinished()); + } while (!future.isFinished() || m_InstallationProgress->isVisible()); if (!future.result()) { throw MyException(QString("extracting failed (%1)").arg(m_ArchiveHandler->getLastError())); } @@ -430,21 +433,9 @@ DirectoryTree::Node *InstallationManager::getSimpleArchiveBase(DirectoryTree *da void InstallationManager::updateProgress(float percentage) -{ - emit progressUpdate(percentage); -} - - -void InstallationManager::updateProgressFile(QString const &fileName) -{ - emit progressUpdate(fileName); -} - - -void InstallationManager::doProgressUpdate(float percentage) { if (m_InstallationProgress != nullptr) { - m_InstallationProgress->setValue(static_cast(percentage * 100.0)); + m_Progress = static_cast(percentage * 100.0); if (m_InstallationProgress->wasCanceled()) { m_ArchiveHandler->cancel(); @@ -453,11 +444,10 @@ void InstallationManager::doProgressUpdate(float percentage) } } -void InstallationManager::doProgressFileUpdate(QString const fileName) + +void InstallationManager::updateProgressFile(QString const &fileName) { - if (m_InstallationProgress != nullptr) { - m_InstallationProgress->setLabelText(fileName); - } + m_ProgressFile = fileName; } @@ -610,7 +600,10 @@ bool InstallationManager::doInstall(GuessedValue &modName, QString game ); }); do { - ::Sleep(50); + if (m_Progress != m_InstallationProgress->value()) + m_InstallationProgress->setValue(m_Progress); + if (m_ProgressFile != m_InstallationProgress->labelText()) + m_InstallationProgress->setLabelText(m_ProgressFile); QCoreApplication::processEvents(); } while (!future.isFinished()); if (!future.result()) { diff --git a/src/installationmanager.h b/src/installationmanager.h index 84ccd1a4..e17aee17 100644 --- a/src/installationmanager.h +++ b/src/installationmanager.h @@ -182,11 +182,6 @@ signals: void progressUpdate(float percentage); void progressUpdate(QString const fileName); -private slots: - - void doProgressUpdate(float percentage); - void doProgressFileUpdate(const QString fileName); - private: struct ByPriority { @@ -217,6 +212,8 @@ private: QString m_CurrentFile; QProgressDialog *m_InstallationProgress { nullptr }; + int m_Progress; + QString m_ProgressFile; std::set m_TempFilesToDelete; -- cgit v1.3.1 From 7d9514d497924774b2f9eff55b6529f318d2c418 Mon Sep 17 00:00:00 2001 From: Silarn Date: Sun, 6 May 2018 17:22:06 -0500 Subject: Translation updates --- src/organizer_en.ts | 249 +++++++++++++++++++++++++--------------------------- 1 file changed, 122 insertions(+), 127 deletions(-) (limited to 'src') diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 851b5d71..27b077ac 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -34,69 +34,64 @@ LePresidente (Project Lead) - - - AL12 - - - Other Supporters && Contributors + Major Contributors - + Translators - + Cyb3r (Dutch) - + fruttyx (French) - + Yoplala (French) - + Faron (German) - + Mordan (Greek) - + Yoosk (Polish) - + Brgodfx (Portuguese) - + Jax (Swedish) - + ...and all other Transifex contributors! - - Major Contributors + + Other Supporters && Contributors @@ -625,276 +620,276 @@ p, li { white-space: pre-wrap; } DownloadManager - + failed to rename "%1" to "%2" - + Memory allocation error (in refreshing directory). - - Download again? + + failed to download %1: could not open output file: %2 - - 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. + + Download again? - - failed to download %1: could not open output file: %2 + + 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 (%2). + + 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) - + failed to re-open %1 @@ -1212,98 +1207,98 @@ p, li { white-space: pre-wrap; } InstallationManager - + Password required - + Password - + Extracting files - + failed to create backup - + Mod Name - + Name - + Invalid name - + The name you entered is invalid, please enter a different one. - + File format "%1" not supported - + None of the available installer plugins were able to handle that archive - + no error - + 7z.dll not found - + 7z.dll isn't valid - + archive not found - + failed to open archive - + unsupported archive type - + internal library error - + archive invalid - + unknown archive error @@ -4237,120 +4232,120 @@ 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. - + 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 @@ -5125,12 +5120,12 @@ If the folder was still in use, restart MO and try again. - + failed to access %1 - + failed to set file time %1 -- cgit v1.3.1 From fc74aa90d13cbc1bbc0f61f634d0457f207ec455 Mon Sep 17 00:00:00 2001 From: Silarn Date: Sun, 6 May 2018 17:31:06 -0500 Subject: Updating about dialog with additional supporters --- src/aboutdialog.ui | 10 ++++++++++ src/organizer_en.ts | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/aboutdialog.ui b/src/aboutdialog.ui index 57517623..7e90ec32 100644 --- a/src/aboutdialog.ui +++ b/src/aboutdialog.ui @@ -384,6 +384,11 @@ Gopher + + + GrantSP + + GSDFan @@ -394,6 +399,11 @@ ogrotten + + + outdatedtv + + Schilduin diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 27b077ac..09c100cf 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -95,7 +95,7 @@ - + Close -- cgit v1.3.1 From 02d3f8182984c86b6a1e778e9f32a118f6fe02fe Mon Sep 17 00:00:00 2001 From: Silarn Date: Mon, 7 May 2018 01:29:00 -0500 Subject: Changes to allow installer_NCC to use the game source set in the DL meta --- src/installationmanager.cpp | 2 +- src/organizercore.cpp | 9 +++++++++ src/organizercore.h | 1 + src/organizerproxy.cpp | 5 +++++ src/organizerproxy.h | 1 + 5 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index b7ec3d68..d1922cf9 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -819,7 +819,7 @@ bool InstallationManager::install(const QString &fileName, = installerCustom->supportedExtensions(); if (installerExt.find(fileInfo.suffix()) != installerExt.end()) { installResult - = installerCustom->install(modName, fileName, version, modID); + = installerCustom->install(modName, gameName, fileName, version, modID); unsigned int idx = ModInfo::getIndex(modName); if (idx != UINT_MAX) { ModInfo::Ptr info = ModInfo::getByIndex(idx); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index d1b1fcd7..35486f98 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -789,6 +789,15 @@ MOBase::IModInterface *OrganizerCore::getMod(const QString &name) const return index == UINT_MAX ? nullptr : ModInfo::getByIndex(index).data(); } +MOBase::IPluginGame *OrganizerCore::getGame(const QString &name) const +{ + for (IPluginGame *game : m_PluginContainer->plugins()) { + if (game->gameShortName().compare(name, Qt::CaseInsensitive) == 0) + return game; + } + return nullptr; +} + MOBase::IModInterface *OrganizerCore::createMod(GuessedValue &name) { bool merge = false; diff --git a/src/organizercore.h b/src/organizercore.h index 76c286be..28ecef48 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -184,6 +184,7 @@ public: QString basePath() const; MOBase::VersionInfo appVersion() const; MOBase::IModInterface *getMod(const QString &name) const; + MOBase::IPluginGame *getGame(const QString &gameName) const; MOBase::IModInterface *createMod(MOBase::GuessedValue &name); bool removeMod(MOBase::IModInterface *mod); void modDataChanged(MOBase::IModInterface *mod); diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 77d61a52..8e0bc95b 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -60,6 +60,11 @@ IModInterface *OrganizerProxy::getMod(const QString &name) const return m_Proxied->getMod(name); } +IPluginGame *OrganizerProxy::getGame(const QString &gameName) const +{ + return m_Proxied->getGame(gameName); +} + IModInterface *OrganizerProxy::createMod(MOBase::GuessedValue &name) { return m_Proxied->createMod(name); diff --git a/src/organizerproxy.h b/src/organizerproxy.h index cebb98ac..b8eb9b82 100644 --- a/src/organizerproxy.h +++ b/src/organizerproxy.h @@ -22,6 +22,7 @@ public: virtual QString basePath() const; virtual MOBase::VersionInfo appVersion() const; virtual MOBase::IModInterface *getMod(const QString &name) const; + virtual MOBase::IPluginGame *getGame(const QString &gameName) const; virtual MOBase::IModInterface *createMod(MOBase::GuessedValue &name); virtual bool removeMod(MOBase::IModInterface *mod); virtual void modDataChanged(MOBase::IModInterface *mod); -- cgit v1.3.1 From 44111d12c181ff062d7936480fdc380e33232e0e Mon Sep 17 00:00:00 2001 From: Silarn Date: Mon, 7 May 2018 17:20:33 -0500 Subject: Remove a pointless error when the game doesn't have a script extender --- src/loadmechanism.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/loadmechanism.cpp b/src/loadmechanism.cpp index 7ad36ac7..ebbb5de3 100644 --- a/src/loadmechanism.cpp +++ b/src/loadmechanism.cpp @@ -126,7 +126,7 @@ void LoadMechanism::deactivateScriptExtender() IPluginGame const *game = qApp->property("managed_game").value(); ScriptExtender *extender = game->feature(); if (extender == nullptr) { - throw MyException(QObject::tr("game doesn't support a script extender")); + return; } QDir pluginsDir(game->gameDirectory().absolutePath() + "/data/" + extender->PluginPath()); @@ -193,7 +193,7 @@ void LoadMechanism::activateScriptExtender() IPluginGame const *game = qApp->property("managed_game").value(); ScriptExtender *extender = game->feature(); if (extender == nullptr) { - throw MyException(QObject::tr("game doesn't support a script extender")); + return; } QDir pluginsDir(game->gameDirectory().absolutePath() + "/data/" + extender->PluginPath()); -- cgit v1.3.1