summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJonathan Feenstra <26406078+JonathanFeenstra@users.noreply.github.com>2025-08-18 07:55:52 +0200
committerGitHub <noreply@github.com>2025-08-18 07:55:52 +0200
commitd52fcccb831b63dc1add068084369543d0a45246 (patch)
tree3d3009a7e0ad3b477b64b349a1bdc7fd1e0d5f62 /src
parent6c64236e2e7a8e2883b7656657e0fac329d5621b (diff)
Add author and uploader columns to mod list (#2269)
Diffstat (limited to 'src')
-rw-r--r--src/downloadlistview.cpp13
-rw-r--r--src/downloadlistview.h2
-rw-r--r--src/downloadmanager.cpp46
-rw-r--r--src/downloadmanager.h2
-rw-r--r--src/downloadstab.cpp2
-rw-r--r--src/mainwindow.cpp6
-rw-r--r--src/modinfo.h30
-rw-r--r--src/modinfobackup.h6
-rw-r--r--src/modinfoforeign.h6
-rw-r--r--src/modinfooverwrite.h6
-rw-r--r--src/modinforegular.cpp45
-rw-r--r--src/modinforegular.h33
-rw-r--r--src/modinfoseparator.h6
-rw-r--r--src/modlist.cpp12
-rw-r--r--src/modlist.h2
-rw-r--r--src/modlistcontextmenu.cpp12
-rw-r--r--src/modlistsortproxy.cpp44
-rw-r--r--src/modlistview.cpp1
-rw-r--r--src/modlistviewactions.cpp70
-rw-r--r--src/modlistviewactions.h7
-rw-r--r--src/nexusinterface.cpp20
21 files changed, 336 insertions, 35 deletions
diff --git a/src/downloadlistview.cpp b/src/downloadlistview.cpp
index a895d2e2..f2677523 100644
--- a/src/downloadlistview.cpp
+++ b/src/downloadlistview.cpp
@@ -231,14 +231,18 @@ void DownloadListView::onCustomContextMenu(const QPoint& point)
menu.addAction(tr("Install"), [=] {
issueInstall(row);
});
- if (m_Manager->isInfoIncomplete(row))
+ if (m_Manager->isInfoIncomplete(row)) {
menu.addAction(tr("Query Info"), [=] {
issueQueryInfoMd5(row);
});
- else
+ } else {
menu.addAction(tr("Visit on Nexus"), [=] {
issueVisitOnNexus(row);
});
+ menu.addAction(tr("Visit the uploader's profile"), [=] {
+ issueVisitUploaderProfile(row);
+ });
+ }
menu.addAction(tr("Open File"), [=] {
issueOpenFile(row);
});
@@ -408,6 +412,11 @@ void DownloadListView::issueVisitOnNexus(int index)
emit visitOnNexus(index);
}
+void DownloadListView::issueVisitUploaderProfile(int index)
+{
+ emit visitUploaderProfile(index);
+}
+
void DownloadListView::issueOpenFile(int index)
{
emit openFile(index);
diff --git a/src/downloadlistview.h b/src/downloadlistview.h
index 65d98849..9fccdbc5 100644
--- a/src/downloadlistview.h
+++ b/src/downloadlistview.h
@@ -87,6 +87,7 @@ signals:
void pauseDownload(int index);
void resumeDownload(int index);
void visitOnNexus(int index);
+ void visitUploaderProfile(int index);
void openFile(int index);
void openMetaFile(int index);
void openInDownloadsFolder(int index);
@@ -105,6 +106,7 @@ private slots:
void issueRestoreToView(int index);
void issueRestoreToViewAll();
void issueVisitOnNexus(int index);
+ void issueVisitUploaderProfile(int index);
void issueOpenFile(int index);
void issueOpenMetaFile(int index);
void issueOpenInDownloadsFolder(int index);
diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp
index 52eb8c8e..ce6ba665 100644
--- a/src/downloadmanager.cpp
+++ b/src/downloadmanager.cpp
@@ -148,6 +148,9 @@ DownloadManager::DownloadInfo::createFromMeta(const QString& filePath, bool show
info->m_FileInfo->fileCategory = metaFile.value("fileCategory", 0).toInt();
info->m_FileInfo->repository = metaFile.value("repository", "Nexus").toString();
info->m_FileInfo->userData = metaFile.value("userData").toMap();
+ info->m_FileInfo->author = metaFile.value("author", "").toString();
+ info->m_FileInfo->uploader = metaFile.value("uploader", "").toString();
+ info->m_FileInfo->uploaderUrl = metaFile.value("uploaderUrl", "").toString();
info->m_Reply = nullptr;
return info;
@@ -1217,6 +1220,27 @@ void DownloadManager::visitOnNexus(int index)
}
}
+void DownloadManager::visitUploaderProfile(int index)
+{
+ if ((index < 0) || (index >= m_ActiveDownloads.size())) {
+ reportError(tr("VisitUploaderProfile: invalid download index %1").arg(index));
+ return;
+ }
+ DownloadInfo* info = m_ActiveDownloads[index];
+
+ if (info->m_State < DownloadManager::STATE_READY) {
+ // UI shouldn't allow this
+ return;
+ }
+
+ const auto& uploaderUrl = info->m_FileInfo->uploaderUrl;
+ if (!uploaderUrl.isEmpty()) {
+ shell::Open(QUrl(uploaderUrl));
+ } else {
+ emit showMessage(tr("Uploader for this Mod is unknown"));
+ }
+}
+
void DownloadManager::openFile(int index)
{
if ((index < 0) || (index >= m_ActiveDownloads.size())) {
@@ -1724,6 +1748,9 @@ void DownloadManager::createMetaFile(DownloadInfo* info)
metaFile.setValue("category", info->m_FileInfo->categoryID);
metaFile.setValue("repository", info->m_FileInfo->repository);
metaFile.setValue("userData", info->m_FileInfo->userData);
+ metaFile.setValue("author", info->m_FileInfo->author);
+ metaFile.setValue("uploader", info->m_FileInfo->uploader);
+ metaFile.setValue("uploaderUrl", info->m_FileInfo->uploaderUrl);
metaFile.setValue("installed", info->m_State == DownloadManager::STATE_INSTALLED);
metaFile.setValue("uninstalled", info->m_State == DownloadManager::STATE_UNINSTALLED);
metaFile.setValue("paused", (info->m_State == DownloadManager::STATE_PAUSED) ||
@@ -1753,7 +1780,10 @@ void DownloadManager::nxmDescriptionAvailable(QString, int, QVariant userData,
DownloadInfo* info = downloadInfoByID(userData.toInt());
if (info == nullptr)
return;
- info->m_FileInfo->categoryID = result["category_id"].toInt();
+ info->m_FileInfo->categoryID = result["category_id"].toInt();
+ info->m_FileInfo->author = result["author"].toString();
+ info->m_FileInfo->uploader = result["uploaded_by"].toString();
+ info->m_FileInfo->uploaderUrl = result["uploaded_users_profile_url"].toString();
QTextDocument doc;
doc.setHtml(result["name"].toString().trimmed());
info->m_FileInfo->modName = doc.toPlainText();
@@ -1813,7 +1843,10 @@ void DownloadManager::nxmFilesAvailable(QString, int, QVariant userData,
info->m_FileInfo->fileName = fileInfo["file_name"].toString();
info->m_FileInfo->description =
BBCode::convertToHTML(fileInfo["description"].toString());
- found = true;
+ info->m_FileInfo->author = fileInfo["author"].toString();
+ info->m_FileInfo->uploader = fileInfo["uploaded_by"].toString();
+ info->m_FileInfo->uploaderUrl = fileInfo["uploaded_users_profile_url"].toString();
+ found = true;
break;
}
}
@@ -2116,9 +2149,12 @@ void DownloadManager::nxmFileInfoFromMd5Available(QString gameName, QVariant use
info->m_FileInfo->version.parse(fileDetails["mod_version"].toString());
info->m_FileInfo->fileCategory = fileDetails["category_id"].toInt();
- info->m_FileInfo->modID = modDetails["mod_id"].toInt();
- info->m_FileInfo->modName = modDetails["name"].toString();
- info->m_FileInfo->categoryID = modDetails["category_id"].toInt();
+ info->m_FileInfo->modID = modDetails["mod_id"].toInt();
+ info->m_FileInfo->modName = modDetails["name"].toString();
+ info->m_FileInfo->categoryID = modDetails["category_id"].toInt();
+ info->m_FileInfo->author = modDetails["author"].toString();
+ info->m_FileInfo->uploader = modDetails["uploaded_by"].toString();
+ info->m_FileInfo->uploaderUrl = modDetails["uploaded_users_profile_url"].toString();
QString gameShortName = gameName;
QStringList games(m_ManagedGame->validShortNames());
diff --git a/src/downloadmanager.h b/src/downloadmanager.h
index 7ff7752d..abf909c8 100644
--- a/src/downloadmanager.h
+++ b/src/downloadmanager.h
@@ -503,6 +503,8 @@ public slots:
void visitOnNexus(int index);
+ void visitUploaderProfile(int index);
+
void openFile(int index);
void openMetaFile(int index);
diff --git a/src/downloadstab.cpp b/src/downloadstab.cpp
index 958efbd5..4f988bf0 100644
--- a/src/downloadstab.cpp
+++ b/src/downloadstab.cpp
@@ -37,6 +37,8 @@ DownloadsTab::DownloadsTab(OrganizerCore& core, Ui::MainWindow* mwui)
SLOT(queryInfoMd5(int)));
connect(ui.list, SIGNAL(visitOnNexus(int)), m_core.downloadManager(),
SLOT(visitOnNexus(int)));
+ connect(ui.list, SIGNAL(visitUploaderProfile(int)), m_core.downloadManager(),
+ SLOT(visitUploaderProfile(int)));
connect(ui.list, SIGNAL(openFile(int)), m_core.downloadManager(),
SLOT(openFile(int)));
connect(ui.list, SIGNAL(openMetaFile(int)), m_core.downloadManager(),
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 90c7dec7..ce432196 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -3357,6 +3357,12 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
mod->setNexusCategory(result["category_id"].toInt());
+ mod->setAuthor(result["author"].toString());
+
+ mod->setUploader(result["uploaded_by"].toString());
+
+ mod->setUploaderUrl(result["uploaded_users_profile_url"].toString());
+
if ((mod->endorsedState() != EndorsedState::ENDORSED_NEVER) &&
(result.contains("endorsement"))) {
QVariantMap endorsement = result["endorsement"].toMap();
diff --git a/src/modinfo.h b/src/modinfo.h
index bbb1aae3..b73073ed 100644
--- a/src/modinfo.h
+++ b/src/modinfo.h
@@ -352,6 +352,21 @@ public: // IModInterface implementations / Re-declaration
virtual QStringList categories() const override;
/**
+ * @return the author of the mod.
+ */
+ virtual QString author() const = 0;
+
+ /**
+ * @return the name of the uploader of this mod.
+ */
+ virtual QString uploader() const = 0;
+
+ /**
+ * @return the URL of the uploader of this mod's profile.
+ */
+ virtual QString uploaderUrl() const = 0;
+
+ /**
* @return the tracked state of this mod.
*/
virtual MOBase::TrackedState trackedState() const override
@@ -486,6 +501,21 @@ public: // Mutable operations:
*/
virtual bool setName(const QString& name) = 0;
+ /**
+ * @brief Set the author of the mod.
+ */
+ virtual void setAuthor(const QString& author) = 0;
+
+ /**
+ * @brief Set the name of the uploader of this mod.
+ */
+ virtual void setUploader(const QString& uploader) = 0;
+
+ /**
+ * @brief Set the URL of the uploader of this mod's profile.
+ */
+ virtual void setUploaderUrl(const QString& uploaderUrl) = 0;
+
public: // Methods after this do not come from IModInterface:
/**
* @return true if this mod is empty, false otherwise.
diff --git a/src/modinfobackup.h b/src/modinfobackup.h
index 0c3c29d7..620359b9 100644
--- a/src/modinfobackup.h
+++ b/src/modinfobackup.h
@@ -40,6 +40,12 @@ public:
virtual QString getNexusDescription() const override { return QString(); }
virtual void setNexusCategory(int) override {}
virtual int getNexusCategory() const override { return 0; }
+ virtual QString author() const override { return QString(); }
+ virtual void setAuthor(const QString&) override {}
+ virtual QString uploader() const override { return QString(); }
+ virtual void setUploader(const QString&) override {}
+ virtual QString uploaderUrl() const override { return QString(); }
+ virtual void setUploaderUrl(const QString&) override {}
virtual bool isBackup() const override { return true; }
virtual void addInstalledFile(int, int) override {}
diff --git a/src/modinfoforeign.h b/src/modinfoforeign.h
index fa57e378..813725d5 100644
--- a/src/modinfoforeign.h
+++ b/src/modinfoforeign.h
@@ -67,6 +67,12 @@ public:
virtual QDateTime getNexusLastModified() const override { return QDateTime(); }
virtual void setNexusLastModified(QDateTime) override {}
virtual QString getNexusDescription() const override { return QString(); }
+ virtual QString author() const override { return QString(); }
+ virtual void setAuthor(const QString&) override {}
+ virtual QString uploader() const override { return QString(); }
+ virtual void setUploader(const QString&) override {}
+ virtual QString uploaderUrl() const override { return QString(); }
+ virtual void setUploaderUrl(const QString&) override {}
virtual QStringList archives(bool = false) override { return m_Archives; }
virtual QStringList stealFiles() const override
{
diff --git a/src/modinfooverwrite.h b/src/modinfooverwrite.h
index 45b5bbdd..dda7d388 100644
--- a/src/modinfooverwrite.h
+++ b/src/modinfooverwrite.h
@@ -70,6 +70,12 @@ public:
virtual QString getNexusDescription() const override { return QString(); }
virtual void setNexusCategory(int) override {}
virtual int getNexusCategory() const override { return 0; }
+ virtual QString author() const override { return QString(); }
+ virtual void setAuthor(const QString&) override {}
+ virtual QString uploader() const override { return QString(); }
+ virtual void setUploader(const QString&) override {}
+ virtual QString uploaderUrl() const override { return QString(); }
+ virtual void setUploaderUrl(const QString&) override {}
virtual QStringList archives(bool checkOnDisk = false) override;
virtual void addInstalledFile(int, int) override {}
virtual std::set<std::pair<int, int>> installedFiles() const override { return {}; }
diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp
index b33bbc44..c529c054 100644
--- a/src/modinforegular.cpp
+++ b/src/modinforegular.cpp
@@ -98,6 +98,9 @@ void ModInfoRegular::readMeta()
m_NexusDescription = metaFile.value("nexusDescription", "").toString();
m_NexusFileStatus = metaFile.value("nexusFileStatus", "1").toInt();
m_NexusCategory = metaFile.value("nexusCategory", 0).toInt();
+ m_Author = metaFile.value("author", "").toString();
+ m_Uploader = metaFile.value("uploader", "").toString();
+ m_UploaderUrl = metaFile.value("uploaderUrl", "").toString();
m_Repository = metaFile.value("repository", "Nexus").toString();
m_Converted = metaFile.value("converted", false).toBool();
m_Validated = metaFile.value("validated", false).toBool();
@@ -270,6 +273,9 @@ void ModInfoRegular::saveMeta()
metaFile.setValue("lastNexusUpdate", m_LastNexusUpdate.toString(Qt::ISODate));
metaFile.setValue("nexusLastModified", m_NexusLastModified.toString(Qt::ISODate));
metaFile.setValue("nexusCategory", m_NexusCategory);
+ metaFile.setValue("author", m_Author);
+ metaFile.setValue("uploader", m_Uploader);
+ metaFile.setValue("uploaderUrl", m_UploaderUrl);
metaFile.setValue("converted", m_Converted);
metaFile.setValue("validated", m_Validated);
metaFile.setValue("color", m_Color);
@@ -855,6 +861,45 @@ void ModInfoRegular::setNexusCategory(int category)
saveMeta();
}
+QString ModInfoRegular::author() const
+{
+ return m_Author;
+}
+
+void ModInfoRegular::setAuthor(const QString& author)
+{
+ m_Author = author;
+ m_MetaInfoChanged = true;
+ saveMeta();
+ emit modDetailsUpdated(true);
+}
+
+QString ModInfoRegular::uploader() const
+{
+ return m_Uploader;
+}
+
+void ModInfoRegular::setUploader(const QString& uploader)
+{
+ m_Uploader = uploader;
+ m_MetaInfoChanged = true;
+ saveMeta();
+ emit modDetailsUpdated(true);
+}
+
+QString ModInfoRegular::uploaderUrl() const
+{
+ return m_UploaderUrl;
+}
+
+void ModInfoRegular::setUploaderUrl(const QString& uploaderUrl)
+{
+ m_UploaderUrl = uploaderUrl;
+ m_MetaInfoChanged = true;
+ saveMeta();
+ emit modDetailsUpdated(true);
+}
+
void ModInfoRegular::setCustomURL(QString const& url)
{
m_CustomURL = url;
diff --git a/src/modinforegular.h b/src/modinforegular.h
index a408d966..062f3dd8 100644
--- a/src/modinforegular.h
+++ b/src/modinforegular.h
@@ -389,6 +389,36 @@ public:
*/
virtual void setNexusCategory(int category) override;
+ /**
+ * @return the author of the mod.
+ */
+ virtual QString author() const override;
+
+ /**
+ * @brief Set the author of the mod.
+ */
+ virtual void setAuthor(const QString&) override;
+
+ /**
+ * @return the name of the uploader of this mod.
+ */
+ virtual QString uploader() const override;
+
+ /**
+ * @brief Set the name of the uploader of this mod.
+ */
+ virtual void setUploader(const QString&) override;
+
+ /**
+ * @return the URL of the uploader of this mod's profile.
+ */
+ virtual QString uploaderUrl() const override;
+
+ /**
+ * @brief Set the URL of the uploader of this mod's profile.
+ */
+ virtual void setUploaderUrl(const QString&) override;
+
virtual QStringList archives(bool checkOnDisk = false) override;
virtual void setColor(QColor color) override;
@@ -468,6 +498,9 @@ private:
QDateTime m_LastNexusUpdate;
QDateTime m_NexusLastModified;
int m_NexusCategory;
+ QString m_Author;
+ QString m_Uploader;
+ QString m_UploaderUrl;
QColor m_Color;
diff --git a/src/modinfoseparator.h b/src/modinfoseparator.h
index 67bc3d56..ef966a8f 100644
--- a/src/modinfoseparator.h
+++ b/src/modinfoseparator.h
@@ -49,6 +49,12 @@ public:
virtual void setNexusCategory(int) override {}
virtual QDateTime creationTime() const override { return QDateTime(); }
virtual QString getNexusDescription() const override { return QString(); }
+ virtual QString author() const override { return QString(); }
+ virtual void setAuthor(const QString&) override {}
+ virtual QString uploader() const override { return QString(); }
+ virtual void setUploader(const QString&) override {}
+ virtual QString uploaderUrl() const override { return QString(); }
+ virtual void setUploaderUrl(const QString&) override {}
virtual void addInstalledFile(int /*modId*/, int /*fileId*/) override {}
virtual bool isSeparator() const override { return true; }
diff --git a/src/modlist.cpp b/src/modlist.cpp
index 4ef3afbc..d3552c73 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -250,6 +250,10 @@ QVariant ModList::data(const QModelIndex& modelIndex, int role) const
return QVariant();
}
}
+ } else if (column == COL_AUTHOR) {
+ return modInfo->author();
+ } else if (column == COL_UPLOADER) {
+ return modInfo->uploader();
} else if (column == COL_INSTALLTIME) {
// display installation time for mods that can be updated
if (modInfo->creationTime().isValid()) {
@@ -1317,6 +1321,10 @@ QString ModList::getColumnName(int column)
return tr("Priority");
case COL_CATEGORY:
return tr("Category");
+ case COL_AUTHOR:
+ return tr("Author");
+ case COL_UPLOADER:
+ return tr("Uploader");
case COL_GAME:
return tr("Source Game");
case COL_MODID:
@@ -1343,6 +1351,10 @@ QString ModList::getColumnToolTip(int column) const
"overwrites files from mods with lower priority.");
case COL_CATEGORY:
return tr("Primary category of the mod.");
+ case COL_AUTHOR:
+ return tr("Author(s) of the mod.");
+ case COL_UPLOADER:
+ return tr("Uploader of the mod. This is not necessarily the same as the author.");
case COL_GAME:
return tr("The source game which was the origin of this mod.");
case COL_MODID:
diff --git a/src/modlist.h b/src/modlist.h
index 112070e1..1da74fa8 100644
--- a/src/modlist.h
+++ b/src/modlist.h
@@ -85,6 +85,8 @@ public:
COL_FLAGS,
COL_CONTENT,
COL_CATEGORY,
+ COL_AUTHOR,
+ COL_UPLOADER,
COL_MODID,
COL_GAME,
COL_VERSION,
diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp
index fefb1d73..d2c8e2a1 100644
--- a/src/modlistcontextmenu.cpp
+++ b/src/modlistcontextmenu.cpp
@@ -443,6 +443,12 @@ void ModListContextMenu::addBackupActions(ModInfo::Ptr mod)
});
}
+ if (!mod->uploaderUrl().isEmpty()) {
+ addAction(tr("Visit the uploader's profile"), [=]() {
+ m_actions.visitUploaderProfile(m_selected);
+ });
+ }
+
const auto url = mod->parseCustomURL();
if (url.isValid()) {
addAction(tr("Visit on %1").arg(url.host()), [=]() {
@@ -612,6 +618,12 @@ void ModListContextMenu::addRegularActions(ModInfo::Ptr mod)
});
}
+ if (!mod->uploaderUrl().isEmpty()) {
+ addAction(tr("Visit the uploader's profile"), [=]() {
+ m_actions.visitUploaderProfile(m_selected);
+ });
+ }
+
const auto url = mod->parseCustomURL();
if (url.isValid()) {
addAction(tr("Visit on %1").arg(url.host()), [=]() {
diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp
index 3cc7de40..2fb854c4 100644
--- a/src/modlistsortproxy.cpp
+++ b/src/modlistsortproxy.cpp
@@ -183,6 +183,26 @@ bool ModListSortProxy::lessThan(const QModelIndex& left, const QModelIndex& righ
}
}
} break;
+ case ModList::COL_AUTHOR: {
+ if (leftMod->author() != rightMod->author()) {
+ lt = leftMod->author() < rightMod->author();
+ } else {
+ int comp = QString::compare(leftMod->uploader(), rightMod->uploader(),
+ Qt::CaseInsensitive);
+ if (comp != 0)
+ lt = comp < 0;
+ }
+ } break;
+ case ModList::COL_UPLOADER: {
+ if (leftMod->uploader() != rightMod->uploader()) {
+ lt = leftMod->uploader() < rightMod->uploader();
+ } else {
+ int comp = QString::compare(leftMod->uploader(), rightMod->uploader(),
+ Qt::CaseInsensitive);
+ if (comp != 0)
+ lt = comp < 0;
+ }
+ } break;
case ModList::COL_MODID: {
if (leftMod->nexusId() != rightMod->nexusId())
lt = leftMod->nexusId() < rightMod->nexusId();
@@ -447,8 +467,8 @@ bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const
segmentGood = true;
bool foundKeyword = false;
- // check each word in the segment for match, each word needs to be matched but it
- // doesn't matter where.
+ // check each word in the segment for match, each word needs to be matched but
+ // it doesn't matter where.
for (auto& currentKeyword : ANDKeywords) {
foundKeyword = false;
@@ -458,6 +478,18 @@ bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const
foundKeyword = true;
}
+ // Search by author
+ if (!foundKeyword && m_EnabledColumns[ModList::COL_AUTHOR] &&
+ info->author().contains(currentKeyword, Qt::CaseInsensitive)) {
+ foundKeyword = true;
+ }
+
+ // Search by uploader
+ if (!foundKeyword && m_EnabledColumns[ModList::COL_UPLOADER] &&
+ info->uploader().contains(currentKeyword, Qt::CaseInsensitive)) {
+ foundKeyword = true;
+ }
+
// Search by notes
if (!foundKeyword && m_EnabledColumns[ModList::COL_NOTES] &&
(info->notes().contains(currentKeyword, Qt::CaseInsensitive) ||
@@ -601,8 +633,8 @@ bool ModListSortProxy::canDropMimeData(const QMimeData* data, Qt::DropAction act
// disable drop install with group proxy, except the one for collapsible separator
// - it would be nice to be able to "install to category" or something like that but
- // it's a bit more complicated since the drop position is based on the category, so
- // just disabling for now
+ // it's a bit more complicated since the drop position is based on the category,
+ // so just disabling for now
if (dropInfo.isDownloadDrop()) {
// maybe there is a cleaner way?
if (qobject_cast<QtGroupingProxy*>(sourceModel())) {
@@ -675,8 +707,8 @@ void ModListSortProxy::aboutToChangeData()
void ModListSortProxy::postDataChanged()
{
// if the filter is re-activated right away the editor can't be deleted but becomes
- // invisible or at least the view continues to think it's being edited. As a result no
- // new editor can be opened
+ // invisible or at least the view continues to think it's being edited. As a result
+ // no new editor can be opened
QTimer::singleShot(10, [this]() {
setCriteria(m_PreChangeCriteria);
m_PreChangeCriteria.clear();
diff --git a/src/modlistview.cpp b/src/modlistview.cpp
index 7b3669fd..6091021b 100644
--- a/src/modlistview.cpp
+++ b/src/modlistview.cpp
@@ -817,6 +817,7 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo
// hide these columns by default
header()->setSectionHidden(ModList::COL_CONTENT, true);
header()->setSectionHidden(ModList::COL_MODID, true);
+ header()->setSectionHidden(ModList::COL_UPLOADER, true);
header()->setSectionHidden(ModList::COL_GAME, true);
header()->setSectionHidden(ModList::COL_INSTALLTIME, true);
header()->setSectionHidden(ModList::COL_NOTES, true);
diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp
index efe48c31..6b26a1e3 100644
--- a/src/modlistviewactions.cpp
+++ b/src/modlistviewactions.cpp
@@ -378,8 +378,11 @@ void ModListViewActions::exportModListCSV() const
QCheckBox* mod_Status = new QCheckBox(tr("Mod_Status"));
mod_Status->setChecked(true);
QCheckBox* primary_Category = new QCheckBox(tr("Primary_Category"));
+ QCheckBox* mod_Author = new QCheckBox(tr("Mod_Author"));
+ QCheckBox* mod_Uploader = new QCheckBox(tr("Mod_Uploader"));
QCheckBox* nexus_ID = new QCheckBox(tr("Nexus_ID"));
QCheckBox* mod_Nexus_URL = new QCheckBox(tr("Mod_Nexus_URL"));
+ QCheckBox* mod_Uploader_URL = new QCheckBox(tr("Mod_Uploader_URL"));
QCheckBox* mod_Version = new QCheckBox(tr("Mod_Version"));
QCheckBox* install_Date = new QCheckBox(tr("Install_Date"));
QCheckBox* download_File_Name = new QCheckBox(tr("Download_File_Name"));
@@ -390,8 +393,11 @@ void ModListViewActions::exportModListCSV() const
vbox1->addWidget(mod_Status);
vbox1->addWidget(mod_Note);
vbox1->addWidget(primary_Category);
+ vbox1->addWidget(mod_Author);
+ vbox1->addWidget(mod_Uploader);
vbox1->addWidget(nexus_ID);
vbox1->addWidget(mod_Nexus_URL);
+ vbox1->addWidget(mod_Uploader_URL);
vbox1->addWidget(mod_Version);
vbox1->addWidget(install_Date);
vbox1->addWidget(download_File_Name);
@@ -435,12 +441,21 @@ void ModListViewActions::exportModListCSV() const
if (primary_Category->isChecked())
fields.push_back(
std::make_pair(QString("#Primary_Category"), CSVBuilder::TYPE_STRING));
+ if (mod_Author->isChecked())
+ fields.push_back(
+ std::make_pair(QString("#Mod_Author"), CSVBuilder::TYPE_STRING));
+ if (mod_Uploader->isChecked())
+ fields.push_back(
+ std::make_pair(QString("#Mod_Uploader"), CSVBuilder::TYPE_STRING));
if (nexus_ID->isChecked())
fields.push_back(
std::make_pair(QString("#Nexus_ID"), CSVBuilder::TYPE_INTEGER));
if (mod_Nexus_URL->isChecked())
fields.push_back(
std::make_pair(QString("#Mod_Nexus_URL"), CSVBuilder::TYPE_STRING));
+ if (mod_Uploader_URL->isChecked())
+ fields.push_back(
+ std::make_pair(QString("#Mod_Uploader_URL"), CSVBuilder::TYPE_STRING));
if (mod_Version->isChecked())
fields.push_back(
std::make_pair(QString("#Mod_Version"), CSVBuilder::TYPE_STRING));
@@ -485,6 +500,10 @@ void ModListViewActions::exportModListCSV() const
(m_categories.categoryExists(info->primaryCategory()))
? m_categories.getCategoryNameByID(info->primaryCategory())
: "");
+ if (mod_Author->isChecked())
+ builder.setRowField("#Mod_Author", info->author());
+ if (mod_Uploader->isChecked())
+ builder.setRowField("#Mod_Uploader", info->uploader());
if (nexus_ID->isChecked())
builder.setRowField("#Nexus_ID", info->nexusId());
if (mod_Nexus_URL->isChecked())
@@ -493,6 +512,8 @@ void ModListViewActions::exportModListCSV() const
? NexusInterface::instance().getModURL(
info->nexusId(), info->gameName())
: "");
+ if (mod_Uploader_URL->isChecked())
+ builder.setRowField("#Mod_Uploader_URL", info->uploaderUrl());
if (mod_Version->isChecked())
builder.setRowField("#Mod_Version", info->version().canonicalString());
if (install_Date->isChecked())
@@ -887,11 +908,7 @@ void ModListViewActions::markConverted(const QModelIndexList& indices) const
void ModListViewActions::visitOnNexus(const QModelIndexList& indices) const
{
if (indices.size() > 10) {
- if (QMessageBox::question(m_parent, tr("Opening Nexus Links"),
- tr("You are trying to open %1 links to Nexus Mods. Are "
- "you sure you want to do this?")
- .arg(indices.size()),
- QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
+ if (!askOpenLinksConfirmation(indices.size(), tr("Nexus Links"))) {
return;
}
}
@@ -911,11 +928,7 @@ void ModListViewActions::visitOnNexus(const QModelIndexList& indices) const
void ModListViewActions::visitWebPage(const QModelIndexList& indices) const
{
if (indices.size() > 10) {
- if (QMessageBox::question(m_parent, tr("Opening Web Pages"),
- tr("You are trying to open %1 Web Pages. Are you sure "
- "you want to do this?")
- .arg(indices.size()),
- QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
+ if (!askOpenLinksConfirmation(indices.size(), tr("Web Pages"))) {
return;
}
}
@@ -933,11 +946,7 @@ void ModListViewActions::visitWebPage(const QModelIndexList& indices) const
void ModListViewActions::visitNexusOrWebPage(const QModelIndexList& indices) const
{
if (indices.size() > 10) {
- if (QMessageBox::question(m_parent, tr("Opening Web Pages"),
- tr("You are trying to open %1 Web Pages. Are you sure "
- "you want to do this?")
- .arg(indices.size()),
- QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
+ if (!askOpenLinksConfirmation(indices.size(), tr("Web Pages"))) {
return;
}
}
@@ -963,6 +972,37 @@ void ModListViewActions::visitNexusOrWebPage(const QModelIndexList& indices) con
}
}
+void ModListViewActions::visitUploaderProfile(const QModelIndexList& indices) const
+{
+ if (indices.size() > 10) {
+ if (!askOpenLinksConfirmation(indices.size(), tr("Uploader Profiles"))) {
+ return;
+ }
+ }
+
+ for (auto& idx : indices) {
+ ModInfo::Ptr info = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt());
+ const auto uploaderUrl = info->uploaderUrl();
+
+ if (!uploaderUrl.isEmpty()) {
+ shell::Open(QUrl(uploaderUrl));
+ } else {
+ log::error("mod '{}' has no uploader url", info->name());
+ }
+ }
+}
+
+bool ModListViewActions::askOpenLinksConfirmation(std::size_t numberOfLinks,
+ const QString& nameOfLinks) const
+{
+ return QMessageBox::question(m_parent, tr("Opening %1").arg(nameOfLinks),
+ tr("You are trying to open %1 %2. Are you sure "
+ "you want to do this?")
+ .arg(numberOfLinks)
+ .arg(nameOfLinks),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes;
+}
+
void ModListViewActions::reinstallMod(const QModelIndex& index) const
{
ModInfo::Ptr modInfo = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt());
diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h
index ad20e784..fb834a90 100644
--- a/src/modlistviewactions.h
+++ b/src/modlistviewactions.h
@@ -92,6 +92,7 @@ public:
void visitOnNexus(const QModelIndexList& indices) const;
void visitWebPage(const QModelIndexList& indices) const;
void visitNexusOrWebPage(const QModelIndexList& indices) const;
+ void visitUploaderProfile(const QModelIndexList& indices) const;
void reinstallMod(const QModelIndex& index) const;
void createBackup(const QModelIndex& index) const;
void restoreHiddenFiles(const QModelIndexList& indices) const;
@@ -174,6 +175,12 @@ private:
//
void checkModsForUpdates(std::multimap<QString, int> const& IDs) const;
+ // ask the user for confirmation for opening many links at once.
+ // returns true if the user confirmed, false otherwise.
+ //
+ bool askOpenLinksConfirmation(std::size_t numberOfLinks,
+ const QString& nameOfLinks) const;
+
private:
OrganizerCore& m_core;
FilterList& m_filters;
diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp
index ecd33cb6..fa4dd729 100644
--- a/src/nexusinterface.cpp
+++ b/src/nexusinterface.cpp
@@ -131,6 +131,9 @@ void NexusBridge::nxmFilesAvailable(QString gameName, int modID, QVariant userDa
temp.categoryID = fileInfo["category_id"].toInt();
temp.fileID = fileInfo["file_id"].toInt();
temp.fileSize = fileInfo["size"].toInt();
+ temp.author = fileInfo["author"].toString();
+ temp.uploader = fileInfo["uploaded_by"].toString();
+ temp.uploaderUrl = fileInfo["uploaded_users_profile_url"].toString();
fileInfoList.append(&temp);
}
@@ -575,13 +578,16 @@ void NexusInterface::fakeFiles()
QVariantList result;
QVariantMap fileMap;
- fileMap["uri"] = "fakeURI";
- fileMap["name"] = "fakeName";
- fileMap["description"] = "fakeDescription";
- fileMap["version"] = "1.0.0";
- fileMap["category_id"] = "1";
- fileMap["id"] = "1";
- fileMap["size"] = "512";
+ fileMap["uri"] = "fakeURI";
+ fileMap["name"] = "fakeName";
+ fileMap["description"] = "fakeDescription";
+ fileMap["version"] = "1.0.0";
+ fileMap["category_id"] = "1";
+ fileMap["id"] = "1";
+ fileMap["size"] = "512";
+ fileMap["author"] = "fakeAuthor";
+ fileMap["uploaded_by"] = "fakeUploader";
+ fileMap["uploaded_users_profile_url"] = "https://fakeuploader.com";
result.append(fileMap);
emit nxmFilesAvailable("fakeGame", 1234, "fake", result, id++);