diff options
| -rw-r--r-- | readme.md | 17 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/aboutdialog.ui | 20 | ||||
| -rw-r--r-- | src/downloadlistview.cpp (renamed from src/downloadlistwidget.cpp) | 191 | ||||
| -rw-r--r-- | src/downloadlistview.h (renamed from src/downloadlistwidget.h) | 51 | ||||
| -rw-r--r-- | src/downloadstab.cpp | 8 | ||||
| -rw-r--r-- | src/downloadstab.h | 4 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 1 | ||||
| -rw-r--r-- | src/mainwindow.ui | 12 | ||||
| -rw-r--r-- | src/modlistcontextmenu.cpp | 48 | ||||
| -rw-r--r-- | src/modlistcontextmenu.h | 3 | ||||
| -rw-r--r-- | src/modlistversiondelegate.cpp | 8 | ||||
| -rw-r--r-- | src/modlistversiondelegate.h | 5 | ||||
| -rw-r--r-- | src/modlistview.cpp | 27 | ||||
| -rw-r--r-- | src/modlistview.h | 9 | ||||
| -rw-r--r-- | src/modlistviewactions.cpp | 17 | ||||
| -rw-r--r-- | src/modlistviewactions.h | 4 | ||||
| -rw-r--r-- | src/organizercore.cpp | 3 | ||||
| -rw-r--r-- | src/settings.cpp | 38 | ||||
| -rw-r--r-- | src/settings.h | 23 | ||||
| -rw-r--r-- | src/settingsdialog.ui | 208 | ||||
| -rw-r--r-- | src/settingsdialoguserinterface.cpp | 33 | ||||
| -rw-r--r-- | src/settingsdialoguserinterface.h | 6 | ||||
| -rw-r--r-- | src/shared/directoryentry.cpp | 14 |
24 files changed, 506 insertions, 246 deletions
@@ -12,17 +12,17 @@ The project took up speed again after a few more coders showed up in late 2017, ## Help Wanted!
Mod Organizer 2 is an open project in the hands of the community, there are problems that need to be solved and things that could be added. MO2 really needs developers and if you have the programming skills and some free time you can really improve the experience of the modding community.
-To have more information, please join the open MO2 Development discord server :* [ModOrganizerDevs](https://discord.gg/vD2ZbfX)
+To have more information, please join the open MO2 Development discord server: [Mod Organizer 2](https://discord.gg/vD2ZbfX)
If you want to help translate MO2 to your language you should join the discord server too and head to the #translation channel.
-To setup a development environment on your machine, there is the [umbrella project](https://github.com/Modorganizer2/modorganizer-umbrella) that handles that.
-If you want to submit your code changes, please use a good formating style like the default one in Visual Studio.
+To setup a development environment on your machine, there is the [mob project](https://github.com/modorganizer2/mob) that handles that.
+If you want to submit your code changes, please use a good formatting style like the default one in Visual Studio.
Through the work of a few people of the community MO2 has come quite far, now it needs some more of those people to go further.
## Reporting Issues:
-Issues should be reported to the GitHub page or on the open discord server: [ModOrganizerDevs](https://discord.gg/vD2ZbfX). Here is also where dev builds are tested, bugs are reported and investigated, suggestions are discussed and a lot more.
+Issues should be reported to the GitHub page or on the open discord server: [Mod Organizer 2](https://discord.gg/vD2ZbfX). Here is also where dev builds are tested, bugs are reported and investigated, suggestions are discussed and a lot more.
-Credits to Tannin, LePresidente, Silarn, erasmux, AL12, LostDragonist, AnyOldName3 and many others for the development.
+Credits to Tannin, LePresidente, Silarn, erasmux, AL12, LostDragonist, AnyOldName3, isa, Holt59 and many others for the development.
## Download Location
@@ -35,11 +35,11 @@ Credits to Tannin, LePresidente, Silarn, erasmux, AL12, LostDragonist, AnyOldNam ## Building
-Please refer to [Modorganizer2/modorganizer-umbrella](https://github.com/Modorganizer2/modorganizer-umbrella) for build instructions.
+Please refer to [Modorganizer2/mob](https://github.com/modorganizer2/mob) for build instructions.
## Other Repositories
-MO2 consists of multiple repositories on github. The Umbrella project will download them automatically as required. They should however also be buildable individually.
+MO2 consists of multiple repositories on github. The mob project will download them automatically as required. They should however also be buildable individually.
Here is a complete list:
* https://github.com/LePresidente/cpython-1
@@ -47,6 +47,7 @@ Here is a complete list: * https://github.com/ModOrganizer2/githubpp
* https://github.com/ModOrganizer2/modorganizer
* https://github.com/ModOrganizer2/modorganizer-archive
+* https://github.com/ModOrganizer2/modorganizer-basic_games
* https://github.com/ModOrganizer2/modorganizer-bsatk
* https://github.com/ModOrganizer2/modorganizer-bsa_extractor
* https://github.com/ModOrganizer2/modorganizer-check_fnis
@@ -68,11 +69,13 @@ Here is a complete list: * https://github.com/ModOrganizer2/modorganizer-game_skyrimVR
* https://github.com/ModOrganizer2/modorganizer-game_ttw
* https://github.com/ModOrganizer2/modorganizer-installer_bain
+* https://github.com/ModOrganizer2/modorganizer-installer_wizard
* https://github.com/ModOrganizer2/modorganizer-installer_bundle
* https://github.com/ModOrganizer2/modorganizer-installer_fomod
* https://github.com/ModOrganizer2/modorganizer-installer_fomod_csharp
* https://github.com/ModOrganizer2/modorganizer-installer_manual
* https://github.com/ModOrganizer2/modorganizer-installer_ncc
+* https://github.com/ModOrganizer2/modorganizer-installer_omod
* https://github.com/ModOrganizer2/modorganizer-installer_quick
* https://github.com/ModOrganizer2/modorganizer-lootcli
* https://github.com/ModOrganizer2/modorganizer-NCC
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d1092637..a5d15740 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,7 +63,7 @@ add_filter(NAME src/dialogs GROUPS add_filter(NAME src/downloads GROUPS downloadlist - downloadlistwidget + downloadlistview downloadmanager ) diff --git a/src/aboutdialog.ui b/src/aboutdialog.ui index e1f7aef3..16fc57aa 100644 --- a/src/aboutdialog.ui +++ b/src/aboutdialog.ui @@ -220,6 +220,11 @@ <string notr="true">isa</string>
</property>
</item>
+ <item>
+ <property name="text">
+ <string>Holt59</string>
+ </property>
+ </item>
</widget>
</item>
</layout>
@@ -256,11 +261,6 @@ <string notr="true">przester</string>
</property>
</item>
- <item>
- <property name="text">
- <string notr="true">Holt59</string>
- </property>
- </item>
</widget>
</item>
</layout>
@@ -491,6 +491,11 @@ </item>
<item>
<property name="text">
+ <string>Luca|EzioTheDeadPoet</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
<string notr="true">ogrotten</string>
</property>
</item>
@@ -501,6 +506,11 @@ </item>
<item>
<property name="text">
+ <string>Patchier</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
<string notr="true">PurpleFez</string>
</property>
</item>
diff --git a/src/downloadlistwidget.cpp b/src/downloadlistview.cpp index 22e3a3b9..a1f544a8 100644 --- a/src/downloadlistwidget.cpp +++ b/src/downloadlistview.cpp @@ -18,7 +18,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. */
#include "downloadlist.h"
-#include "downloadlistwidget.h"
+#include "downloadlistview.h"
#include <report.h>
#include <log.h>
#include <QPainter>
@@ -34,7 +34,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. using namespace MOBase;
DownloadProgressDelegate::DownloadProgressDelegate(
- DownloadManager* manager, DownloadListWidget* list)
+ DownloadManager* manager, DownloadListView* list)
: QStyledItemDelegate(list), m_Manager(manager), m_List(list)
{
}
@@ -113,7 +113,7 @@ void DownloadListHeader::mouseReleaseEvent(QMouseEvent *event) customResizeSections();
}
-DownloadListWidget::DownloadListWidget(QWidget *parent)
+DownloadListView::DownloadListView(QWidget *parent)
: QTreeView(parent)
{
setHeader(new DownloadListHeader(Qt::Horizontal, this));
@@ -135,11 +135,11 @@ DownloadListWidget::DownloadListWidget(QWidget *parent) connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomContextMenu(QPoint)));
}
-DownloadListWidget::~DownloadListWidget()
+DownloadListView::~DownloadListView()
{
}
-void DownloadListWidget::setManager(DownloadManager *manager)
+void DownloadListView::setManager(DownloadManager *manager)
{
m_Manager = manager;
@@ -154,18 +154,18 @@ void DownloadListWidget::setManager(DownloadManager *manager) header()->hideSection(DownloadList::COL_SOURCEGAME);
}
-void DownloadListWidget::setSourceModel(DownloadList *sourceModel)
+void DownloadListView::setSourceModel(DownloadList *sourceModel)
{
m_SourceModel = sourceModel;
}
-void DownloadListWidget::setMetaDisplay(bool metaDisplay)
+void DownloadListView::setMetaDisplay(bool metaDisplay)
{
if (m_SourceModel != nullptr)
m_SourceModel->setMetaDisplay(metaDisplay);
}
-void DownloadListWidget::onDoubleClick(const QModelIndex &index)
+void DownloadListView::onDoubleClick(const QModelIndex &index)
{
QModelIndex sourceIndex = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(index);
if (m_Manager->getState(sourceIndex.row()) >= DownloadManager::STATE_READY)
@@ -175,7 +175,7 @@ void DownloadListWidget::onDoubleClick(const QModelIndex &index) emit resumeDownload(sourceIndex.row());
}
-void DownloadListWidget::onHeaderCustomContextMenu(const QPoint &point)
+void DownloadListView::onHeaderCustomContextMenu(const QPoint &point)
{
QMenu menu;
@@ -209,98 +209,133 @@ void DownloadListWidget::onHeaderCustomContextMenu(const QPoint &point) qobject_cast<DownloadListHeader*>(header())->customResizeSections();
}
-void DownloadListWidget::resizeEvent(QResizeEvent *event)
+void DownloadListView::resizeEvent(QResizeEvent *event)
{
QTreeView::resizeEvent(event);
qobject_cast<DownloadListHeader*>(header())->customResizeSections();
}
-void DownloadListWidget::onCustomContextMenu(const QPoint &point)
+void DownloadListView::onCustomContextMenu(const QPoint &point)
{
QMenu menu(this);
QModelIndex index = indexAt(point);
bool hidden = false;
- try
- {
+ try {
if (index.row() >= 0) {
- m_ContextRow = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(index).row();
- DownloadManager::DownloadState state = m_Manager->getState(m_ContextRow);
+ const int row = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(index).row();
+ DownloadManager::DownloadState state = m_Manager->getState(row);
- hidden = m_Manager->isHidden(m_ContextRow);
+ hidden = m_Manager->isHidden(row);
if (state >= DownloadManager::STATE_READY) {
- menu.addAction(tr("Install"), this, SLOT(issueInstall()));
- if (m_Manager->isInfoIncomplete(m_ContextRow))
- menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfoMd5()));
+ menu.addAction(tr("Install"), [=] { issueInstall(row); });
+ if (m_Manager->isInfoIncomplete(row))
+ menu.addAction(tr("Query Info"), [=] { issueQueryInfoMd5(row); });
else
- menu.addAction(tr("Visit on Nexus"), this, SLOT(issueVisitOnNexus()));
- menu.addAction(tr("Open File"), this, SLOT(issueOpenFile()));
- menu.addAction(tr("Open Meta File"), this, SLOT(issueOpenMetaFile()));
- menu.addAction(tr("Reveal in Explorer"), this, SLOT(issueOpenInDownloadsFolder()));
+ menu.addAction(tr("Visit on Nexus"), [=] { issueVisitOnNexus(row); });
+ menu.addAction(tr("Open File"), [=] { issueOpenFile(row); });
+ menu.addAction(tr("Open Meta File"), [=] { issueOpenMetaFile(row); });
+ menu.addAction(tr("Reveal in Explorer"), [=] { issueOpenInDownloadsFolder(row); });
menu.addSeparator();
- menu.addAction(tr("Delete..."), this, SLOT(issueDelete()));
+ menu.addAction(tr("Delete..."), [=] { issueDelete(row); });
if (hidden)
- menu.addAction(tr("Un-Hide"), this, SLOT(issueRestoreToView()));
+ menu.addAction(tr("Un-Hide"), [=] { issueRestoreToView(row); });
else
- menu.addAction(tr("Hide"), this, SLOT(issueRemoveFromView()));
+ menu.addAction(tr("Hide"), [=] { issueRemoveFromView(row); });
} else if (state == DownloadManager::STATE_DOWNLOADING) {
- menu.addAction(tr("Cancel"), this, SLOT(issueCancel()));
- menu.addAction(tr("Pause"), this, SLOT(issuePause()));
- menu.addAction(tr("Reveal in Explorer"), this, SLOT(issueOpenInDownloadsFolder()));
- } else if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)
- || (state == DownloadManager::STATE_PAUSING)) {
- menu.addAction(tr("Delete..."), this, SLOT(issueDelete()));
- menu.addAction(tr("Resume"), this, SLOT(issueResume()));
- menu.addAction(tr("Reveal in Explorer"), this, SLOT(issueOpenInDownloadsFolder()));
+ menu.addAction(tr("Cancel"), [=] { issueCancel(row); });
+ menu.addAction(tr("Pause"), [=] { issuePause(row); });
+ menu.addAction(tr("Reveal in Explorer"), [=] { issueOpenInDownloadsFolder(row); });
+ }
+ else if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)
+ || (state == DownloadManager::STATE_PAUSING)) {
+ menu.addAction(tr("Delete..."), [=] { issueDelete(row); });
+ menu.addAction(tr("Resume"), [=] { issueResume(row); });
+ menu.addAction(tr("Reveal in Explorer"), [=] { issueOpenInDownloadsFolder(row); });
}
menu.addSeparator();
}
- } catch(std::exception&)
- {
+ }
+ catch(std::exception&) {
// this happens when the download index is not found, ignore it and don't
// display download-specific actions
}
- menu.addAction(tr("Delete Installed Downloads..."), this, SLOT(issueDeleteCompleted()));
- menu.addAction(tr("Delete Uninstalled Downloads..."), this, SLOT(issueDeleteUninstalled()));
- menu.addAction(tr("Delete All Downloads..."), this, SLOT(issueDeleteAll()));
+ menu.addAction(tr("Delete Installed Downloads..."), [=] { issueDeleteCompleted(); });
+ menu.addAction(tr("Delete Uninstalled Downloads..."), [=] { issueDeleteUninstalled(); });
+ menu.addAction(tr("Delete All Downloads..."), [=] { issueDeleteAll(); });
menu.addSeparator();
if (!hidden) {
- menu.addAction(tr("Hide Installed..."), this, SLOT(issueRemoveFromViewCompleted()));
- menu.addAction(tr("Hide Uninstalled..."), this, SLOT(issueRemoveFromViewUninstalled()));
- menu.addAction(tr("Hide All..."), this, SLOT(issueRemoveFromViewAll()));
+ menu.addAction(tr("Hide Installed..."), [=] { issueRemoveFromViewCompleted(); });
+ menu.addAction(tr("Hide Uninstalled..."), [=] { issueRemoveFromViewUninstalled(); });
+ menu.addAction(tr("Hide All..."), [=] { issueRemoveFromViewAll(); });
} else {
- menu.addAction(tr("Un-Hide All..."), this, SLOT(issueRestoreToViewAll()));
+ menu.addAction(tr("Un-Hide All..."), [=] { issueRestoreToViewAll(); } );
}
menu.exec(viewport()->mapToGlobal(point));
}
-void DownloadListWidget::issueInstall()
+void DownloadListView::keyPressEvent(QKeyEvent* event)
+{
+ if (selectionModel()->hasSelection()) {
+ const int row = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(currentIndex()).row();
+ auto state = m_Manager->getState(row);
+ if (state >= DownloadManager::STATE_READY) {
+ if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
+ issueInstall(row);
+ }
+ else if (event->key() == Qt::Key_Delete) {
+ issueDelete(row);
+ }
+ }
+ else if (state == DownloadManager::STATE_DOWNLOADING) {
+ if (event->key() == Qt::Key_Delete) {
+ issueCancel(row);
+ }
+ else if (event->key() == Qt::Key_Space) {
+ issuePause(event->key());
+ }
+ }
+ else if (state == DownloadManager::STATE_PAUSED
+ || state == DownloadManager::STATE_ERROR
+ || state == DownloadManager::STATE_PAUSING) {
+ if (event->key() == Qt::Key_Delete) {
+ issueDelete(row);
+ }
+ else if (event->key() == Qt::Key_Space) {
+ issueResume(row);
+ }
+ }
+ }
+ QTreeView::keyPressEvent(event);
+}
+
+void DownloadListView::issueInstall(int index)
{
- emit installDownload(m_ContextRow);
+ emit installDownload(index);
}
-void DownloadListWidget::issueQueryInfo()
+void DownloadListView::issueQueryInfo(int index)
{
- emit queryInfo(m_ContextRow);
+ emit queryInfo(index);
}
-void DownloadListWidget::issueQueryInfoMd5()
+void DownloadListView::issueQueryInfoMd5(int index)
{
- emit queryInfoMd5(m_ContextRow);
+ emit queryInfoMd5(index);
}
-void DownloadListWidget::issueDelete()
+void DownloadListView::issueDelete(int index)
{
const auto r = MOBase::TaskDialog(this, tr("Delete download"))
.main("Are you sure you want to delete this download?")
- .content(m_Manager->getFilePath(m_ContextRow))
+ .content(m_Manager->getFilePath(index))
.icon(QMessageBox::Question)
.button({tr("Move to the Recycle Bin"), QMessageBox::Yes})
.button({tr("Cancel"), QMessageBox::Cancel})
@@ -310,60 +345,60 @@ void DownloadListWidget::issueDelete() return;
}
- emit removeDownload(m_ContextRow, true);
+ emit removeDownload(index, true);
}
-void DownloadListWidget::issueRemoveFromView()
+void DownloadListView::issueRemoveFromView(int index)
{
- log::debug("removing from view: {}", m_ContextRow);
- emit removeDownload(m_ContextRow, false);
+ log::debug("removing from view: {}", index);
+ emit removeDownload(index, false);
}
-void DownloadListWidget::issueRestoreToView()
+void DownloadListView::issueRestoreToView(int index)
{
- emit restoreDownload(m_ContextRow);
+ emit restoreDownload(index);
}
-void DownloadListWidget::issueRestoreToViewAll()
+void DownloadListView::issueRestoreToViewAll()
{
emit restoreDownload(-1);
}
-void DownloadListWidget::issueVisitOnNexus()
+void DownloadListView::issueVisitOnNexus(int index)
{
- emit visitOnNexus(m_ContextRow);
+ emit visitOnNexus(index);
}
-void DownloadListWidget::issueOpenFile()
+void DownloadListView::issueOpenFile(int index)
{
- emit openFile(m_ContextRow);
+ emit openFile(index);
}
-void DownloadListWidget::issueOpenMetaFile() {
- emit openMetaFile(m_ContextRow);
+void DownloadListView::issueOpenMetaFile(int index) {
+ emit openMetaFile(index);
}
-void DownloadListWidget::issueOpenInDownloadsFolder()
+void DownloadListView::issueOpenInDownloadsFolder(int index)
{
- emit openInDownloadsFolder(m_ContextRow);
+ emit openInDownloadsFolder(index);
}
-void DownloadListWidget::issueCancel()
+void DownloadListView::issueCancel(int index)
{
- emit cancelDownload(m_ContextRow);
+ emit cancelDownload(index);
}
-void DownloadListWidget::issuePause()
+void DownloadListView::issuePause(int index)
{
- emit pauseDownload(m_ContextRow);
+ emit pauseDownload(index);
}
-void DownloadListWidget::issueResume()
+void DownloadListView::issueResume(int index)
{
- emit resumeDownload(m_ContextRow);
+ emit resumeDownload(index);
}
-void DownloadListWidget::issueDeleteAll()
+void DownloadListView::issueDeleteAll()
{
if (QMessageBox::warning(nullptr, tr("Delete Files?"),
tr("This will remove all finished downloads from this list and from disk.\n\nAre you absolutely sure you want to proceed?"),
@@ -372,7 +407,7 @@ void DownloadListWidget::issueDeleteAll() }
}
-void DownloadListWidget::issueDeleteCompleted()
+void DownloadListView::issueDeleteCompleted()
{
if (QMessageBox::warning(nullptr, tr("Delete Files?"),
tr("This will remove all installed downloads from this list and from disk.\n\nAre you absolutely sure you want to proceed?"),
@@ -381,7 +416,7 @@ void DownloadListWidget::issueDeleteCompleted() }
}
-void DownloadListWidget::issueDeleteUninstalled()
+void DownloadListView::issueDeleteUninstalled()
{
if (QMessageBox::warning(nullptr, tr("Delete Files?"),
tr("This will remove all uninstalled downloads from this list and from disk.\n\nAre you absolutely sure you want to proceed?"),
@@ -390,7 +425,7 @@ void DownloadListWidget::issueDeleteUninstalled() }
}
-void DownloadListWidget::issueRemoveFromViewAll()
+void DownloadListView::issueRemoveFromViewAll()
{
if (QMessageBox::question(nullptr, tr("Hide Files?"),
tr("This will remove all finished downloads from this list (but NOT from disk)."),
@@ -399,7 +434,7 @@ void DownloadListWidget::issueRemoveFromViewAll() }
}
-void DownloadListWidget::issueRemoveFromViewCompleted()
+void DownloadListView::issueRemoveFromViewCompleted()
{
if (QMessageBox::question(nullptr, tr("Hide Files?"),
tr("This will remove all installed downloads from this list (but NOT from disk)."),
@@ -408,7 +443,7 @@ void DownloadListWidget::issueRemoveFromViewCompleted() }
}
-void DownloadListWidget::issueRemoveFromViewUninstalled()
+void DownloadListView::issueRemoveFromViewUninstalled()
{
if (QMessageBox::question(nullptr, tr("Hide Files?"),
tr("This will remove all uninstalled downloads from this list (but NOT from disk)."),
diff --git a/src/downloadlistwidget.h b/src/downloadlistview.h index 64e1a6e8..050c869f 100644 --- a/src/downloadlistwidget.h +++ b/src/downloadlistview.h @@ -32,24 +32,24 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. namespace Ui {
- class DownloadListWidget;
+ class DownloadListView;
}
-class DownloadListWidget;
+class DownloadListView;
class DownloadProgressDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
- DownloadProgressDelegate(DownloadManager* manager, DownloadListWidget* list);
+ DownloadProgressDelegate(DownloadManager* manager, DownloadListView* list);
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
private:
DownloadManager* m_Manager;
- DownloadListWidget* m_List;
+ DownloadListView* m_List;
};
class DownloadListHeader : public QHeaderView
@@ -64,13 +64,13 @@ private: void mouseReleaseEvent(QMouseEvent *event) override;
};
-class DownloadListWidget : public QTreeView
+class DownloadListView : public QTreeView
{
Q_OBJECT
public:
- explicit DownloadListWidget(QWidget *parent = 0);
- ~DownloadListWidget();
+ explicit DownloadListView(QWidget *parent = 0);
+ ~DownloadListView();
void setManager(DownloadManager *manager);
void setSourceModel(DownloadList *sourceModel);
@@ -90,35 +90,38 @@ signals: void openMetaFile(int index);
void openInDownloadsFolder(int index);
+protected:
+ void keyPressEvent(QKeyEvent* event) override;
+
private slots:
- void onDoubleClick(const QModelIndex &index);
- void onCustomContextMenu(const QPoint &point);
- void onHeaderCustomContextMenu(const QPoint &point);
- void issueInstall();
- void issueDelete();
- void issueRemoveFromView();
- void issueRestoreToView();
+ void onDoubleClick(const QModelIndex& index);
+ void onCustomContextMenu(const QPoint& point);
+ void onHeaderCustomContextMenu(const QPoint& point);
+
+ void issueInstall(int index);
+ void issueDelete(int index);
+ void issueRemoveFromView(int index);
+ void issueRestoreToView(int index);
void issueRestoreToViewAll();
- void issueVisitOnNexus();
- void issueOpenFile();
- void issueOpenMetaFile();
- void issueOpenInDownloadsFolder();
- void issueCancel();
- void issuePause();
- void issueResume();
+ void issueVisitOnNexus(int index);
+ void issueOpenFile(int index);
+ void issueOpenMetaFile(int index);
+ void issueOpenInDownloadsFolder(int index);
+ void issueCancel(int index);
+ void issuePause(int index);
+ void issueResume(int index);
void issueDeleteAll();
void issueDeleteCompleted();
void issueDeleteUninstalled();
void issueRemoveFromViewAll();
void issueRemoveFromViewCompleted();
void issueRemoveFromViewUninstalled();
- void issueQueryInfo();
- void issueQueryInfoMd5();
+ void issueQueryInfo(int index);
+ void issueQueryInfoMd5(int index);
private:
DownloadManager *m_Manager;
DownloadList *m_SourceModel = 0;
- int m_ContextRow;
void resizeEvent(QResizeEvent *event);
};
diff --git a/src/downloadstab.cpp b/src/downloadstab.cpp index a0602ede..ab7a1c9a 100644 --- a/src/downloadstab.cpp +++ b/src/downloadstab.cpp @@ -1,6 +1,6 @@ #include "downloadstab.h" #include "downloadlist.h" -#include "downloadlistwidget.h" +#include "downloadlistview.h" #include "organizercore.h" #include "ui_mainwindow.h" @@ -37,7 +37,7 @@ DownloadsTab::DownloadsTab(OrganizerCore& core, Ui::MainWindow* mwui) connect(ui.list, SIGNAL(restoreDownload(int)), m_core.downloadManager(), SLOT(restoreDownload(int))); connect(ui.list, SIGNAL(cancelDownload(int)), m_core.downloadManager(), SLOT(cancelDownload(int))); connect(ui.list, SIGNAL(pauseDownload(int)), m_core.downloadManager(), SLOT(pauseDownload(int))); - connect(ui.list, &DownloadListWidget::resumeDownload, [&](int i){ resumeDownload(i); }); + connect(ui.list, &DownloadListView::resumeDownload, [&](int i){ resumeDownload(i); }); } void DownloadsTab::update() @@ -50,10 +50,10 @@ void DownloadsTab::update() // set the view attribute and default row sizes if (m_core.settings().interface().compactDownloads()) { ui.list->setProperty("downloadView", "compact"); - ui.list->setStyleSheet("DownloadListWidget::item { padding: 4px 2px; }"); + ui.list->setStyleSheet("DownloadListView::item { padding: 4px 2px; }"); } else { ui.list->setProperty("downloadView", "standard"); - ui.list->setStyleSheet("DownloadListWidget::item { padding: 16px 4px; }"); + ui.list->setStyleSheet("DownloadListView::item { padding: 16px 4px; }"); } ui.list->setMetaDisplay(m_core.settings().interface().metaDownloads()); diff --git a/src/downloadstab.h b/src/downloadstab.h index ac0cf0e2..f39f4ba5 100644 --- a/src/downloadstab.h +++ b/src/downloadstab.h @@ -5,7 +5,7 @@ namespace Ui { class MainWindow; } class OrganizerCore; -class DownloadListWidget; +class DownloadListView; class DownloadsTab : public QObject { @@ -20,7 +20,7 @@ private: struct DownloadsTabUi { QPushButton* refresh; - DownloadListWidget* list; + DownloadListView* list; QCheckBox* showHidden; QLineEdit* filter; }; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c9824a24..9c049efa 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -46,7 +46,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "categoriesdialog.h" #include "overwriteinfodialog.h" #include "downloadlist.h" -#include "downloadlistwidget.h" #include "messagedialog.h" #include "installationmanager.h" #include "motddialog.h" diff --git a/src/mainwindow.ui b/src/mainwindow.ui index a0900542..4317b6c0 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1282,7 +1282,7 @@ p, li { white-space: pre-wrap; } <item> <layout class="QVBoxLayout" name="downloadLayout"> <item> - <widget class="DownloadListWidget" name="downloadView"> + <widget class="DownloadListView" name="downloadView"> <property name="minimumSize"> <size> <width>320</width> @@ -1942,11 +1942,6 @@ p, li { white-space: pre-wrap; } <header>lcdnumber.h</header> </customwidget> <customwidget> - <class>DownloadListWidget</class> - <extends>QTreeView</extends> - <header>downloadlistwidget.h</header> - </customwidget> - <customwidget> <class>MOBase::SortableTreeWidget</class> <extends>QWidget</extends> <header>sortabletreewidget.h</header> @@ -1961,6 +1956,11 @@ p, li { white-space: pre-wrap; } <extends>QStatusBar</extends> <header>statusbar.h</header> </customwidget> + <customwidget> + <class>DownloadListView</class> + <extends>QTreeView</extends> + <header>downloadlistview.h</header> + </customwidget> </customwidgets> <resources> <include location="resources.qrc"/> diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp index 8bc73e40..f4aea109 100644 --- a/src/modlistcontextmenu.cpp +++ b/src/modlistcontextmenu.cpp @@ -17,20 +17,33 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, const QModelIndex& index, QWidget* parent) : QMenu(parent) { + connect(this, &QMenu::aboutToShow, [=, &core] { populate(core, view, index); }); +} + +void ModListGlobalContextMenu::populate(OrganizerCore& core, ModListView* view, const QModelIndex& index) +{ + clear(); + addAction(tr("Install Mod..."), [=]() { view->actions().installMod(); }); auto modIndex = index.data(ModList::IndexRole); - if (modIndex.isValid()) { + if (modIndex.isValid() && view->sortColumn() == ModList::COL_PRIORITY) { auto info = ModInfo::getByIndex(modIndex.toInt()); if (!info->isBackup()) { - addAction(info->isSeparator() ? tr("Create empty mod inside") : tr("Create empty mod before"), - [=]() { view->actions().createEmptyMod(index); }); - addAction(tr("Create separator before"), [=]() { view->actions().createSeparator(index); }); + QString text = tr("Create empty mod above"); + if (info->isSeparator()) { + text = tr("Create empty mod inside"); + } + else if (view->sortOrder() == Qt::DescendingOrder) { + text = tr("Create empty mod below"); + } + addAction(text, [=]() { view->actions().createEmptyMod(index); }); + addAction(tr("Create separator above"), [=]() { view->actions().createSeparator(index); }); } } else { - addAction(tr("Create empty mod at the end"), [=]() { view->actions().createEmptyMod(); }); - addAction(tr("Create separator at the end"), [=]() { view->actions().createSeparator(); }); + addAction(tr("Create empty mod"), [=]() { view->actions().createEmptyMod(); }); + addAction(tr("Create separator"), [=]() { view->actions().createSeparator(); }); } if (view->hasCollapsibleSeparators()) { @@ -41,18 +54,17 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV addSeparator(); - addAction(tr("Enable all visible"), [=]() { - if (QMessageBox::question(view, tr("Confirm"), tr("Really enable all visible mods?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - view->enableAllVisible(); - } - }); - addAction(tr("Disable all visible"), [=]() { - if (QMessageBox::question(parent, tr("Confirm"), tr("Really disable all visible mods?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - view->disableAllVisible(); - } - }); + QString enableTxt = tr("Enable all"), + disableTxt = tr("Disable all"); + + if (view->isFilterActive()) { + enableTxt = tr("Enable all matching mods"); + disableTxt = tr("Disable all matching mods"); + } + + addAction(enableTxt, [=] { view->actions().setAllMatchingModsEnabled(true); }); + addAction(disableTxt, [=] { view->actions().setAllMatchingModsEnabled(false); }); + addAction(tr("Check for updates"), [=]() { view->actions().checkModsForUpdates(); }); addAction(tr("Refresh"), &core, &OrganizerCore::profileRefresh); addAction(tr("Export to csv..."), [=]() { view->actions().exportModListCSV(); }); diff --git a/src/modlistcontextmenu.h b/src/modlistcontextmenu.h index 2b3f9dcd..8a6ce3df 100644 --- a/src/modlistcontextmenu.h +++ b/src/modlistcontextmenu.h @@ -25,6 +25,9 @@ protected: friend class ModListContextMenu; + // populate the menu + void populate(OrganizerCore& core, ModListView* view, const QModelIndex& index); + // creates a "All mods" context menu for the given index (can be invalid). ModListGlobalContextMenu(OrganizerCore& core, ModListView* view, const QModelIndex& index, QWidget* parent = nullptr); diff --git a/src/modlistversiondelegate.cpp b/src/modlistversiondelegate.cpp index 657718f5..5ce574a5 100644 --- a/src/modlistversiondelegate.cpp +++ b/src/modlistversiondelegate.cpp @@ -1,10 +1,13 @@ #include "modlistversiondelegate.h" +#include "settings.h" #include "modlistview.h" #include "log.h" -ModListVersionDelegate::ModListVersionDelegate(ModListView* view) : - QItemDelegate(view), m_view(view) { } +ModListVersionDelegate::ModListVersionDelegate(ModListView* view, Settings& settings) : + QItemDelegate(view), m_view(view), m_settings(settings) +{ +} void ModListVersionDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const @@ -13,6 +16,7 @@ void ModListVersionDelegate::paint(QPainter* painter, const QStyleOptionViewItem if (m_view->hasCollapsibleSeparators() && m_view->model()->hasChildren(index) + && m_settings.interface().collapsibleSeparatorsIcons(ModList::COL_VERSION) && !m_view->isExpanded(index.sibling(index.row(), 0))) { auto* model = m_view->model(); diff --git a/src/modlistversiondelegate.h b/src/modlistversiondelegate.h index 8a51e9b4..d3d0ad3b 100644 --- a/src/modlistversiondelegate.h +++ b/src/modlistversiondelegate.h @@ -4,18 +4,19 @@ #include <QStyledItemDelegate> class ModListView; +class Settings; class ModListVersionDelegate : public QItemDelegate { public: - ModListVersionDelegate(ModListView* view); + ModListVersionDelegate(ModListView* view, Settings& settings); void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: - ModListView* m_view; + Settings& m_settings; }; #endif diff --git a/src/modlistview.cpp b/src/modlistview.cpp index 526e9c33..9b461b8f 100644 --- a/src/modlistview.cpp +++ b/src/modlistview.cpp @@ -213,6 +213,11 @@ Qt::SortOrder ModListView::sortOrder() const return m_sortProxy ? m_sortProxy->sortOrder() : Qt::AscendingOrder;
}
+bool ModListView::isFilterActive() const
+{
+ return m_sortProxy && m_sortProxy->isFilterActive();
+}
+
ModListView::GroupByMode ModListView::groupByMode() const
{
if (m_sortProxy == nullptr) {
@@ -294,16 +299,6 @@ std::optional<unsigned int> ModListView::prevMod(unsigned int modIndex) const return {};
}
-void ModListView::enableAllVisible()
-{
- m_core->modList()->setActive(indexViewToModel(flatIndex(model())), true);
-}
-
-void ModListView::disableAllVisible()
-{
- m_core->modList()->setActive(indexViewToModel(flatIndex(model())), false);
-}
-
void ModListView::setFilterCriteria(const std::vector<ModListSortProxy::Criteria>& criteria)
{
m_sortProxy->setCriteria(criteria);
@@ -800,7 +795,7 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo setItemDelegateForColumn(ModList::COL_FLAGS, new ModFlagIconDelegate(this, ModList::COL_FLAGS, 120));
setItemDelegateForColumn(ModList::COL_CONFLICTFLAGS, new ModConflictIconDelegate(this, ModList::COL_CONFLICTFLAGS, 80));
setItemDelegateForColumn(ModList::COL_CONTENT, new ModContentIconDelegate(this, ModList::COL_CONTENT, 150));
- setItemDelegateForColumn(ModList::COL_VERSION, new ModListVersionDelegate(this));
+ setItemDelegateForColumn(ModList::COL_VERSION, new ModListVersionDelegate(this, core.settings()));
if (m_core->settings().geometry().restoreState(header())) {
// hack: force the resize-signal to be triggered because restoreState doesn't seem to do that
@@ -1058,7 +1053,7 @@ void ModListView::refreshMarkersAndPlugins() {
QModelIndexList indexes = selectionModel()->selectedRows();
- if (m_core->settings().interface().collapsibleSeparatorsConflicts()) {
+ if (m_core->settings().interface().collapsibleSeparatorsHighlightFrom()) {
for (auto& idx : selectionModel()->selectedRows()) {
if (hasCollapsibleSeparators()
&& model()->hasChildren(idx)
@@ -1132,7 +1127,7 @@ QColor ModListView::markerColor(const QModelIndex& index) const // collapsed separator
auto rowIndex = index.sibling(index.row(), 0);
if (hasCollapsibleSeparators()
- && m_core->settings().interface().collapsibleSeparatorsConflicts()
+ && m_core->settings().interface().collapsibleSeparatorsHighlightTo()
&& model()->hasChildren(rowIndex) && !isExpanded(rowIndex)) {
std::vector<QColor> colors;
@@ -1169,7 +1164,7 @@ std::vector<ModInfo::EFlag> ModListView::modFlags(const QModelIndex& index, bool bool compact = false;
if (info->isSeparator()
&& hasCollapsibleSeparators()
- && m_core->settings().interface().collapsibleSeparatorsConflicts()
+ && m_core->settings().interface().collapsibleSeparatorsIcons(ModList::COL_FLAGS)
&& !isExpanded(index.sibling(index.row(), 0))) {
// combine the child conflicts
@@ -1200,7 +1195,7 @@ std::vector<ModInfo::EConflictFlag> ModListView::conflictFlags(const QModelIndex bool compact = false;
if (info->isSeparator()
&& hasCollapsibleSeparators()
- && m_core->settings().interface().collapsibleSeparatorsConflicts()
+ && m_core->settings().interface().collapsibleSeparatorsIcons(ModList::COL_CONFLICTFLAGS)
&& !isExpanded(index.sibling(index.row(), 0))) {
// combine the child conflicts
@@ -1235,7 +1230,7 @@ std::set<int> ModListView::contents(const QModelIndex& index, bool* includeChild if (info->isSeparator()
&& hasCollapsibleSeparators()
- && m_core->settings().interface().collapsibleSeparatorsConflicts()
+ && m_core->settings().interface().collapsibleSeparatorsIcons(ModList::COL_CONTENT)
&& !isExpanded(index.sibling(index.row(), 0))) {
// combine the child contents
diff --git a/src/modlistview.h b/src/modlistview.h index 39099cf7..adffd737 100644 --- a/src/modlistview.h +++ b/src/modlistview.h @@ -66,6 +66,10 @@ public: int sortColumn() const;
Qt::SortOrder sortOrder() const;
+ // check if a filter is currently active
+ //
+ bool isFilterActive() const;
+
// the current group mode
//
GroupByMode groupByMode() const;
@@ -108,11 +112,6 @@ signals: public slots:
- // enable/disable all visible mods
- //
- void enableAllVisible();
- void disableAllVisible();
-
// set the filter criteria/options for mods
//
void setFilterCriteria(const std::vector<ModListSortProxy::Criteria>& criteria);
diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp index 03afa587..5af79b3b 100644 --- a/src/modlistviewactions.cpp +++ b/src/modlistviewactions.cpp @@ -17,6 +17,7 @@ #include "modinfodialog.h" #include "modlist.h" #include "modlistview.h" +#include "modelutils.h" #include "messagedialog.h" #include "nexusinterface.h" #include "nxmaccessmanager.h" @@ -172,6 +173,11 @@ void ModListViewActions::createSeparator(const QModelIndex& index) const int newPriority = -1; if (index.isValid() && m_view->sortColumn() == ModList::COL_PRIORITY) { newPriority = m_core.currentProfile()->getModPriority(index.data(ModList::IndexRole).toInt()); + + // descending order, we need to fix the priority + if (m_view->sortOrder() == Qt::DescendingOrder) { + newPriority++; + } } if (m_core.createMod(name) == nullptr) { @@ -192,6 +198,17 @@ void ModListViewActions::createSeparator(const QModelIndex& index) const m_view->scrollToAndSelect(m_view->indexModelToView(m_core.modList()->index(mIndex, 0))); } +void ModListViewActions::setAllMatchingModsEnabled(bool enabled) const +{ + const auto allIndex = m_view->indexViewToModel(flatIndex(m_view->model())); + const QString message = enabled ? + tr("Really enable %1 mod(s)?") : tr("Really disable %1 mod(s)?"); + if (QMessageBox::question(m_parent, tr("Confirm"), message.arg(allIndex.size()), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { + m_core.modList()->setActive(allIndex, enabled); + } +} + void ModListViewActions::checkModsForUpdates() const { bool checkingModsForUpdate = false; diff --git a/src/modlistviewactions.h b/src/modlistviewactions.h index a3ae1b1a..3c225fbc 100644 --- a/src/modlistviewactions.h +++ b/src/modlistviewactions.h @@ -41,6 +41,10 @@ public: void createEmptyMod(const QModelIndex& index = QModelIndex()) const; void createSeparator(const QModelIndex& index = QModelIndex()) const; + // enable/disable all non-filtered mods + // + void setAllMatchingModsEnabled(bool enabled) const; + // check all mods for update // void checkModsForUpdates() const; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index bbf6029b..e95aa565 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -808,6 +808,9 @@ ModInfo::Ptr OrganizerCore::installDownload(int index, int priority) reportError(tr("mod not found: %1").arg(qUtf8Printable(modName))); } m_DownloadManager.markInstalled(index); + if (settings().interface().hideDownloadsAfterInstallation()) { + m_DownloadManager.removeDownload(index, false); + } emit modInstalled(modName); return modInfo; } diff --git a/src/settings.cpp b/src/settings.cpp index d1f3ef4f..7047fa44 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2188,14 +2188,34 @@ void InterfaceSettings::setCollapsibleSeparators(bool ascending, bool descending set(m_Settings, "Settings", "collapsible_separators_dsc", descending); } -bool InterfaceSettings::collapsibleSeparatorsConflicts() const +bool InterfaceSettings::collapsibleSeparatorsHighlightTo() const { - return get<bool>(m_Settings, "Settings", "collapsible_separators_conflicts", true); + return get<bool>(m_Settings, "Settings", "collapsible_separators_conflicts_to", true); } -void InterfaceSettings::setCollapsibleSeparatorsConflicts(bool b) +void InterfaceSettings::setCollapsibleSeparatorsHighlightTo(bool b) { - set(m_Settings, "Settings", "collapsible_separators_conflicts", b); + set(m_Settings, "Settings", "collapsible_separators_conflicts_to", b); +} + +bool InterfaceSettings::collapsibleSeparatorsHighlightFrom() const +{ + return get<bool>(m_Settings, "Settings", "collapsible_separators_conflicts_from", true); +} + +void InterfaceSettings::setCollapsibleSeparatorsHighlightFrom(bool b) +{ + set(m_Settings, "Settings", "collapsible_separators_conflicts_from", b); +} + +bool InterfaceSettings::collapsibleSeparatorsIcons(int column) const +{ + return get<bool>(m_Settings, "Settings", QString("collapsible_separators_icons_%1").arg(column), true); +} + +void InterfaceSettings::setCollapsibleSeparatorsIcons(int column, bool show) +{ + set(m_Settings, "Settings", QString("collapsible_separators_icons_%1").arg(column), show); } bool InterfaceSettings::collapsibleSeparatorsPerProfile() const @@ -2238,6 +2258,16 @@ void InterfaceSettings::setMetaDownloads(bool b) set(m_Settings, "Settings", "meta_downloads", b); } +bool InterfaceSettings::hideDownloadsAfterInstallation() const +{ + return get<bool>(m_Settings, "Settings", "autohide_downloads", false); +} + +void InterfaceSettings::setHideDownloadsAfterInstallation(bool b) +{ + set(m_Settings, "Settings", "autohide_downloads", b); +} + bool InterfaceSettings::hideAPICounter() const { return get<bool>(m_Settings, "Settings", "hide_api_counter", false); diff --git a/src/settings.h b/src/settings.h index 484ce163..5f6dd37c 100644 --- a/src/settings.h +++ b/src/settings.h @@ -626,10 +626,22 @@ public: bool collapsibleSeparators(Qt::SortOrder order) const; void setCollapsibleSeparators(bool ascending, bool descending); - // whether to display mod conflicts on separators when collapsed + // whether to highlight mod conflicts and plugins on collapsed + // separators // - bool collapsibleSeparatorsConflicts() const; - void setCollapsibleSeparatorsConflicts(bool b); + bool collapsibleSeparatorsHighlightTo() const; + void setCollapsibleSeparatorsHighlightTo(bool b); + + // whether to highlight mod conflicts and plugins from separators + // when selected but collapsed + // + bool collapsibleSeparatorsHighlightFrom() const; + void setCollapsibleSeparatorsHighlightFrom(bool b); + + // whether to show icons on collapsed separators + // + bool collapsibleSeparatorsIcons(int column) const; + void setCollapsibleSeparatorsIcons(int column, bool show); // whether each profile should have its own expansion state // @@ -651,6 +663,11 @@ public: bool metaDownloads() const; void setMetaDownloads(bool b); + // whether to hide downloads after installing them + // + bool hideDownloadsAfterInstallation() const; + void setHideDownloadsAfterInstallation(bool b); + // whether the API counter should be hidden // bool hideAPICounter() const; diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index b23e6d90..bcef5d95 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -311,22 +311,16 @@ If you disable this feature, MO will only display official DLCs this way. Please <property name="checked"> <bool>false</bool> </property> - <layout class="QVBoxLayout" name="verticalLayout_20"> - <property name="leftMargin"> - <number>7</number> - </property> - <property name="rightMargin"> - <number>7</number> - </property> - <item> - <widget class="QWidget" name="widget_8" native="true"> + <layout class="QGridLayout" name="gridLayout_7"> + <item row="0" column="0"> + <widget class="QWidget" name="collapsibleSeparatorsWidget" native="true"> <property name="minimumSize"> <size> <width>0</width> - <height>0</height> + <height>50</height> </size> </property> - <layout class="QHBoxLayout" name="horizontalLayout_13"> + <layout class="QGridLayout" name="gridLayout_10"> <property name="leftMargin"> <number>0</number> </property> @@ -339,62 +333,139 @@ If you disable this feature, MO will only display official DLCs this way. Please <property name="bottomMargin"> <number>0</number> </property> - <item> - <widget class="QLabel" name="label_18"> - <property name="text"> - <string>When sorting by</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="collapsibleSeparatorsAscBox"> - <property name="text"> - <string>ascending priority</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="collapsibleSeparatorsDscBox"> - <property name="text"> - <string>descending priority</string> + <item row="0" column="0"> + <layout class="QGridLayout" name="gridLayout_9"> + <property name="verticalSpacing"> + <number>9</number> </property> - </widget> + <item row="0" column="0"> + <widget class="QLabel" name="collapsibleSeparatorsLabel"> + <property name="text"> + <string>When sorting by</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_35"> + <property name="text"> + <string>Show icons on separators</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="3"> + <widget class="QCheckBox" name="collapsibleSeparatorsPerProfileBox"> + <property name="toolTip"> + <string>Do not share the collapse/expanded state of separators between profiles.</string> + </property> + <property name="whatsThis"> + <string>Do not share the collapse/expanded state of separators between profiles.</string> + </property> + <property name="text"> + <string>Profile-specific collapse states for separators</string> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QCheckBox" name="collapsibleSeparatorsIconsFlagsBox"> + <property name="text"> + <string>flags</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_34"> + <property name="text"> + <string>Show conflicts and plugins </string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QCheckBox" name="collapsibleSeparatorsIconsConflictsBox"> + <property name="text"> + <string>conflicts</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="3" column="3"> + <widget class="QCheckBox" name="collapsibleSeparatorsIconsContentsBox"> + <property name="text"> + <string>content</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="3" column="4"> + <widget class="QCheckBox" name="collapsibleSeparatorsIconsVersionBox"> + <property name="text"> + <string>version</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1" colspan="2"> + <widget class="QCheckBox" name="collapsibleSeparatorsAscBox"> + <property name="text"> + <string>ascending priority</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="3" colspan="2"> + <widget class="QCheckBox" name="collapsibleSeparatorsDscBox"> + <property name="text"> + <string>descending priority</string> + </property> + </widget> + </item> + <item row="2" column="1" colspan="2"> + <widget class="QCheckBox" name="collapsibleSeparatorsHighlightToBox"> + <property name="toolTip"> + <string>Highlight collapsed separators based on conflicts and plugins from mods inside them.</string> + </property> + <property name="whatsThis"> + <string>Highlight collapsed separators based on conflicts and plugins from mods inside them.</string> + </property> + <property name="text"> + <string>on separators</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="3" colspan="2"> + <widget class="QCheckBox" name="collapsibleSeparatorsHighlightFromBox"> + <property name="toolTip"> + <string>When selecting a collapsed separator, highlight conflicting mods and plugins from mods inside the separator.</string> + </property> + <property name="whatsThis"> + <string>When selecting a collapsed separator, highlight conflicting mods and plugins from mods inside the separator.</string> + </property> + <property name="text"> + <string>from separators</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> </item> </layout> </widget> </item> - <item> - <widget class="QCheckBox" name="collapsibleSeparatorsConflictsBox"> - <property name="toolTip"> - <string>Display mod conflicts on and from separator when collapsed, and show plugins from collapsed separators.</string> - </property> - <property name="whatsThis"> - <string>Display mod conflicts on and from separator when collapsed, and show plugins from collapsed separators.</string> - </property> - <property name="text"> - <string>Show conflicts and plugins on separators and from separators</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="collapsibleSeparatorsPerProfileBox"> - <property name="toolTip"> - <string>Do not share the collapse/expanded state of separators between profiles.</string> - </property> - <property name="whatsThis"> - <string>Do not share the collapse/expanded state of separators between profiles.</string> - </property> - <property name="text"> - <string>Profile-specific collapse states for separators</string> - </property> - </widget> - </item> </layout> </widget> </item> @@ -450,6 +521,19 @@ If you disable this feature, MO will only display official DLCs this way. Please </widget> </item> <item> + <widget class="QCheckBox" name="hideDownloadInstallBox"> + <property name="toolTip"> + <string>Automatically hide downloads after successful installation.</string> + </property> + <property name="whatsThis"> + <string>Automatically hide downloads after successful installation.</string> + </property> + <property name="text"> + <string>Hide downloads after installation</string> + </property> + </widget> + </item> + <item> <spacer name="verticalSpacer_5"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/settingsdialoguserinterface.cpp b/src/settingsdialoguserinterface.cpp index cdd30f2e..49710771 100644 --- a/src/settingsdialoguserinterface.cpp +++ b/src/settingsdialoguserinterface.cpp @@ -3,13 +3,20 @@ #include "shared/appconfig.h" #include "categoriesdialog.h" #include "colortable.h" +#include "modlist.h" #include <utility.h> #include <questionboxmemory.h> using namespace MOBase; UserInterfaceSettingsTab::UserInterfaceSettingsTab(Settings& s, SettingsDialog& d) - : SettingsTab(s, d) + : SettingsTab(s, d), + m_columnToBox{ + { ModList::COL_CONFLICTFLAGS, ui->collapsibleSeparatorsIconsConflictsBox }, + { ModList::COL_FLAGS, ui->collapsibleSeparatorsIconsFlagsBox }, + { ModList::COL_CONTENT, ui->collapsibleSeparatorsIconsContentsBox}, + { ModList::COL_VERSION, ui->collapsibleSeparatorsIconsVersionBox } + } { // connect before setting to trigger @@ -19,15 +26,21 @@ UserInterfaceSettingsTab::UserInterfaceSettingsTab(Settings& s, SettingsDialog& // mod list ui->displayForeignBox->setChecked(settings().interface().displayForeign()); ui->colorSeparatorsBox->setChecked(settings().colors().colorSeparatorScrollbar()); - ui->collapsibleSeparatorsConflictsBox->setChecked(settings().interface().collapsibleSeparatorsConflicts()); ui->collapsibleSeparatorsAscBox->setChecked(settings().interface().collapsibleSeparators(Qt::AscendingOrder)); ui->collapsibleSeparatorsDscBox->setChecked(settings().interface().collapsibleSeparators(Qt::DescendingOrder)); + ui->collapsibleSeparatorsHighlightFromBox->setChecked(settings().interface().collapsibleSeparatorsHighlightFrom()); + ui->collapsibleSeparatorsHighlightToBox->setChecked(settings().interface().collapsibleSeparatorsHighlightTo()); ui->collapsibleSeparatorsPerProfileBox->setChecked(settings().interface().collapsibleSeparatorsPerProfile()); ui->saveFiltersBox->setChecked(settings().interface().saveFilters()); + for (auto& p : m_columnToBox) { + p.second->setChecked(settings().interface().collapsibleSeparatorsIcons(p.first)); + } + // download list ui->compactBox->setChecked(settings().interface().compactDownloads()); ui->showMetaBox->setChecked(settings().interface().metaDownloads()); + ui->hideDownloadInstallBox->setChecked(settings().interface().hideDownloadsAfterInstallation()); // colors ui->colorTable->load(s); @@ -43,13 +56,19 @@ void UserInterfaceSettingsTab::update() settings().interface().setDisplayForeign(ui->displayForeignBox->isChecked()); settings().interface().setCollapsibleSeparators( ui->collapsibleSeparatorsAscBox->isChecked(), ui->collapsibleSeparatorsDscBox->isChecked()); - settings().interface().setCollapsibleSeparatorsConflicts(ui->collapsibleSeparatorsConflictsBox->isChecked()); + settings().interface().setCollapsibleSeparatorsHighlightFrom(ui->collapsibleSeparatorsHighlightFromBox->isChecked()); + settings().interface().setCollapsibleSeparatorsHighlightTo(ui->collapsibleSeparatorsHighlightToBox->isChecked()); settings().interface().setCollapsibleSeparatorsPerProfile(ui->collapsibleSeparatorsPerProfileBox->isChecked()); settings().interface().setSaveFilters(ui->saveFiltersBox->isChecked()); + for (auto& p : m_columnToBox) { + settings().interface().setCollapsibleSeparatorsIcons(p.first, p.second->isChecked()); + } + // download list settings().interface().setCompactDownloads(ui->compactBox->isChecked()); settings().interface().setMetaDownloads(ui->showMetaBox->isChecked()); + settings().interface().setHideDownloadsAfterInstallation(ui->hideDownloadInstallBox->isChecked()); // colors ui->colorTable->commitColors(); @@ -59,6 +78,10 @@ void UserInterfaceSettingsTab::updateCollapsibleSeparatorsGroup() { const auto checked = ui->collapsibleSeparatorsAscBox->isChecked() || ui->collapsibleSeparatorsDscBox->isChecked(); - ui->collapsibleSeparatorsConflictsBox->setEnabled(checked); - ui->collapsibleSeparatorsPerProfileBox->setEnabled(checked); + for (auto* widget : ui->collapsibleSeparatorsWidget->findChildren<QWidget*>()) { + widget->setEnabled(checked); + } + ui->collapsibleSeparatorsLabel->setEnabled(true); + ui->collapsibleSeparatorsAscBox->setEnabled(true); + ui->collapsibleSeparatorsDscBox->setEnabled(true); } diff --git a/src/settingsdialoguserinterface.h b/src/settingsdialoguserinterface.h index 4609ae54..0c3c7fb7 100644 --- a/src/settingsdialoguserinterface.h +++ b/src/settingsdialoguserinterface.h @@ -1,6 +1,8 @@ #ifndef SETTINGSDIALOGUSERINTERFACE_H #define SETTINGSDIALOGUSERINTERFACE_H +#include <QCheckBox> + #include "settingsdialog.h" #include "settings.h" @@ -16,6 +18,10 @@ protected slots: // enable/disable the collapsible separators group depending on // the checkbox states void updateCollapsibleSeparatorsGroup(); + +private: + + const std::map<int, QCheckBox*> m_columnToBox; }; #endif // SETTINGSDIALOGGENERAL_H diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 0d275844..badb5636 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -211,7 +211,19 @@ void DirectoryEntry::addFromBSA( }
BSA::Archive archive;
- BSA::EErrorCode res = archive.read(ToString(archivePath, false).c_str(), false);
+ BSA::EErrorCode res = BSA::ERROR_NONE;
+
+ try
+ {
+ // read() can return an error, but it can also throw if the file is not a
+ // valid bsa
+ res = archive.read(ToString(archivePath, false).c_str(), false);
+ }
+ catch(std::exception& e)
+ {
+ log::error("invalid bsa '{}', error {}", archivePath, e.what());
+ return;
+ }
if ((res != BSA::ERROR_NONE) && (res != BSA::ERROR_INVALIDHASHES)) {
log::error("invalid bsa '{}', error {}", archivePath, res);
|
